섹션4
백엔드 서버 세팅하기
백앤드 환경설정
- node 설치
- postgresql 설치
설치방법
postgres라는 db 관리자가 생긴다.
비밀번호 설정해준다.
포트는 5432로 설정한다.
- redis 설치 (로그인 할 때 사용)
https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-mac-os/
- Nest-prisma 세팅
https://github.com/zerocho/nest-prisma 폴더다운로드 해서 사용한다.
npm install 로 패키지 설치하기
.env 파일 수정
DATABASE_URL="postgresql://postgres:비밀번호입력@localhost:5432/zcom?schema=public"
pgAdmin 4 실행
1. register server (name 입력, connection hostname, port 입력, 비밀번호 입력)
2. create database
zcom을 만들어준다.
3. nest-prisma 저장소로 돌아가 명령어로 migrate하기
npx prisma migrate dev
4. pgadmin에서 schemas/public/tables 경로에 데이터 들어와있는지 확인하기
nest 서버 실행하기
npm run start:dev
로컬 9090포트에서 서버확인, swagger 확인
로그인과 회원가입 실제로 하기
서버액션은 오류가 나더라도 클라이언트에서 오류로 나오지 않기때문에 확인하기 힘들다.
회원가입시 fetch 옵션 credentials: 'include'를 넣어줘야 세션 쿠키가 브라우저에 전달, 등록된다.
const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/users`, {
method: 'post',
body: formData,
credentials: 'include',
})
회원가입을 하면 db에 사용자에 데이터가 들어간것을 확인할수있다.
주소를 rewrite 해야하는경우 next.config.js에서 설정할 수있다.
const nextConfig = {
async rewrites() {
return [
{
source: '/upload/:slug',
destination: 'http://localhost:9090/upload/:slug',
},
]
},
}
module.exports = nextConfig
업로드 이미지 미리보기
react-textarea-autosize
textarea 텍스트 사이즈에 맞춰 자동 크기 조절 되는 플러그인
이미지 미리보기
readAsDataURL은 파일 컨텐츠 데이터를 읽어오는 역할을 한다. read 행위가 종료되면 onloadend 이벤트가 트리거 된다.
base64 인코딩 된 스트링 데이터가 result에 담아지게 된다.
참고
https://developer.mozilla.org/ko/docs/Web/API/FileReader/readAsDataURL
Array.from 메소드
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/from
const onUpload = (e: any) => {
e.preventDefault();
if (e.target.files) {
Array.from(e.target.files).forEach((file: File, index: number) => {
const reader = new FileReader();
reader.onloadend = () => {
setPreview((prevPreview: any) => {
const prev = [...prevPreview];
prev[index] = {dataUrl: reader.result as string, file};
return prev
//return [...prevPreview, reader.result as string]
})
}
reader.readAsDataURL(file); //
})
}
}
세션쿠키 공유하기 & 게시글 업로드 완성
프론트는 로그인시 세션쿠키를 사용하지만 백엔드는 세션쿠키를 사용하지 않는다.
session-token(클라이언트용), connect.sid (백엔드용)
클라이언트에서는 next-auth 기능을 사용하면 되고, 서버에서는 auth.ts에서 export한 메서드를 사용하면 된다.
Auth.ts에서 로그인시 connect.sid 저장하는 로직을 추가해준다.
서버에는 로그인값을 저장못한다. 다른사람들도 함께 사용하는 공용의 서버이기때문에
providers: [
CredentialsProvider({
async authorize(credentials) {
const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: credentials.username,
password: credentials.password,
}),
})
let setCookie = authResponse.headers.get('Set-Cookie');
console.log('set-cookie', setCookie);
if (setCookie) {
const parsed = cookie.parse(setCookie);
cookies().set('connect.sid', parsed['connect.sid'], parsed); // 브라우저에 쿠키를 심어주는 것
}
if (!authResponse.ok) {
return null
}
const user = await authResponse.json()
console.log('user', user);
return {
email: user.id,
name: user.nickname,
image: user.image,
...user,
}
},
}),
]
useMutation 사용하기
react useMutation 사용하는 이유는 상태관리가 가능하다(ispending, isError, isSuccess)
optimistic update를 활용할수있다. (게시글올렸을때 바로성공했다고 치고 화면에 올려준다.)
좋아요 누르기 정도 도입해서 빠른 상호작용을 보여주면 좋을듯 하다.
onMutate() {
return '1111'
},
onSuccess(response, variable, context)
// context는 onMutate에서 return한 값
onSettled() { // 마지막에 무조건실행한다.
queryClient.invalidateQueries({
queryKey: ['posts'] // invalidateQueries는 posts를 쿼리키로 가진 애들을 다시 가져오는 메소드
})
}
데이터를 안불러온상태 pending
데이터를 불러오고 있는상태 fetching
참고
https://tanstack.com/query/latest/docs/framework/react/reference/useMutation
주소에 해시가 들어가면 문제가 됩니다.
주소에 특수문자 해시가 들어가면 서버에 정보가 넘어가지 않는다. encoding을 해줘야한다.
encodeURIComponent('#test')
api에서도 쿼리스트링 url객체로 변경해서 toString 해줘야한다.
`http://localhost:9090/api/post?${urlSearchParams.toString()}`
하트 누를때 optimistic update 적용하기
팔로우 , 하트누를 때 적용
복잡한 구조에서 해당id를 찾고 불변성을 지키면서 업데이트를 해줘야하는경우 로직이 복잡해 질수있다.
immer는 불변성을 지키면서 값을 변경할 수 있도록 해주는 라이브러리
https://react.vlpt.us/basic/23-immer.html
Array.flat()
여러차원의 배열을 1차원 배열로 만들어주는 메소드
참고 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
// immer 사용한 경우
shallow.pages[pageIndex][index].Hearts = [{userId: sesson?.user?.email as string}];
shallow.pages[pageIndex][index]._count.Hearts += 1;
// immer 사용하지 않는경우
const pageIndex = value.pages.findIndex((page) => page.includes(obj));
const index = value.pages[pageIndex].findIndex((v) => v.postId === postId);
console.log('found index', index);
const shallow = { ...value };
value.pages = {...value.pages }
value.pages[pageIndex] = [...value.pages[pageIndex]];
shallow.pages[pageIndex][index] = {
...shallow.pages[pageIndex][index],
Hearts: shallow.pages[pageIndex][index].Hearts.filter((v) => v.userId !== session?.user?.email),
_count: {
...shallow.pages[pageIndex][index]._count,
Hearts: shallow.pages[pageIndex][index]._count.Hearts - 1,
}
}
서로 다른 컴포넌트간 query 일치하게 하기
onError()
첫번째 인자가 에러 정보
두번째인자는 받는 매개변수
완전히 로그아웃하기 , 프론트서버에 쿠키보내기
react query invalidateQueries를 사용하여 캐시를 날려준다.
queryClient.invalidateQueries({
queryKey: ['posts'] // invalidateQueries는 posts를 쿼리키로 가진 애들을 다시 가져오는 메소드
})
fetch를 이용해 로그아웃 api을 호출하면 session이 날라간다.
prefetch query로 getuser가 서버에서 실행될때는
fetch함수에 credential: includes를 넣어도 브라우저 쿠키를 가져오진 못한다
직접 cookie 를 header에 넣어주면된다.
import {cookies} from 'next/headers'
headers: {Cookie: cookies().toString()}
메타데이터 설정하기
서버컴포넌트에서 실행된다.
동적으로 메타데이터 생성할때는 generateMetadata를 사용하면 된다.
layout.tsx, page.tsx 에서 설정하면 된다.
export async function generateMetadata({searchParam}) {
return {
title: `${searchParam.q}`
description: `${searchParam.q}`
}
}
동적으로 생성하지 않아도 되는경우 metadata로 사용한다,
export const Metadata = {
title: '홈',
description: '설명'
}
SSR 적용기준
서버사이드 컴포넌트 확인 하는 법
네트워크 탭 에서 미리보기하면 SSR인지 확인할수있다.
SSR하면 좋은 페이지 - 공유하기가 많은 페이지, 로그인 없이 접근가능한 페이지
서버사이드 장점 - 검색엔진에 최적화
서버사이드 단점 - 프론트서버에 부하가 갈수있다.
zustand 사용하기
zustand create를 이용하여 store를 만들수있다.
store 값을 변경하는 set action을 만들어 불변성을 지키면서 변경할 수 있다.
import { Post } from "@/model/Post";
import { create } from "zustand";
interface ModalState {
mode: "new" | "comment";
data: Post | null;
setMode(mode: "new" | "comment"): void;
setData(data: Post): void;
reset(): void;
}
export const useModalStore = create<ModalState>((set, get) => ({
// 초기 데이터
mode: "new",
data: null,
setMode(mode) {
set({ mode });
},
setData(data) {
set({ data });
},
reset() {
set({
mode: "new",
data: null,
});
},
}));
'NOTE' 카테고리의 다른 글
로컬환경을 인터넷에서도 확인 할 수 있는 ngrok 사용방법 (0) | 2024.12.17 |
---|---|
[Next + React Query로 SNS 서비스 만들기] 섹션 5 정리 (5) | 2024.09.15 |
[Next + React Query로 SNS 서비스 만들기] 섹션 3-2 정리 (react-query, Suspense) (0) | 2024.08.24 |
[Next + React Query로 SNS 서비스 만들기] 섹션 3-1 정리 (msw, server-action, next-auth) (0) | 2024.08.24 |
Next serverActions (0) | 2024.08.10 |