Skip to content

Conversation

@songmijin824
Copy link
Collaborator

요구사항

기본

Sprint Mission 9 기본 요구사항

  • Next.js 활용하여 구현해주세요
  • 자유 게시판 페이지 주소는 “/boards” 입니다.
  • 전체 게시글에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.
  • 게시글 목록 조회 api를 사용하여 베스트 게시글, 게시글을 구현합니다.
  • 게시글 title에 검색어가 일부 포함되면 검색이 됩니다.
  • 베스트 상품 기준 → 정렬 : like →like가 높은 순
  • 반응형 디자인을 구현해주세요.

Sprint Mission 10 기본 요구사항

  • 게시글 등록 페이지 주소는 “/addboard” 입니다.
  • 게시판 이미지는 최대 한개 업로드가 가능합니다.
  • 각 input의 placeholder 값을 정확히 입력해주세요.
  • 이미지를 제외하고 input 에 모든 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
  • 게시글 상세 페이지 주소는 “/board/{id}” 입니다.
  • 댓글 input 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
  • 자유게시판 페이지에서 게시글을 누르면 게시물 상세 페이지로 이동합니다.
  • 게시글 상세 페이지 주소는 “/board/{id}” 입니다.
  • 댓글 input 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.

심화

Sprint Mission 9 심화 요구사항

  • 반응형으로 보여지는 베스트 게시판 개수를 다르게 설정할때 서버에 보내는 pageSize값을 적절하게 설정합니다.
  • next의 data prefetch 기능을 사용해봅니다.

Sprint Mission 10 심화 요구사항

  • 회원가입, 로그인 api를 사용하여 받은 accessToken을 사용하여 게시물 등록을 합니다.
  • ‘등록’ 버튼을 누르면 게시물 상세 페이지로 이동합니다.
  • 테블릿, 모바일 반응형 디자인을 구현해주세요

주요 변경사항

스크린샷

image

멘토에게

  1. 빌더를 시도해보려고 했는데 에러가 계속생깁니다. next.js 는 SSR이 기본값이라고 찾아보기는했는데
    그냥 CSR로 빌더하려고 설정을 맞추어봐도 오류가 계속생깁니다..ㅜ
    검색해보니 useRouter,useSearchParams,localStorage사용 여부 확인 이런내용이 나오는데 …
    왜 실무에 있는 개발자들이 next.js를 싫어하는지 알거 같은 부분이기도 합니다.
    전체적으로 이런 부분들을 바꿔야하는거죠?….ㅜㅜ

  2. 만약에 지금 배운대로 이 사이트를 SSR 방식으로 해서 hydration해준다고 해도 hydration할 부분들을 따로 모두 처리를 해줘야하는 걸까요? 구분을 다 주어야하는거죠?

@songmijin824 songmijin824 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label May 19, 2025
@songmijin824 songmijin824 requested a review from dongqui May 19, 2025 21:18
Copy link
Collaborator

@dongqui dongqui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미진님! 정말 많은 작업을 해주셨군요! 😮 👍 👍

확실히 리엑트나 훅폼, 쿼리 등 라이브러리 등에 익숙해 지시니 속도가 붙는 거 같습니다 :)
next를 쓰시는 김에 조금 더 적극적으로 학습하고, 활용해 보셔도 좋을 거 같아요!

빌더를 시도해보려고 했는데 에러가 계속생깁니다. next.js 는 SSR이 기본값이라고 찾아보기는했는데
그냥 CSR로 빌더하려고 설정을 맞추어봐도 오류가 계속생깁니다..ㅜ
검색해보니 useRouter,useSearchParams,localStorage사용 여부 확인 이런내용이 나오는데 …
왜 실무에 있는 개발자들이 next.js를 싫어하는지 알거 같은 부분이기도 합니다.
전체적으로 이런 부분들을 바꿔야하는거죠?….ㅜㅜ

-> 지금 나는 에러는 SSR 문제도 있지만 대부분 서버컴포넌트와 클라이언트 컴포넌트 차이 때문입니다. (클라이언트 컴포넌트라고 해서 CSR이 아닙니다. 세부 개념들을 명확하게 익혀주셔야 해요! next를 활용하는 것보다 쓰는 이유, 내부 개념들이 훨씬 중요합니다~!)

서버 컴포넌트 문제라는 것은 결국 next 자체보다도 app router를 사용해서 나타나는 오류인거죠 😢
다행인건, 저도 빌드 해봤는데, 에러가 생각보다 그렇게 많지는 않았습니다! 에러 메세지 보시면서 하나하나 해결해 보셔도 좋을 거 같아요 :)

만약에 지금 배운대로 이 사이트를 SSR 방식으로 해서 hydration해준다고 해도 hydration할 부분들을 따로 모두 처리를 해줘야하는 걸까요? 구분을 다 주어야하는거죠?
-> hydration은 SSR시 자연스레 진행되는 거지 개발자가 처리하거나 구분하는 것은 아닙니다 :)
서버단에서 처리될 코드와 클라이언트에서 처리될 코드를 구분 주어야 하는 것은 맞습니다~!

어렵죠... 😢 😿 저번 중급 프로젝트 가이드 때 page router를 먼저 해보시는 것을 추천 드린 이유가 요런 느낌입니다..! app router의 경우 기존 패러다임을 많이 바꿉니다. 지금 react-query를 많이 쓰시는데 app router를 제대로 사용하시면 react-query 쓸 일도 거의 없을 거에요.
단순히 당장 app router를 적용하는 것보다, 렌더링 방식, next 쓰는 이유 app router가 나온 배경 등을 먼저 학습해 보시는 것이 더 나을 수도 있습니다 🤔

mode: 'onBlur',
});

const [addArticles, setAddArticles] = useState<ArticleCreateRequest>(INITIAL_Article);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미 react-hook-form 을 사용중이라 중복해서 상태를 다시 정의할 필요 없습니다! 🤔
우리가 react-hook-form 사용하는 이유중 하나는 uncontrolled로 form을 관리하기 때문입니다. 직접 상태를 관리하실 필요 없는거죠!

controlled/uncontrolled 컴포넌트 개념을 좀 더 알아보셔도 좋습니다 :)


const { mutate: postArticles} = usePostArticles(openConfirmModal,router);

const handleFieldBlur = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위와 마찬가지로 불필요한 함수입니다!

postArticles(addArticles);
};

function handleInputBlur(e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분도 지워도 될 거 같네요 :)

};

const onSubmit: SubmitHandler<FormValues> = (data) => {
setAddArticles((prev) => ({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

애초에 불필요한 코드이기도 하지만, 다시 생각해 보시면 좋은 것은, setState는 비동기적으로 동작하죠!
여기서 setAddArticles 한다고 해도 addArticles 상태 값은 바로 변하지 않습니다!

아래에서 실행되는 postArticles(addArticles); 는 이전의 addArticles를 넣고 있는거죠 :)

type="submit"
variant="roundedSS"
className="!absolute top-0 right-0"
disabled = { !isValid || !isDirty || !addArticles.content || !addArticles.image || !addArticles.title }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required 옵션, validation을 잘 넣어주셨다면 !isValid 로도 충분할 거 같네요! :)

(openModal: (msg: string) => void, options?: { onSuccess?: (data: any) => void }) => {

return useMutation({
mutationFn: async ({ id, isFavorited, setIsFavorited, setCount }:ProductFavoriteResponse ) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상태 값이 너무 복잡합니다..!

사실상 favorited에 대한 데이터는 queryKey: ['articleDetails', articleId], 키를 가진 쿼리에서 관리 중입니다.
그런데 LikeButton을 상태를 다시 정의하고 그 상태 값을 가지고 오고있죠..!

이렇게 되면 하나의 데이터에 두 개의 상태가 존재하게 됩니다. 지금의 경우 LikeButton 상태 값만 업데이트 되고 있으므로 만약 해당 쿼리가 다른 곳에도 사용된다면, 그쪽 데이터는 업데이트가 안 될 거에요.

queryClient에 접근해 직접 상태를 수정해주거나
https://tanstack.com/query/latest/docs/framework/react/guides/updates-from-mutation-responses

쿼리를 invalidate 시켜 업데이트 된 값을 다시 받아오셔야 합니다
https://tanstack.com/query/latest/docs/framework/react/guides/invalidations-from-mutations

}

setIsFavorited(!isFavorited);
setCount((prev: number) => isFavorited ? prev - 1 : prev + 1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 낙관적UI에 대한 가이드도 있습니다~!
https://tanstack.com/query/latest/docs/framework/react/guides/optimistic-updates


return useMutation({
mutationFn: async ({ id, isFavorited, setIsFavorited, setCount }:ProductFavoriteResponse ) => {
const token = localStorage.getItem('accessToken');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요런 로직은 mutate가 실행되기 전 밖에서 처리하는 것이 조금 더 낫습니다 :)

openModal을 실행할지, 토큰을 검사할지는 밖에서 결정해야 것이 좀더 유연하게 사용될 수 있는거죠!

return res.data;
},
onSuccess: (data) => {
openModal('게시물 등록이 완료되었습니다!');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 마찬가지로, openModal 이라는 구체적인 행위를 받아 실행하는 것보다 차라리 callback을 받아 실행 시켜주는 것이 조금 더 확장성 있게 사용될 수 있습니다 :) 같은 로직을 처리하지만 다른 페이지에서는 modal이 아니라 툴팁 같은 것들을 띄우는 경우를 예로 들 수 있을 거 같네요!

@dongqui dongqui merged commit e2dbeb4 into codeit-bootcamp-frontend:Next-송미진 May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants