Skip to content

Conversation

@ToKyun02
Copy link
Collaborator

@ToKyun02 ToKyun02 commented Jan 18, 2025

요구사항

기본

상품 등록 페이지

  • 상품 등록 페이지 주소는 "/addboard" 입니다.
  • 게시판 이미지는 최대 한개 업로드가 가능합니다.
  • 각 input의 placeholder 값을 정확히 입력해주세요.
  • 이미지를 제외하고 input 에 모든 값을 입력하면 '등록' 버튼이 활성화 됩니다.

상품 상세 페이지

  • 상품 상세 페이지 주소는 "/board/{id}" 입니다.
  • 댓글 input 값을 입력하면 '등록' 버튼이 활성화 됩니다.
  • 활성화된 '등록' 버튼을 누르면 댓글이 등록됩니다

심화

상품 등록 페이지

  • 회원가입, 로그인 api를 사용하여 받은accessToken을 사용하여 게시물 등록을 합니다.
  • '등록' 버튼을 누르면 게시물 상세 페이지로 이동합니다.

주요 변경사항

  • 게시글 댓글 조회는 리액트 쿼리로 구현했습니다.
  • 게시글 댓글 Form과 게시글 Form은 서버액션 + React-Hook-Form으로 구현했습니다.
  • 임시로 /signin 페이지를 만들어놨습니다.
    • 토큰 테스트 때문에 임시로 만들어놔서 개선이 필요한 페이지입니다.
    • Link로 이동하지 못하기에 확인하려면 직접 URL로 입력하셔야 합니다.
  • 게시글 생성 후 3초 뒤에 게시글 상세 페이지로 이동하도록 구현했습니다.

스크린샷

게시글 생성 페이지

image

게시글 상세 페이지 (게시글 댓글 등록 페이지)

image

배포사이트

멘토에게

  • 10주차 내용은 README.md에 정리했습니다.
  • accessToken과 refreshToken은 우선 로컬스토리지에 넣고 진행했습니다.

@ToKyun02 ToKyun02 requested a review from Lanace January 18, 2025 11:18
@ToKyun02 ToKyun02 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jan 18, 2025
@ToKyun02 ToKyun02 self-assigned this Jan 18, 2025
Copy link
Collaborator

@Lanace Lanace left a comment

Choose a reason for hiding this comment

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

개발하시느라 고생 많으셨습니다ㅎㅎ!

dayjs.extend(relativeTime);
dayjs.extend(customParseFormat);

export default function getDiffTime(dateStr: string) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

이런 유틸로 분리해둔거 너무 좋네요ㅎㅎ!
조금더 확장성을 가져가보면 string 만 받는게 아니라 number나 date 를 받아서 처리할 수도 있을것같아요!

export default function getDiffTime(dateStr: string | number | Date): strubg {
  ...
}

이게 괜찮은게 dayjs도 그렇고 new Date 도 그렇고 생성자로 string 도 받고 number도 받고 Date도 받거든요ㅎㅎ

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 +9 to +16
const now = dayjs();
const date = dayjs(dateStr);

const diffInSeconds = now.diff(date, 'second');
const diffInMinutes = now.diff(date, 'minute');
const diffInHours = now.diff(date, 'hour');
const diffInDays = now.diff(date, 'day');
const diffInMonths = now.diff(date, 'month');
Copy link
Collaborator

Choose a reason for hiding this comment

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

만약에 이상한 input 값이 들어왔을때 예외처리를 해주면 좋을것같아요~

dayjs("이상한 날짜값")

image

이렇게 나오니 dayjs(value).isValie() 로 값이 올바른지 검증을 해주면 좋을것같아여ㅎㅎ!

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 +18 to +37
if (diffInSeconds < 60) {
return `${diffInSeconds}초 전`;
}
if (diffInMinutes < 60) {
return `${diffInMinutes}분 전`;
}
if (diffInHours < 24) {
return `${diffInHours}시간 전`;
}
if (diffInDays < 7) {
return `${diffInDays}일 전`;
}
if (diffInDays < 30) {
const diffInWeeks = Math.floor(diffInDays / 7);
return `${diffInWeeks}주 전`;
}
if (diffInMonths < 12) {
return `${diffInMonths}개월 전`;
}
return date.format('YYYY.MM.DD HH:mm');
Copy link
Collaborator

Choose a reason for hiding this comment

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

그리고 참고로 https://day.js.org/docs/en/display/from-now 같은 기능도 있어요!

image

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 +3 to +7
export async function submitComment(formData: FormData, accessToken: string | null, refreshToken: string | null, id: number) {
if (!accessToken || !refreshToken) {
return { success: false, message: '로그인이 필요합니다.' };
}
const formDataObject = Object.fromEntries(formData.entries());
Copy link
Collaborator

Choose a reason for hiding this comment

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

혹시 값을 formData로 받아와서 Object.fromEntries로 object로 만들어주는 이유가 있나요?

그냥 차라리 object로 받아오는게 편하지 않을까여?

FormData같은경우엔 안에 어떤 값이 key-value로 있는지 몰라서 사용하기 조금 어렵거든요ㅠ

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

처음엔 FormData로 바로 request 요청을 보내려고했는데, 백엔드에서 Content-type이 application/json만 허용되는 걸 뒤늦게 알아서 이렇게 했습니다..

Comment on lines +22 to +47
if (response.status === 401) {
const refreshResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/refresh-token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ refreshToken }),
});
if (refreshResponse.ok) {
const { accessToken: newAccessToken } = await refreshResponse.json();

const retryResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/articles/${id}/comments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${newAccessToken}`,
},
body: JSON.stringify(formDataObject),
});

if (retryResponse.ok) {
return { success: true, message: '댓글 등록이 완료되었습니다.', accessToken: newAccessToken };
} else {
return { success: false, message: '댓글 등록 중 오류가 발생했습니다.' };
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

refresh-token을 사용해서 재발행하고 처리하는 로직인데, 이렇게되니 관리하기가 어렵겠죠ㅠ

아마 다른 API에 경우에도 refresh 해야하는 경우가 많을텐데, 매번 이렇게 처리하면 힘들꺼에요!ㅠ

그래서 axios에 retry라던가 interceptor같은 기능을 통해 해결하면 조금더 간결하게 처리하실 수 있을꺼에요!ㅎㅎ

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 +53 to +56
} catch (error) {
console.log(error);
return { success: false, message: '서버 요청에 실패했습니다.' };
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

예외처리 깔끔하게 잘 하신것같아요ㅎㅎ!

그 console.log 말고도 console.error 도 있어요~

이렇게하면 빨간색으로 표시되여ㅋㅋ
같은 기능으로 console.warn 도 있습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

감사합니다! 습관적으로 console.log로 사용한 것 같습니다..

import CommentForm from '@/components/CommentForm';
import ArticleCommentList from '@/components/ArticleCommentList';

async function Title({ id }: { id: string }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

id 값이 string 으로 들어오는데 number값으로 오는게 정상적인 경우겠져...

물론 서버쪽에서 예외처리 했을꺼고 해당하는 article도 없을꺼라 문제 없겠지만 서버입장에선 이상한 데이터가 요청으로 계속 올 수 있어서...

이런 부분은 프론트가 validation 처리해서 가능하면 서버의 부담을 줄여주면 좋아요ㅎㅎ!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

조언 감사합니다! 이 부분도 고려하겠습니다.

);
}

export default async function DetailBoard({ params }: { params: Promise<{ id: string }> }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

혹시 Promise<{ id: string }> 로 값을 받으신 이유가 있을까요?ㅠ

보통은 Promise로 값을 받는 경우는 없거든요;;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

찾아보니 next15에서 라우팅 URL의 파라미터를 받으려면 비동기로 받아야 한다고 나와 있어서 그랬습니다!
https://nextjs.org/docs/app/building-your-application/upgrading/version-15#params--searchparams

new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
Copy link
Collaborator

Choose a reason for hiding this comment

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

staleTime을 따로 Infinity로 준 이유가 있나여??

이러면 기존 데이터가 계속해서 보일것같은데ㅠ

Copy link
Collaborator

Choose a reason for hiding this comment

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

그래도 Provider 형태로 잘 만드셨네여ㅎㅎ

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

제 생각으론 무한 스크롤링을 통해 이후 데이터를 받고, 댓글을 추가하면 기존 캐싱을 무효화시키는 형식으로 로직을 구성하여서 stale시간을 Inifinity로 줬습니다!

@Lanace Lanace merged commit 9ccdc47 into codeit-bootcamp-frontend:Next-김도균 Jan 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