Skip to content

Next 고을호 - #116

Open
goeulho wants to merge 3 commits into
codeit-sprint-fullstack:next-고을호from
goeulho:next-고을호

Hidden character warning

The head ref may contain hidden characters: "next-\uace0\uc744\ud638"
Open

Next 고을호#116
goeulho wants to merge 3 commits into
codeit-sprint-fullstack:next-고을호from
goeulho:next-고을호

Conversation

@goeulho

@goeulho goeulho commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

완료한 기능

로그인/회원가입

  • JS → Next.js 마이그레이션
  • 회원가입/로그인 페이지 이동 연결
  • POST /auth/signIn, /auth/signUp 연동 (JWT)
  • accessToken localStorage 저장
  • 이미 로그인된 상태면 /items로 리다이렉트
  • 소셜로그인 링크 (구글/카카오)
  • 로그인/회원가입 실패시 인풋별 에러메시지 + 모달
  • 비밀번호 눈모양 토글

GNB

  • 미인가시 로그인 버튼
  • 인가시 유저정보 API 프로필 표시

상품 상세 페이지

  • /items/{itemId} 라우팅
  • 목록으로 돌아가기
  • GET 상품상세 (인가된 사용자만)
  • 좋아요/좋아요취소 (인가된 사용자만)
  • 댓글 조회/생성/수정/삭제 (인가된 사용자만)
  • 상품 수정/삭제 + 삭제 확인 모달

심화 요구사항

  • react-hook-form
  • 반응형 디자인
  • axios interceptors
  • React Query 마이그레이션
  • Prefetching / 캐싱

시간 관계상 상품 수정/삭제 기능과 심화 요구사항은 완료하지 못했습니다.
AI의 도움을 받아 진행했으며, 질문을 주고받으며 개념을 이해하려 노력했습니다.

@goeulho
goeulho requested a review from suyeoniii August 23, 2026 18:41
import { createContext, useContext, useState, useEffect } from "react";
import Header from "./header";

const AuthContext = createContext(null);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👀
인증 상태를 Context로 잘 빼주셨네요!

createContext(null) 이라 값 타입이 null 로 고정돼서, 19번째 줄 value 와 useAuth() 쓰는 곳들에서 타입 오류가 납니다. 12번째 줄 useState(null) 도 마찬가지구요.

npm run build 한번 돌려봐주세요! 키워드는 createContext 제네릭 입니다.
https://react.dev/reference/react/createContext

initialFavoriteCount: number;
};

export const FavoriteButton = ({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👀
앗 위쪽 5번째 줄에 타입은 잘 정의해두셨는데, 정작 컴포넌트 파라미터에는 안붙어있네요!

이 상태면 props 3개가 전부 any 가 되는데, tsconfig 에 strict 가 켜져있어서 빌드할 때 오류가 날거에요.
타입 이름도 컴포넌트 이름이랑 똑같아서 헷갈리기 쉬워요. FavoriteButtonProps 처럼 바꿔주시면 좋겠습니다!

Comment thread app/(main)/items/page.tsx
<h4>베스트상품</h4>

<h4>판매 중인 상품</h4>
<SearchProduct initialProducts={list} />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👀
상품 목록은 실제 API로 잘 옮겨주셨는데, 여기로 넘겨주는 SearchProduct 는 아직 localhost:4000 을 보고 응답도 예전 목 서버 형태인 data.products 로 꺼내고 있어요.

그래서 검색해보시면 목 서버가 켜져있을 땐 로컬 데이터가 나오고, 꺼져있으면(배포 환경도요) fetch 가 실패해서 콘솔에만 에러가 찍히고 검색이 아무 반응이 없을거에요.

데이터 소스를 옮기실 땐 그걸 쓰는 곳까지 같이 확인해주세요!

}
}, [id, accessToken]);

if (!accessToken) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👀
로그인한 상태로 들어와도 "로그인이 필요한 페이지입니다" 가 잠깐 보였다가 상품이 나올거에요. 헤더의 로그인 버튼도 같이 깜빡일겁니다.

AuthProvider 가 localStorage 를 useEffect 에서 읽는데, useEffect 는 첫 렌더링이 끝난 뒤에 실행되기 때문이에요.

accessToken === null 하나로 "아직 안읽었다" 와 "로그인을 안했다" 를 같이 표현하는게 원인인데, 이 둘은 보여줘야 할 화면이 다르죠!

body: JSON.stringify({ content }),
},
);
const newComment = await res.json();

@suyeoniii suyeoniii Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👀
지난 미션에서도 드렸던 코멘트인데, 여기도 fetch가 성공하는 케이스만 처리되어 있습니다!

fetch는 서버가 400, 401을 내려줘도 오류를 던지지 않아요. 그래서 토큰이 만료되면 에러 객체가 그대로 댓글 목록에 들어가서, 내용이 비어있는 댓글이 하나 생기고 새로고침하면 사라지게 됩니다. (아래 수정/삭제도 마찬가지에요)

res.ok 로 한번 막아주시고, 실패했을 때 사용자가 알 수 있도록 처리해주세요!

},
},
);
setIsFavorite(!isFavorite);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👀
여기도 응답 확인 없이 바로 화면을 바꾸고 있어서, 401이나 500이 와도 하트는 눌린 것처럼 보입니다. 새로고침하면 원래대로라 "눌렀는데 왜 안되지?" 하게 될거에요.

먼저 바꿔두고 실패하면 되돌리는 방식은 낙관적 업데이트(optimistic update) 라고 해요 :)

그리고 21번째 줄 console.log("토큰확인", accessToken) 은 꼭 지워주세요! 액세스 토큰은 그 자체가 로그인 자격증명이거든요.

Comment thread app/layout.tsx
return (
<html lang="ko">
<body className="min-h-full flex flex-col">
<AuthProvider>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍
AuthProvider 가 클라이언트 컴포넌트인데, {children} 을 그 안쪽에 자식으로 넣어주셨네요!

이렇게 하면 children 으로 들어오는 페이지들은 클라이언트로 바뀌지 않고 서버 컴포넌트로 그대로 남습니다. (AuthProvider 안에서 직접 import 해서 그렸다면 전부 클라이언트가 됐을거에요)

Next 에서 자주 헷갈리는 부분인데 잘 잡아주셨어요 :)

Comment thread app/(main)/items/page.tsx
}: {
searchParams: Promise<SearchParams>;
}) => {
const { page = "1" } = await searchParams;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

👍
오 지난 미션에 드렸던 page 기본값 코멘트, 이번에 잘 반영해주셨네요! :)

한걸음 더 나가서 사용자가 주소창에 /items?page=abc 나 /items?page=-5 를 직접 입력하면 어떻게 될지도 한번 고민해보시면 좋겠습니다!

@suyeoniii suyeoniii left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

을호님 고생많으셨습니다!

로그인/회원가입부터 토큰 인증, 좋아요, 댓글까지 기능 흐름을 잘 이어주셨습니다. 인증 상태를 AuthProvider로 분리해서 여러 페이지가 같이 쓰게 한 구조가 특히 좋았어요.

아래 2가지를 우선 확인 부탁드립니다~!

  1. npm run build 로 타입 오류를 잡아주세요. authProvider 와 favoriteButton 의 타입 때문에 지금은 빌드가 안됩니다. 제출 전에 build 한번 돌려보는 습관 들이시면 좋아요!

  2. fetch 응답의 res.ok 확인 부탁드립니다!

이 외에 적용해보면 좋을 것들은

  • react query 를 한번 적용해보세요!
  • 로그인/회원가입 폼을 react-hook-form 이용하여 구현해보세요
  • 실패 안내가 alert 인데 대신 모달과 input 아래 오류 문구로 구현해보세요
  • 지난 미션에 드린 next/dist import, 의 ./ 상대경로, 미사용(Enter 제출)도 반영 부탁드려요!
  • console.log 와 안쓰는 import, 빈 정리하면 더 좋을 것 같아요

고생하셨습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants