-
Notifications
You must be signed in to change notification settings - Fork 37
[김찬기] Sprint9 #285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[김찬기] Sprint9 #285
The head ref may contain hidden characters: "Next-\uAE40\uCC2C\uAE30-sprint9"
Conversation
- Link태그 변경 (react router에서 next link) - client 컴포넌트에 'use client' directive추가 - react router에서 next navigation으로 교체 - 페이지 라우팅 설정 - nextjs로 옮기면서 생기는 오류 수정 (로컬스토리지)
- mini, border 프롭 추가
- 다른컴포넌트에서 무한랜더링 유발 방지용으로 useCallback 적용
- 방향 추가 - 사이즈 추가 - 날짜를 공용 컴포넌트로 교체
- 베스트 게시물 (csr) - 전체 게시물 (ssr)
- 리프레시발급에 axios인스턴스 변경 (인터셉터 무한지옥에 빠짐) - client경우에는 context에서 인터셉터를 설정하나, ssr용으로 인터셉션 추가로 설정
- 세션체크는 미들웨어 - 소유자체크는 서버에서 세션체크
- 헤더 세션체크를 클라이언트 세션을 체크하도록 변경 (ssg 전략때문에)
- 중복되거나 비슷한 컴포넌트 리팩토링 - 쿼리파람을 미리 resolve해서 컴포넌트로 보내기 (두번의 await 방지) - 사용안하는 클라이언트 훅 삭제
- params를 미리 resolve해서 전달 - 에러 핸들링 추가 - 컴포넌트 이름 오타 수정
src/service/axios.ts
Outdated
| import { auth } from "@/auth"; | ||
| import axios from "axios"; | ||
|
|
||
| export const axiosInstance = axios.create({ | ||
| baseURL: process.env.NEXT_PUBLIC_API_URL, | ||
| }); | ||
|
|
||
| if (typeof window === "undefined") { | ||
| axiosInstance.interceptors.request.use( | ||
| async (config) => { | ||
| const session = await auth(); | ||
| if (session?.accessToken) { | ||
| config.headers.Authorization = `Bearer ${session?.accessToken}`; | ||
| } | ||
| return config; | ||
| }, | ||
| (error) => { | ||
| return Promise.reject(error); | ||
| } | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서버 런타임환경에서 데이터를 요청할때
서버에 세션이 있으면 헤더에 토큰을 넣어보내도록했습니다.
(중고마켓 게시물 상세페이지를 요청할때 토큰을 넣어보내면, 좋아요를 눌렀는지 판별해서 보내주는것 때문에 넣게 되었습니다.)
클라이언트 사이드에서 인터셉터는 프로젝트 루트쪽에 컴포넌트를 끼워두웠습니다. useEffect코드안에서 interceptor를 설정했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
조금더 고민을 해보니까 "use server"를 이용해보면 좋을것 같아서,
앱시작점에서 "use server" 지시어를 달고 있는 인터셉터 함수파일을 실행하는 방향으로 바꾸었습니다.
| if (accessToken !== data.accessToken) { | ||
| // next-auth session update | ||
| await update({ accessToken }); | ||
| error.config.headers.Authorization = `Bearer ${accessToken}`; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
클라이언트 사이드에서 토큰 재발급후에, nextauth에게 어떻게 바뀐 토큰을 알려주는지 찾아보다가
update를 알게되어서 사용했습니다.
Lanace
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 코드가 너무 많아서 다 확인하진 못했어요ㅠ
근데 전반적으로 너무 잘 작성해주셨네요
혹시 직접 처음부터 다 개발하신건가요??
어느부분은 어딘가에서 가져오신건지 궁금하더라구요...ㄷㄷ
너무 잘 짜주셔서...ㅎㅎ;;
README쪽에 질문처럼 남겨주셨던데, 제가 이해를 잘 못해서... 괜찮으시면 있다가 멘토링떄 같이 볼 수 있을까요??
너무 고생 많으셨습니다ㅎㅎ!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tip
git ignore 라는 사이트가 있는데,
여기서 사용하시는 기술스텍을 입력하면 알아서 만들어줄꺼에요ㅎㅎ
보통 한번만 잘 만들고 따로 신경쓸일 없어서... 한번 참고해보시면 좋을꺼에요~
| images: { | ||
| remotePatterns: [ | ||
| { | ||
| protocol: "http", | ||
| hostname: "**", | ||
| }, | ||
| { | ||
| protocol: "https", | ||
| hostname: "**", | ||
| }, | ||
| ], | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모든 요청에 대해서 열어둘수도 있긴 한데, 실제로는 이렇게 다 여는경우는 별로 없긴 해요...!
내가 사용하는 cdn 만 열거나 하거든요 보통은...
지금은 딱히 문제 안될것같아요ㅎㅎ!
| @@ -1,5 +1,5 @@ | |||
| { | |||
| "name": "fe-weekly-mission", | |||
| "name": "next", | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
꼼꼼하시네요 이런부분들도 신경쓰시고...ㅎㅎ
| interface AuthContainerProps { | ||
| children: ReactNode; | ||
| mode?: "login" | "signup"; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아래와 같이 사용할 수 있어요ㅎㅎ
interface AuthContainerProps extends React.PropsWithChildren {
mode?: "login" | "signup";
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안그래도 저번 팀 마지막 멘토링때 배웠는데, 이번 미션에 수정해보겠습니다
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
궁금한게 있는데, _components 에 앞에 _ 는 어떤 의미인가요??
보통 javascript 에서 앞에 _가 있으면 암묵적으로 외부에서 사용하지 않는 변수나 값이라는걸 의미하는데,
여기에 _가 있길래요ㅠ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 그리구 보통 component 폴더는 src아래 또는 root 쪽에 두는경우가 많아요ㅎㅎ
왜냐하면 app 폴더 안쪽에서는 페이지나 레이아웃같은 예약어로 된 파일들이 있어야해서요...!
(물론 동작하는데 문제는 없음)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
next에서 제공하는 private 폴더 규칙을 썻습니다.
리액트부터 진행했던 소스라 전부다 app폴더 안으로 들고오지는 못했어요.
저는 app폴더밑에 같이 있는게 편하더라구요
| export type PaginationResponse<T> = { | ||
| totalCount: number; | ||
| list: T[]; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제네릭타입도 잘 사용하시네요...!
| const query = `page=${page}&pageSize=${pageSize}&orderBy=${orderBy}&keyword=${keyword}`; | ||
| const response = await axiosInstance.get<PaginationResponse<Product>>( | ||
| `/products?${query}` | ||
| ); | ||
|
|
||
| return response.data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
물론 위에 있는 코드도 잘 동작할텐데 아래 코드가 조금더 읽기엔 좋은것같긴 해요ㅎㅎ!
const response = await axiosInstance.get<PaginationResponse<Product>>(
`/products`, {
params: {
page,
pageSize,
orderBy,
keyword,
}
}
);| const response = await axiosInstance.post<ImageUploadResponse>( | ||
| "/images/upload", | ||
| formData | ||
| ); | ||
|
|
||
| return response.data; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
밖에서 form 데이터를 만들어서 API 를 호출할 수 있긴 한데,
그러면 다른 사람들이 uploadProductImage 함수를 호출할떄 formData를 어떻게 만들어야하는지 문서까지 보아야할꺼에요ㅠ
그래서 가능하면 File을 받고, formData를 여기서 만들어주는게 좋을것같긴 하네여ㅎㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아... service 레이어를 생각하신거 보니 spring이나 다른 backend 에 mvc 모델을 Nextjs에 적용하시려고 하셨나보군요ㅎㅎ
물론 나쁘지 않은 접근이에요!
조금 다른건 보통 service layer에서는 비즈니스 로직을 담당하는데, 프론트에서는 그러한 로직을 component 나 page 쪽에서 다루는 경우가 있어요.
실제로도 여기서는 API에서 받아온 data를 전달하는 역할을 하죠... 굳이 따지자면 DAO 정도의 역할을 하고있는것같아요.
그래서 전 보통 ArticleApis.ts 정도로 이름을 짓고 apis로 묶어버리긴 해요ㅎㅎ!
(해주신 방식이 틀린건 아닌데, 다른 프론트엔드 개발자분들이 이해를 못할 수도 있긴 해요)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
api호출함수가 그대로 컴포넌트 레벨로 섞여들어가면, 나중에 여러곳 수정하기가 힘들것 같아서
따로 분리해서 작업해왔었습니다. (갑자기 주소가 바뀌거나, 파라미터가 바껴야하는 상황에)
그런데 생각보다 여러군데서 불러서 쓰지 않는것 같아서 이렇게 까지 해야할까 하긴 했습니다.
| ), | ||
| }); | ||
|
|
||
| export type ProductFormType = z.infer<typeof ProductFormSchema>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하나하나 zod 스키마를 다 정의하신건가요?ㄷㄷ
엄청 잘 하셨네요...
요구사항
기본
심화
주요 변경사항
스크린샷
멘토에게