Skip to content

Conversation

@rak517
Copy link
Collaborator

@rak517 rak517 commented Jan 19, 2025

요구사항

기본

상품 등록 페이지

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

상품 상세 페이지

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

심화

상품 등록 페이지

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

주요 변경사항

  • accessToken, refreshToken을 로컬 스토리지에 넣고 진행했습니다.
  • 게시글 작성, 댓글 작성을 accessToken을 통해 기능을 구현했습니다.
  • 페이지 라우터, fetch를 사용했습니다

스크린샷

스크린샷 2025-01-19 20 55 23
스크린샷 2025-01-19 20 55 52
스크린샷 2025-01-19 20 56 20

멘토에게

  • 로그인 페이지, 회원가입 페이지의 UI는 빠른 진행을 위해 UI만 vercel의 v0 이용해서 만들었습니다.
  • 기능 구현을 중점으로 작업을해서 코드 가독성, 코드 분리 등이 좀 미흡하고 SPRINT9 에 받은 피드백은 아직 적용하지 못했습니다.
  • 급하게 작업하느라 다음 스프린트 미션에서 리팩토링 진행하겠습니다.
  • 보안 인증 및 세션 관리를 구현하는 데 도움이되는 Next.js 호환 라이브러리도 사용해보고 싶은데 보통 어떤걸 사용하는지? 현재 코드에서 그런 라이브버리를 사용하면 어떤 방향으로 가면 좋을지 궁금합니다.

@rak517 rak517 requested a review from Lanace January 19, 2025 12:03
@rak517 rak517 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Jan 19, 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.

전반적으로 코드가 깔끔하고 일정한 형태를 갖추고 있어서 보기도 좋고 예상 가능한 코드여서 리뷰하기도 좋았네여ㅎㅎ!

개발하느라 고생 많으셨어요~!!

Comment on lines +11 to +13
.link {
text-decoration: none;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

자주 등장하게 될텐데 저는 개인적으로 이런 스타일은 global에 적용해두긴 해요ㅎㅎ;;

예외적으로 몇개 있긴 한데, 링크에 underline도 그렇고 appearance도 그래요...!
한번 참고만 해두셔도 좋을것같아요~

https://developer.mozilla.org/en-US/docs/Web/CSS/appearance

물론 해주신 방법대로 해도 문제될건 없습니다!

import Link from "next/link";

export default function BestItemCard({ content, updatedAt, likeCount, writer, image, title }: Article) {
export default function BestItemCard({ id, content, updatedAt, likeCount, writer, image, title }: Article) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

ArticleItemCard 이랑 차이점이 size밖에 없는것같은데, 이런 부분은 아에 props로 size를 지정해주면 오히려 관리하기가 좀더 좋을것같아요ㅎㅎ!


export default function BestItemCard({ content, updatedAt, likeCount, writer, image, title }: Article) {
export default function BestItemCard({ id, content, updatedAt, likeCount, writer, image, title }: Article) {
const formattedDate = formatDate(updatedAt);
Copy link
Collaborator

Choose a reason for hiding this comment

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

큰건 아닌데, 이런 상수성 변수들은 차라리 useMemo 로 감싸두면 좋긴 해요ㅎㅎ!
useMemo랑 useCallback 쓰는 습관을 들이면 좋긴 한데, React 버전 올라가면서 없어질꺼라는 얘기도 나오긴 하더라구요;;

Comment on lines +1 to +2
const ACCESS_TOKEN_KEY = "accessToken";
const REFRESH_TOKEN_KEY = "refreshToken";
Copy link
Collaborator

Choose a reason for hiding this comment

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

자잘하긴 하지만...ㅎㅎ;;
보통 상수값은 대문자로 값도 넣어주긴 해요!

const ACCESS_TOKEN_KEY = "ACCESS_TOKEN_KEY" as const;
const REFRESH_TOKEN_KEY = "REFRESH_TOKEN_KEY"  as const;

Comment on lines +4 to +20
export function saveTokens(accessToken: string, refreshToken: string) {
localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);
localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);
}

export function getAccessToken() {
return typeof window !== "undefined" ? localStorage.getItem(ACCESS_TOKEN_KEY) : null;
}

export function getRefreshToken() {
return typeof window !== "undefined" ? localStorage.getItem(REFRESH_TOKEN_KEY) : null;
}

export function clearTokens() {
localStorage.removeItem(ACCESS_TOKEN_KEY);
localStorage.removeItem(REFRESH_TOKEN_KEY);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

깔끔하고 좋네요ㅎㅎ!

clearToken 도 원하는 키값에 대해서만 저렇게 수정하면 좋아요

혹시나 싶어서... 전체 다 없애고 싶으시면 아래처럼 하시면 되요!

localStorage.clear()

물론 이거보단 위에서 해주신 각각 키값으로 삭제하는게 좀더 좋긴 해요!
원하지 않게 사이드이펙트가 생길 수 있어서요ㅠ

Comment on lines +4 to +8
interface PostPayload {
title: string;
content: string;
image?: File | null;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

이것도 별도의 types.ts 로 분리시켜주면 조금더 좋을것같긴 하네요ㅎㅎ!

Copy link
Collaborator

Choose a reason for hiding this comment

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

너무 좋긴 한데, API 별로 Service가 나오면 관리하기가 조금 어려워지지 않을까요?

물론 지금 하신것도 잘못된건 아니긴 합니다만 관리하는 측면에서는 어느정도 묶어주는게 좀더 낫지않나 싶어서요ㅎㅎ

export default function SignupPage() {
const router = useRouter();

const handleSignup = async (formData: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

formData 라고 하니까 뭔가 진짜 FormData 인것같긴 하네여ㅎㅎ;;

진짜 FormData 라고 하면 formData 라고 이름을 지어도 괜찮은데, 단순 object인데 formData 라고 하면 혼동을 줄 수 있으니 다른 이름을 쓰는건 어떨까요?

저는 보통 API 요청을 보내기 위한 객체는 request 나 좀더 구체적으로 signupRequest 같은이름을 쓰긴 해요ㅎㅎ!

Copy link
Collaborator

Choose a reason for hiding this comment

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

아 그리고 차라리 api 의 타입을 정의할때 같이 정의해버려서 해당 타입을 쓰면 편해요ㅎㅎ!

api.ts

export const signup = async(request: SignupRequest): Promise<SignupResponse> => {
  ...
}

이런식으루요ㅎㅎ!
어차피 저렇게 타입을 정의해두면 쓰게되더라구요

Comment on lines +14 to +15
const { accessToken, refreshToken } = await login(formData);
saveTokens(accessToken, refreshToken);
Copy link
Collaborator

Choose a reason for hiding this comment

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

물론 여기서 saveToken을 해주어도 되긴하지만 login api의 특징을 생각해보았을땐 아에 saveToken을 login 함수 안쪽에서 해주는것도 좋을것같은데 어떠신가요??

왜냐하면 로그인을 호출해서 성공했다는건 token을 storage 에 저장하는거니까요ㅎㅎ!

하나의 동작으로 보는거죠~

Comment on lines +94 to +98
if (typeof id !== "string") {
return {
notFound: true,
};
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

오 이런 예외처리 너무 좋습니다!
외부에서 들어오는 input 에 대해서는 항상 타입과 값을 검증하는 validation 처리를 해주세요!

그런데 위 경우에선 딱히 의미가 없긴 해요ㅠ
왜냐하면 어차피 context.params에 있는 값은 다 string 일꺼거든요ㅠ

그래서 이렇게 검증하는것보단

if (!Number(id)) {
  return {
    notFound: true
  }
}

가 좀더 유용할것같아요!

참고로 NaN은 false 랑 동일해요ㅎㅎ! 정확히는 falsy...

@Lanace
Copy link
Collaborator

Lanace commented Jan 20, 2025

천천히 하셔도 괜찮아요ㅎㅎ!

아 그리고 nextjs에서 인증 관련해서는 보통 next-auth를 저는 가장 많이 쓰긴 한데, auth0 나 다른 인증 라이브러리도 많이들 써요...!

@Lanace Lanace merged commit fc5dbca 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