Skip to content
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

[함헌규] sprint8 #112

Merged

Conversation

heonq
Copy link
Collaborator

@heonq heonq commented Dec 7, 2024

요구사항

기본 요구사항

공통

  • Github에 스프린트 미션 PR을 만들어 주세요.
  • Next.js를 사용해 진행합니다.

자유 게시판 페이지

  • 게시글 목록에서 드롭다운을 사용하여 "최신 순"으로 정렬할 수 있도록 합니다.
  • 본인이 이전 미션에서 생성한 게시글 목록 조회 API를 활용해 GET 메서드로 데이터를 가져옵니다.
  • 게시글 제목에 검색어가 일부 포함되면 해당 게시글을 검색할 수 있도록 합니다.
  • 이미지는 디폴트 이미지로 프론트엔드에서 처리해 주세요.
  • 게시글 닉네임 및 좋아요 개수 역시 임의값으로 프론트엔드에서 처리해주세요.
  • 베스트 게시글은 최신순 3개 게시글을 요청으로 데이터를 가져와 구현해주세요.
  • 자유게시판 페이지에서 특정 게시글을 클릭하면 해당 게시물의 상세 페이지로 이동합니다.

게시글 등록 & 수정 페이지

  • 각 input 필드에 정확한 placeholder 값을 입력합니다.
  • 모든 input 필드에 값을 입력하면 '등록' 버튼이 활성화됩니다.
  • 본인이 이전 미션에서 생성한 게시글 생성 API를 활용해 POST 메서드로 게시글을 등록합니다.
  • '등록' 버튼을 누르면 해당 게시물 상세 페이지로 이동합니다.
  • 게시글 수정 페이지 UI는 게시글 등록 페이지와 동일합니다.
  • 본인이 이전 미션에서 생성한 게시글 상세 API의 PATCH 메소드를 사용하여 게시물을 수정합니다.

게시글 상세 페이지

  • 본인이 이전 미션에서 생성한 게시글 상세 API의 GET 메소드를 사용하여 데이터를 가져옵니다.
  • 본인이 이전 미션에서 생성한 게시글 상세 API의 DELETE 메소드를 사용하여 게시물을 삭제합니다.
  • 댓글 input에 값을 입력하면 '등록' 버튼이 활성화됩니다.
  • 본인이 이전 미션에서 생성한 댓글 생성 API를 활용해 POST 메소드로 댓글을 등록합니다.
  • 본인이 이전 미션에서 생성한 댓글 생성 API를 활용해 PATCH 메소드로 댓글을 수정합니다.
  • 본인이 이전 미션에서 생성한 댓글 생성 API를 활용해 DELETE 메소드로 댓글을 삭제합니다.

심화 요구사항

공통

  • 디자인 시안에 따라 반응형 디자인을 구현합니다.
  • (생략 가능) 원한다면 지금까지 진행한 모든 React 코드를 Next.js로 마이그레이션 해주세요.
  • 마이그레이션에 상당한 시간이 소요될 수 있으므로 진행을 권장하지 않습니다.

스크린샷

배포 주소 : https://3-sprint-mission-fe.vercel.app/community
(서버가 켜지는 시간이 필요해 처음엔 오류가 뜰 수 있습니다.)

게시글 목록페이지
localhost_3000_community (1)

게시글 상세페이지
localhost_3000_community (3)

게시글 쓰기 페이지
localhost_3000_community (6)

멘토에게

  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

Copy link
Collaborator Author

@heonq heonq Dec 9, 2024

Choose a reason for hiding this comment

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

상품 리스트의 경우 화면크기에 따라 take의 값이 달라져야 하는데 화면크기를 확인하는 훅을 사용하기 위해 클라이언트 컴포넌트로 처리했습니다. 이 부분도 서버사이드 렌더링을 적용할 수 있는 방법이 있을까요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

말씀하신 것처럼 서버에서는 클라이언트의 화면 크기에 따른 요청 갯수를 미리 파악할 수 없기 때문에 서버 사이드만으로 처리하기에는 한계가 있습니다. 페이지가 최초로 로드될 때만 서버에서 처리하고 이후로는 클라이언트에서 처리하는 방식으로 구현하면 SEO나 CSR로 구현할 때 페이지 로드 후 데이터를 불러오기까지 빈 데이터가 노출되는 지연을 줄일 수 있을 것 같네요.

Copy link
Collaborator

@pers0n4 pers0n4 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로 마이그레이션하신 것 아닌가요? ㅋㅋ

컴포넌트를 더 나누라고 말하기 힘들 정도로 이미 잘게 나누어 놓으셔서 컴포넌트와 커밋 메시지, 단위에 관해서도 더 드릴 코멘트는 없을 것 같네요.

컴포넌트마다 상수를 적극적으로 활용하신 부분도 좋습니다.

고생하셨습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

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

말씀하신 것처럼 서버에서는 클라이언트의 화면 크기에 따른 요청 갯수를 미리 파악할 수 없기 때문에 서버 사이드만으로 처리하기에는 한계가 있습니다. 페이지가 최초로 로드될 때만 서버에서 처리하고 이후로는 클라이언트에서 처리하는 방식으로 구현하면 SEO나 CSR로 구현할 때 페이지 로드 후 데이터를 불러오기까지 빈 데이터가 노출되는 지연을 줄일 수 있을 것 같네요.

Comment on lines 16 to 25
const queryParams = new URLSearchParams({
skip: skip.toString(),
take: take.toString(),
orderBy,
...(word && { word }),
});
try {
const { data } = await axiosInstance.get<GetArticleListResponse>(
`/articles?${queryParams.toString()}`,
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

axios를 사용하면 query string은 params로 처리할 수 있는데, URLSearchParams로 처리하신 이유가 무엇인가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

작업하면서 놓친 부분이라 수정했습니다! 감사합니다.

Comment on lines 59 to 84
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
e.preventDefault();
setTagError('');
if (e.nativeEvent.isComposing) return;
const newTag = tagInput.trim();

if (newTag.length > 5 || newTag.length < 1) {
return setTagError('5글자 이내로 입력해주세요');
}

if (newTag !== '' && !tags.includes(newTag)) {
setValue('tags', [...tags, newTag]);
return setTagInput('');
}

setTagError('이미 등록된 태그입니다.');
}
};

const handleRemoveTag = (indexToRemove: number) => {
setValue(
'tags',
tags.filter((_, index) => indexToRemove !== index),
);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런 핸들러 함수와 state를 묶어서 커스텀 훅으로 정의해보는 건 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

말씀해주신대로 훅을 만들어서 사용하도록 수정했습니다 감사합니다!

Comment on lines +55 to +61
{isEditMode ? '게시글 수정하기' : '게시글 쓰기'}
</h2>
<CommonBtn
disabled={!formComplete}
type='submit'
>
{isEditMode ? '수정' : '등록'}
Copy link
Collaborator

Choose a reason for hiding this comment

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

WriteForm 컴포넌트 내부에서 출력한 텍스트를 판단하기 위해서 pathname까지 가져오고 있는데, 이 부분을 page 레이어에서 판단 후 타이틀과 버튼 내용을 props로 전달하는 방식으로 처리하면 WriteForm 내부에서 직접적인 라우터 의존도 줄일 수 있지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

writeForm에 variant prop을 추가하고 각 페이지에서 수정인지 등록인지 입력하는 방식으로 수정했습니다! 감사합니다!

Comment on lines +23 to +28
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const comment = formData.get('content') as string;
createCommentMutation.mutate(comment);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

다른 쪽에서는 form을 처리할 때 hook form 라이브러리를 사용하셨던 것 같은데, 여기서는 FormEvent로 처리하셨군요.
comment를 등록할 때는 HTMLEvent로 처리한 이유가 구현이 간단해서인가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

네 댓글의 경우 하나의 인풋만 있어서 리액트 훅 폼을 사용하지 않았습니다 이 부분도 라이브러리를 사용하는 게 나을까요?

Copy link
Collaborator

Choose a reason for hiding this comment

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

다른 사람(협업자나 컨트리뷰터)에게 공유할 수 있는 명확한 기준이 있다면 괜찮습니다. 어쩔 땐 A고, 어쩔 땐 B고, 기준이 없는 대안이 늘어나다 보면 코드가 통일되지 않고 제각각이 될 수 있거든요.
그게 제가 현재 사용하고 있는 Django 프레임워크의 가장 큰 문제....

@pers0n4 pers0n4 merged commit bd1a680 into codeit-sprint-fullstack:next-함헌규 Dec 12, 2024
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