Skip to content

feat: 온보딩 페이지 구현 및 CheckBox 컴포넌트 생성 (#38)#83

Merged
wkdtnqls0506 merged 28 commits into
mainfrom
feature/PRODUCT-49
Jul 18, 2025
Merged

feat: 온보딩 페이지 구현 및 CheckBox 컴포넌트 생성 (#38)#83
wkdtnqls0506 merged 28 commits into
mainfrom
feature/PRODUCT-49

Conversation

@wkdtnqls0506
Copy link
Copy Markdown
Contributor

@wkdtnqls0506 wkdtnqls0506 commented Jul 14, 2025

✅ 이슈 번호

close #38


🪄 작업 내용 (변경 사항)

  • /member/_api 회원(member) 관련 API 및 쿼리 생성
  • /member/onboarding 페이지 생성
    • OnboardingFunnel.tsx
    • NicknameStep.tsx
    • PhoneNumberStep.tsx
    • AgreeStep.tsx
  • CheckBox 컴포넌트 생성
  • 쿼리 키 관리를 위한 /constants/queryKeys.constants.ts 상수 생성

📸 스크린샷

2025-07-14.5.40.35.mov

💡 설명

  • 온보딩 플로우는 회원가입 이후 한 번만 진입하는 구조이고 닉네임/전화번호 입력은 모두 회원 정보(member)와 연관된 기능이라고 판단하여, member 도메인 하위에 배치하여 관련 로직을 하나의 도메인 내에서 관리할 수 있도록 구성하였습니다!

🗣️ 리뷰어에게 전달 사항

  1. API Error 메세지를 서버에서 내려주는 대로 사용할 수 있도록 변경

9d058b9

기존에는 에러 발생 시 항상 알 수 없는 오류가 발생했습니다로만 표시되던 문제를, 서버에서 전달하는 실제 에러 메시지를 띄울 수 있도록 수정하였습니다!

// 수정 전 응답
NicknameStep.tsx:56 닉네임 중복 확인 실패: UnauthorizedException: 알 수 없는 오류가 발생했습니다.
    at apiErrorHandler (webpack-internal:///(app-pages-browser)/./src/lib/api/client.ts:33:19)
    at async function_ (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/ky@1.7.5/node_modules/ky/distribution/core/Ky.js:43:29)
    at async Ky._retry (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/ky@1.7.5/node_modules/ky/distribution/core/Ky.js:199:20)
    at async getNicknameCheck (webpack-internal:///(app-pages-browser)/./src/app/member/_api/member.api.ts:28:5)

// 수정 후 응답
NicknameStep.tsx:56 닉네임 중복 확인 실패: UnauthorizedException: 인증되지 않은 회원입니다.
    at apiErrorHandler (webpack-internal:///(app-pages-browser)/./src/lib/api/client.ts:32:19)
    at async function_ (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/ky@1.7.5/node_modules/ky/distribution/core/Ky.js:43:29)
    at async Ky._retry (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/ky@1.7.5/node_modules/ky/distribution/core/Ky.js:199:20)
    at async getNicknameCheck (webpack-internal:///(app-pages-browser)/./src/app/member/_api/member.api.ts:28:5)
  1. Funnel 참고자료 https://use-funnel.slash.page/ko

  2. 커밋별로 보시는 걸 추천드립니다 ✨


📍 트러블 슈팅

Summary by CodeRabbit

  • New Features

    • 회원 온보딩 플로우(닉네임, 휴대폰 번호, 약관 동의) 도입 및 관련 컴포넌트 추가
    • 닉네임 및 휴대폰 번호 중복 확인, 입력값 검증 기능 제공
    • 온보딩 단계별 유효성 검사 및 실시간 에러 메시지 표시
    • 체크박스 UI 컴포넌트 및 스토리북 문서화 추가
    • 로그인 성공 시 회원 온보딩 페이지로 조건부 리다이렉트 기능 추가
    • 회원 정보 조회 및 수정 API, React Query 훅 추가
    • 전화번호 자동 포맷팅 유틸리티 도입
    • 온보딩 레이아웃 및 네비게이션 바 구현
  • Bug Fixes

    • 리스트 스타일 초기화 CSS가 li 요소에도 적용되도록 수정
  • Style

    • 모바일 환경 대응을 위한 레이아웃 minHeight 단위 개선(vh → dvh)
    • 온보딩 및 체크박스 컴포넌트 스타일 추가
  • Chores

    • 온보딩 및 폼 검증을 위한 외부 라이브러리(zod, @hookform/resolvers, @use-funnel/browser 등) 의존성 추가
  • Documentation

    • 체크박스 컴포넌트 스토리북 문서 추가

@wkdtnqls0506 wkdtnqls0506 added the ✨ feature 새로운 기능 추가 label Jul 14, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 14, 2025

"""

Walkthrough

온보딩 페이지의 UI와 관련된 전체 구조가 새롭게 도입되었습니다. 닉네임, 핸드폰 번호, 약관 동의의 각 단계를 위한 컴포넌트, API, 타입, 유틸리티, 스타일, 그리고 퍼널 흐름 관리가 추가되었으며, 체크박스 UI 컴포넌트와 관련된 스타일 및 스토리북 설정도 포함됩니다. 일부 기존 인증 및 레이아웃 코드도 변경되었습니다.

Changes

파일/경로 요약 변경 요약
src/app/member/onboarding/_components/** 온보딩 퍼널, 닉네임/핸드폰/약관 단계별 컴포넌트, 타이틀 컴포넌트, 각종 스타일 및 인덱스 파일 추가
src/app/member/onboarding/_constants/agreement.constants.ts 약관 동의 항목 상수 추가
src/app/member/onboarding/_types/** 온보딩 단계별 타입 및 인덱스 파일 추가
src/app/member/onboarding/layout.* 온보딩 레이아웃 컴포넌트 및 스타일 추가
src/app/member/onboarding/page.tsx 온보딩 페이지 엔트리 컴포넌트 추가
src/app/member/_api/** 멤버 관련 API, 쿼리, 타입, 인덱스 파일 추가
src/app/member/_schemas/** 닉네임, 핸드폰 번호 유효성 검증 스키마 및 인덱스 파일 추가
src/app/member/_types/** 멤버 타입 및 인덱스 파일 추가
src/app/member/_utils/** 핸드폰 번호 유틸 함수 및 인덱스 파일 추가
src/components/ui/CheckBox/** 체크박스 UI 컴포넌트, 스타일, 스토리북, 인덱스 파일 추가
src/app/(auth)/login/callback/page.tsx 로그인 콜백 성공 시 온보딩 여부에 따라 리다이렉트 로직 변경
src/app/(auth)/_api/auth/auth.queries.ts 로그인 성공 시 세션 캐시 초기화 및 멤버 쿼리 캐시 갱신 추가
src/app/layout.css.ts, src/styles/reset.css.ts 뷰포트 단위 및 리스트 스타일 리셋 등 일부 글로벌 스타일 수정
src/lib/api/client.ts, src/lib/api/type.ts API 에러 메시지 처리 및 Authorization 헤더 포맷 변경, 에러 타입 수정
package.json react-hook-form, zod, @use-funnel/browser 등 온보딩 및 폼 관련 라이브러리 의존성 추가

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OnboardingFunnel
  participant NicknameStep
  participant PhoneNumberStep
  participant AgreeStep
  participant MemberAPI

  User->>OnboardingFunnel: 페이지 진입
  OnboardingFunnel->>NicknameStep: 닉네임 입력 표시
  User->>NicknameStep: 닉네임 입력 및 제출
  NicknameStep->>MemberAPI: 닉네임 중복 검사
  MemberAPI-->>NicknameStep: 결과 반환
  NicknameStep->>OnboardingFunnel: 유효시 다음 단계로 이동

  OnboardingFunnel->>PhoneNumberStep: 핸드폰 번호 입력 표시
  User->>PhoneNumberStep: 번호 입력 및 제출
  PhoneNumberStep->>MemberAPI: 번호 중복 검사
  MemberAPI-->>PhoneNumberStep: 결과 반환
  PhoneNumberStep->>OnboardingFunnel: 유효시 다음 단계로 이동

  OnboardingFunnel->>AgreeStep: 약관 동의 표시
  User->>AgreeStep: 필수 약관 동의 및 제출
  AgreeStep->>MemberAPI: 회원 정보 업데이트
  MemberAPI-->>AgreeStep: 성공 응답
  AgreeStep->>User: 홈으로 이동
Loading

Assessment against linked issues

Objective (이슈 번호) Addressed Explanation
닉네임 입력 UI/검증/중복확인 (#38)
핸드폰 번호 입력 UI/검증/중복확인 (#38)
약관 동의 UI/필수/선택/전체동의/제출 (#38)

Assessment against linked issues: Out-of-scope changes

Code Change (file_path) Explanation
없음

Possibly related PRs

Poem

🐰
온보딩 길 따라 hop hop hop,
닉네임 쓰고, 번호 톡톡,
약관 동의 체크박스 콕콕콕!
퍼널 따라 한 걸음씩,
토끼도 회원이 되고 싶지!
🥕✨
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx

Oops! Something went wrong! :(

ESLint: 9.27.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.3.2_eslint@9.27.0_jiti@2.4.2__typescript@5.8.3/node_modules/eslint-config-next/index.js".

If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e45d1ec and 64f7190.

📒 Files selected for processing (1)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions Bot requested a review from Seojunhwan July 14, 2025 14:53
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 14, 2025

Walkthrough

온보딩 플로우를 위한 UI와 관련 API, 타입, 유틸리티, 스타일, 컴포넌트가 대거 추가되었습니다. 닉네임, 휴대폰 번호, 약관 동의 스텝별 컴포넌트와 폼 검증, React Query 기반 API 통신, 온보딩 레이아웃 및 스타일, 체크박스 UI 등이 포함됩니다. 로그인 콜백 페이지의 온보딩 진입 로직도 보완되었습니다.

Changes

파일/경로 그룹 변경 요약
src/app/(auth)/login/callback/page.tsx 로그인 성공 시 React Query 캐시 갱신 및 온보딩 플로우 진입 로직 추가
src/app/layout.css.ts, src/styles/reset.css.ts CSS 단위 및 reset 규칙 변경 (dvh 적용, li에 list-style 해제)
src/app/member/_api/* 멤버 API/쿼리 모듈 신설 및 인덱스 배럴 추가
src/app/member/_schemas/* 닉네임/전화번호 zod 스키마 및 인덱스 배럴 추가
src/app/member/_types/* 멤버 및 온보딩 관련 타입 정의 및 인덱스 배럴 추가
src/app/member/_utils/* 전화번호 포매팅 유틸 추가 및 인덱스 배럴화
src/app/member/onboarding/_components/* 닉네임, 전화번호, 약관 동의, 타이틀, 퍼널 등 온보딩 UI 컴포넌트 및 스타일 신설
src/app/member/onboarding/_constants/agreement.constants.ts 약관 동의 항목 상수 배열 정의
src/app/member/onboarding/layout.* 온보딩 레이아웃 컴포넌트 및 스타일 추가
src/app/member/onboarding/page.tsx 온보딩 진입 페이지 추가
src/components/ui/CheckBox/* 체크박스 UI 컴포넌트, 스타일, 스토리북 스토리 신설
src/constants/queryKeys.constants.ts, src/constants/index.ts React Query 키 상수 정의 및 배럴 추가
src/lib/api/client.ts, src/lib/api/type.ts API 에러 핸들링 개선 및 타입 보강

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant OnboardingPage
    participant OnboardingFunnel
    participant NicknameStep
    participant PhoneNumberStep
    participant AgreeStep
    participant MemberAPI

    User->>OnboardingPage: 접속
    OnboardingPage->>OnboardingFunnel: 렌더링
    OnboardingFunnel->>NicknameStep: 첫 스텝 진입
    User->>NicknameStep: 닉네임 입력 및 확인
    NicknameStep->>MemberAPI: 닉네임 중복 체크
    NicknameStep-->>OnboardingFunnel: 유효 닉네임 전달
    OnboardingFunnel->>PhoneNumberStep: 다음 스텝 진입
    User->>PhoneNumberStep: 전화번호 입력 및 확인
    PhoneNumberStep->>MemberAPI: 전화번호 중복 체크
    PhoneNumberStep-->>OnboardingFunnel: 유효 전화번호 전달
    OnboardingFunnel->>AgreeStep: 약관 동의 스텝 진입
    User->>AgreeStep: 약관 체크 및 제출
    AgreeStep->>MemberAPI: 멤버 정보 업데이트
    AgreeStep-->>User: 온보딩 완료, 홈 이동
Loading

Assessment against linked issues

Objective Addressed Explanation
닉네임 입력 UI 및 검증 (#38)
핸드폰 번호 입력 UI 및 검증 (#38)
약관 동의 UI 및 필수/선택 체크 (#38)

Possibly related PRs

Poem

🐰
온보딩 길목에 봄바람 불어,
닉네임 적고 전화번호 불러,
체크박스 톡톡 약관도 OK!
새로 만든 퍼널 따라
토끼도 회원이 되었네!
🎉

— 코드래빗 드림

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/app/layout.css.ts

Oops! Something went wrong! :(

ESLint: 9.27.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.3.2_eslint@9.27.0_jiti@2.4.2__typescript@5.8.3/node_modules/eslint-config-next/index.js".

If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.

src/app/member/_api/member.queries.ts

Oops! Something went wrong! :(

ESLint: 9.27.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.3.2_eslint@9.27.0_jiti@2.4.2__typescript@5.8.3/node_modules/eslint-config-next/index.js".

If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.

src/app/member/_api/member.api.ts

Oops! Something went wrong! :(

ESLint: 9.27.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.3.2_eslint@9.27.0_jiti@2.4.2__typescript@5.8.3/node_modules/eslint-config-next/index.js".

If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.

  • 36 others
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jul 14, 2025

🎨 Storybook Preview: https://685a32a1c0bbd269fdb67af4-kvcecvtoar.chromatic.com/
🔗 Chromatic Build: https://www.chromatic.com/build?appId=685a32a1c0bbd269fdb67af4&number=107
🕖 Updated at: 2025년 07월 17일 03시 48분 01초

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (13)
src/app/member/_utils/phoneNumberUtils.ts (1)

1-11: 전화번호 포맷팅 로직이 올바르지만 개선 사항이 있습니다.

핵심 로직은 한국 전화번호 형식에 맞게 잘 구현되어 있습니다. 다음 개선사항을 고려해보세요:

  1. 11자리를 초과하는 전화번호에 대한 처리 로직 추가
  2. 함수의 목적과 예상 입력 형식에 대한 JSDoc 문서화
  3. 빈 문자열이나 잘못된 입력에 대한 명시적 처리

다음과 같은 개선을 제안합니다:

+/**
+ * 전화번호 문자열을 한국 전화번호 형식으로 포맷팅합니다.
+ * @param value - 포맷팅할 전화번호 문자열
+ * @returns 포맷팅된 전화번호 문자열
+ */
export const phoneNumberUtils = (value: string) => {
  const phoneNumber = value.replace(/[^0-9]/g, "");
  if (phoneNumber.length < 4) return phoneNumber;
  if (phoneNumber.length < 8)
    return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
  if (phoneNumber.length === 10)
    return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`;
  if (phoneNumber.length === 11)
    return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
+  // 11자리 초과 시 처리 로직 추가 고려
  return phoneNumber;
};
src/app/member/_api/index.ts (1)

1-2: 와일드카드 재-export 범위 최소화 제안

export * 방식은 의도치 않은 내부 util 함수까지 외부로 노출될 수 있어 유지보수 시 범위 파악이 어려워집니다. 필요 최소한의 식별자를 명시적으로 내보내는 방식을 고려해 보세요.

-export * from "./member.api";
-export * from "./member.queries";
+export {
+  checkNicknameDuplication,
+  checkPhoneNumberDuplication,
+  updateMemberInfo,
+} from "./member.api";
+
+export {
+  useCheckNicknameDuplicateQuery,
+  useMemberInfoMutation,
+} from "./member.queries";
src/constants/queryKeys.constants.ts (1)

1-3: 타입 안전성을 위해 as const 추가를 권장합니다.

React Query의 키는 리터럴 튜플이므로 객체 전체를 as const로 고정하면 추후 오타를 방지할 수 있습니다.

 export const QUERY_KEYS = {
   member: ["member"],
-};
+} as const;
src/app/layout.css.ts (1)

13-14: dvh 지원이 제한된 브라우저용 Fallback 추가 제안

일부 iOS/안드로이드 구버전은 dvh 단위를 인식하지 못합니다. 배열 형태로 vhdvh 순서의 폴백을 주면 호환성을 높일 수 있습니다.

-  minHeight: "100dvh",
+  minHeight: ["100vh", "100dvh"],
src/app/member/onboarding/layout.tsx (1)

17-19: 접근성을 위한 키보드 지원 추가를 고려해보세요.

뒤로 가기 기능이 잘 구현되었지만, 키보드 접근성을 위해 onKeyDown 이벤트 처리를 추가하는 것을 권장합니다.

  const handleClick = () => {
    router.back();
  };
+ const handleKeyDown = (event: React.KeyboardEvent) => {
+   if (event.key === 'Enter' || event.key === ' ') {
+     router.back();
+   }
+ };

그리고 아이콘에 키보드 접근성을 추가:

- <LeftArrowIcon className={styles.icon} onClick={handleClick} />
+ <LeftArrowIcon 
+   className={styles.icon} 
+   onClick={handleClick}
+   onKeyDown={handleKeyDown}
+   tabIndex={0}
+   role="button"
+   aria-label="뒤로 가기"
+ />
src/app/(auth)/login/callback/page.tsx (1)

47-47: 의존성 배열에서 사용하지 않는 next 변수를 제거해보세요.

useEffect의 의존성 배열에 next 변수가 포함되어 있지만 실제로는 사용되지 않습니다.

- }, [code, login, router, next]);
+ }, [code, login, router, queryClient]);

queryClient를 추가하고 next를 제거하여 의존성 배열을 정확하게 유지하세요.

src/components/ui/CheckBox/CheckBox.tsx (2)

54-54: className 병합 시 불필요한 공백을 방지해주세요.

현재 구현에서는 classNameundefined일 때 불필요한 공백이 추가될 수 있습니다.

-    <label className={`${variantClass} ${className ?? ""}`}>
+    <label className={[variantClass, className].filter(Boolean).join(" ")}>

또는 clsx 라이브러리 사용을 고려해보세요.


44-65: 접근성 개선을 위한 aria 속성 추가를 고려해주세요.

체크박스 접근성 향상을 위해 aria-describedbyaria-label 속성 추가를 고려해보세요.

export const CheckBox = ({
  checked,
  hasBackground = true,
  className,
  onCheckedChange,
+ "aria-label": ariaLabel,
+ "aria-describedby": ariaDescribedBy,
  ...props
}: CheckBoxProps) => {
  const variantClass = styles.checkboxWrapper({ hasBackground, checked });

  return (
    <label className={[variantClass, className].filter(Boolean).join(" ")}>
      <input
        type='checkbox'
        className={styles.hiddenCheckbox}
        checked={checked}
        onChange={e => onCheckedChange(e.target.checked)}
+       aria-label={ariaLabel}
+       aria-describedby={ariaDescribedBy}
        {...props}
      />
      <CheckIcon className={styles.icon} />
    </label>
  );
};
src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (1)

41-61: 전화번호 검증 로직을 개선해주세요.

현재 로직은 잘 구현되어 있지만, 몇 가지 개선점이 있습니다:

  1. 하이픈 제거 로직이 중복됩니다 (phoneNumberUtils에서 이미 처리할 수 있음)
  2. 에러 처리에서 콘솔 로그만 사용하고 있습니다
  const onSubmit = () => {
-   const removeHyphenPhoneNumber = phoneNumberValue.replace(/-/g, "");
+   const sanitizedPhoneNumber = phoneNumberValue.replace(/-/g, "");
    checkPhoneNumber(
-     { phoneNumber: removeHyphenPhoneNumber },
+     { phoneNumber: sanitizedPhoneNumber },
      {
        onSuccess: isAvailable => {
          if (isAvailable) {
-           onNext(removeHyphenPhoneNumber);
+           onNext(sanitizedPhoneNumber);
          } else {
            setError("phoneNumber", {
              type: "manual",
              message: "이미 사용 중인 번호에요",
            });
          }
        },
        onError: error => {
-         console.error("휴대폰 번호 중복 확인 실패:", error);
+         console.error("휴대폰 번호 중복 확인 실패:", error);
+         setError("phoneNumber", {
+           type: "manual",
+           message: "번호 확인 중 오류가 발생했습니다. 다시 시도해주세요.",
+         });
        },
      }
    );
  };
src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (1)

43-92: useEffect 훅의 복잡한 로직을 커스텀 훅으로 분리하는 것을 고려해보세요.

닉네임 검증 로직이 복잡하고 많은 의존성을 가지고 있어 가독성과 재사용성을 위해 커스텀 훅으로 분리하는 것이 좋겠습니다.

예시 커스텀 훅:

const useNicknameValidation = (nickname: string, initialNickname?: string) => {
  // 현재 useEffect 로직을 여기로 이동
  // 디바운싱, API 호출, 상태 관리 등을 캡슐화
  return {
    isValidating: isNicknameValidating,
    validateNickname: checkNickname,
    // 기타 필요한 값들
  };
};
src/app/member/_types/member.types.ts (2)

9-10: 중복된 타입 정의를 최적화해보세요.

MemberResponse가 단순히 Member의 별칭이라면, 직접 Member 타입을 사용하거나 더 구체적인 목적을 가진 타입으로 정의하는 것이 좋겠습니다.

// 만약 API 응답 구조가 다르다면
export type MemberResponse = {
  data: Member;
  message: string;
};

// 또는 단순히 Member 타입을 직접 사용
// export type MemberResponse = Member; // 이 줄은 제거 고려

17-21: 관련 타입들을 그룹화하여 가독성을 높여보세요.

닉네임과 휴대폰 번호 관련 타입들을 각각 그룹화하면 더 읽기 쉬운 코드가 됩니다.

// 닉네임 관련 타입들
export type NicknameFormValues = Pick<Member, "nickname">;
export type NicknameCheckRequest = Pick<Member, "nickname">;

// 휴대폰 번호 관련 타입들
export type PhoneNumberFormValues = Pick<Member, "phoneNumber">;
export type PhoneNumberCheckRequest = Pick<Member, "phoneNumber">;
src/app/member/_api/member.api.ts (1)

44-57: 에러 메시지를 상수로 관리하는 것을 고려해보세요.

하드코딩된 에러 메시지들을 상수로 관리하면 유지보수성이 향상됩니다.

const ERROR_MESSAGES = {
  NICKNAME_CHECK_FAILED: "닉네임 중복 체크 실패",
  PHONE_NUMBER_CHECK_FAILED: "휴대폰 번호 중복 체크 실패",
} as const;

// 사용 예시
throw new Error(ERROR_MESSAGES.NICKNAME_CHECK_FAILED);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9329d46 and ba015cf.

📒 Files selected for processing (39)
  • src/app/(auth)/login/callback/page.tsx (2 hunks)
  • src/app/layout.css.ts (1 hunks)
  • src/app/member/_api/index.ts (1 hunks)
  • src/app/member/_api/member.api.ts (1 hunks)
  • src/app/member/_api/member.queries.ts (1 hunks)
  • src/app/member/_schemas/index.ts (1 hunks)
  • src/app/member/_schemas/member.schema.ts (1 hunks)
  • src/app/member/_types/index.ts (1 hunks)
  • src/app/member/_types/member.types.ts (1 hunks)
  • src/app/member/_utils/index.ts (1 hunks)
  • src/app/member/_utils/phoneNumberUtils.ts (1 hunks)
  • src/app/member/onboarding/_components/AgreeStep/AgreeStep.css.ts (1 hunks)
  • src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx (1 hunks)
  • src/app/member/onboarding/_components/AgreeStep/index.ts (1 hunks)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts (1 hunks)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (1 hunks)
  • src/app/member/onboarding/_components/NicknameStep/index.ts (1 hunks)
  • src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx (1 hunks)
  • src/app/member/onboarding/_components/OnboardingFunnel/index.ts (1 hunks)
  • src/app/member/onboarding/_components/OnboardingTitle/OnboardingTitle.tsx (1 hunks)
  • src/app/member/onboarding/_components/OnboardingTitle/index.ts (1 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.css.ts (1 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (1 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/index.ts (1 hunks)
  • src/app/member/onboarding/_constants/agreement.constants.ts (1 hunks)
  • src/app/member/onboarding/_types/index.ts (1 hunks)
  • src/app/member/onboarding/_types/onboarding.types.ts (1 hunks)
  • src/app/member/onboarding/layout.css.ts (1 hunks)
  • src/app/member/onboarding/layout.tsx (1 hunks)
  • src/app/member/onboarding/page.tsx (1 hunks)
  • src/components/ui/CheckBox/CheckBox.css.ts (1 hunks)
  • src/components/ui/CheckBox/CheckBox.stories.tsx (1 hunks)
  • src/components/ui/CheckBox/CheckBox.tsx (1 hunks)
  • src/components/ui/CheckBox/index.ts (1 hunks)
  • src/constants/index.ts (1 hunks)
  • src/constants/queryKeys.constants.ts (1 hunks)
  • src/lib/api/client.ts (2 hunks)
  • src/lib/api/type.ts (1 hunks)
  • src/styles/reset.css.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
{src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
**/*.css.ts

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/styles/*

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
{src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
{src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_schemas/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/lib/api/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_types/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
{src/constants/*.{ts,tsx},src/app/**/_constants/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_constants/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/constants/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_api/*.{api,queries}.ts

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
🧠 Learnings (21)
src/app/member/_utils/index.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}} : 유틸리티 및 API 파일은 camelCase로 네이밍해야 한다 (예: `domainUtils.ts`, `domain.api.ts`).
src/app/member/_api/index.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}} : 유틸리티 및 API 파일은 camelCase로 네이밍해야 한다 (예: `domainUtils.ts`, `domain.api.ts`).
src/styles/reset.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/styles/* : 전역 스타일 파일은 `styles/` 폴더에 위치해야 한다.
src/app/member/_schemas/index.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_schemas/*.{ts,tsx} : Zod 스키마는 `_schemas` 폴더에 정의해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}} : 타입 및 스키마 파일은 camelCase로 네이밍해야 한다 (예: `domain.types.ts`, `form.schema.ts`).
src/app/member/_types/index.ts (5)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/types/*.{ts,tsx} : 전역 타입 정의는 `types/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
src/lib/api/client.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
src/app/member/onboarding/_types/index.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/types/*.{ts,tsx} : 전역 타입 정의는 `types/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/onboarding/_components/OnboardingTitle/OnboardingTitle.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/app/member/onboarding/_constants/agreement.constants.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_constants/*.{ts,tsx} : 도메인별 상수 정의는 `_constants` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/constants/*.{ts,tsx},src/app/**/_constants/*.{ts,tsx}} : 상수 파일은 camelCase로 네이밍해야 한다 (예: `domain.constants.ts`).
src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/_utils/phoneNumberUtils.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}} : 유틸리티 및 API 파일은 camelCase로 네이밍해야 한다 (예: `domainUtils.ts`, `domain.api.ts`).
src/app/member/onboarding/_components/AgreeStep/AgreeStep.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/_schemas/member.schema.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_schemas/*.{ts,tsx} : Zod 스키마는 `_schemas` 폴더에 정의해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}} : 타입 및 스키마 파일은 camelCase로 네이밍해야 한다 (예: `domain.types.ts`, `form.schema.ts`).
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/validations/*.{ts,tsx} : 전역 검증 로직(스키마 및 함수)은 `lib/validations/` 폴더에 위치해야 한다.
src/app/member/onboarding/layout.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/components/ui/CheckBox/CheckBox.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/app/member/_api/member.queries.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/hooks/*.{ts,tsx},src/app/**/_hooks/*.{ts,tsx},src/app/**/feature/**/_hooks/*.{ts,tsx}} : 훅 파일은 camelCase로, `use` 접두사를 붙여 네이밍해야 한다 (예: `useLocalStorage.ts`, `useDomainData.ts`).
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
src/components/ui/CheckBox/CheckBox.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/constants/index.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_constants/*.{ts,tsx} : 도메인별 상수 정의는 `_constants` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/constants/*.{ts,tsx},src/app/**/_constants/*.{ts,tsx}} : 상수 파일은 camelCase로 네이밍해야 한다 (예: `domain.constants.ts`).
src/app/member/_api/member.api.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
src/app/member/_types/member.types.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/types/*.{ts,tsx} : 전역 타입 정의는 `types/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}} : 타입 및 스키마 파일은 camelCase로 네이밍해야 한다 (예: `domain.types.ts`, `form.schema.ts`).
🧬 Code Graph Analysis (4)
src/app/member/onboarding/_components/OnboardingTitle/OnboardingTitle.tsx (1)
src/app/member/onboarding/_components/OnboardingTitle/index.ts (1)
  • OnboardingTitle (1-1)
src/app/member/_utils/phoneNumberUtils.ts (1)
src/app/member/_utils/index.ts (1)
  • phoneNumberUtils (1-1)
src/app/member/_api/member.queries.ts (3)
src/app/member/_types/member.types.ts (3)
  • UpdateMemberRequest (11-14)
  • NicknameCheckRequest (18-18)
  • PhoneNumberCheckRequest (21-21)
src/app/member/_api/member.api.ts (4)
  • putMember (27-35)
  • getNicknameCheck (44-57)
  • getPhoneNumberCheck (66-80)
  • getMember (17-19)
src/constants/queryKeys.constants.ts (1)
  • QUERY_KEYS (1-3)
src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (2)
src/app/member/onboarding/_components/NicknameStep/index.ts (1)
  • NicknameStep (1-1)
src/app/member/_api/member.queries.ts (1)
  • useNicknameCheckMutation (23-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: storybook-preview
  • GitHub Check: test
🔇 Additional comments (42)
src/styles/reset.css.ts (1)

43-43: 리스트 스타일 리셋 개선이 적절합니다.

li 요소를 선택자에 추가하여 모든 리스트 관련 요소에 일관된 스타일 리셋을 적용하는 것은 좋은 개선사항입니다.

src/components/ui/CheckBox/index.ts (1)

1-1: 컴포넌트 익스포트가 올바르게 구현되었습니다.

CheckBox 컴포넌트의 인덱스 익스포트가 표준 패턴을 따르고 있습니다.

src/app/member/onboarding/_components/PhoneNumberStep/index.ts (1)

1-1: PhoneNumberStep 컴포넌트 익스포트가 올바르게 구현되었습니다.

온보딩 컴포넌트의 인덱스 익스포트가 표준 패턴을 따르고 있습니다.

src/app/member/onboarding/_components/NicknameStep/index.ts (1)

1-1: NicknameStep 컴포넌트 익스포트가 올바르게 구현되었습니다.

온보딩 컴포넌트의 인덱스 익스포트가 표준 패턴을 따르고 있습니다.

src/app/member/_utils/index.ts (1)

1-1: 모듈 리익스포트 구성 적절

phoneNumberUtils를 barrel 파일로 노출하여 import 경로를 단축한 점이 👍🏼입니다. 네이밍도 camelCase 규칙을 준수합니다.

src/app/member/onboarding/_components/OnboardingTitle/index.ts (1)

1-1: Barrel 패턴 적용 확인

OnboardingTitle를 index 레벨에서 재노출하여 컴포넌트 트리의 import 가독성이 향상되었습니다. 추가 조치 필요 없습니다.

src/constants/index.ts (1)

1-2: 중복 export 충돌 가능성 확인 필요

queryKeys.constants.tstime.constants.ts 간에 동일한 식별자명이 존재할 경우 빌드 타임에 중복 export 오류가 발생합니다. 두 파일에 같은 이름의 상수가 없는지 한 번 더 확인 부탁드립니다.

src/app/member/_schemas/index.ts (1)

1-1: 스키마 배치 및 네이밍 규칙 준수

nicknameSchema, phoneNumberSchema 모두 _schemas 폴더에 위치하며 파일명도 camelCase로 맞춰져 있습니다. 👍🏼

src/app/member/onboarding/_components/AgreeStep/index.ts (1)

1-1: 모듈 re-export 구조 적절

별도 이슈 없이 컴포넌트를 깔끔하게 재-export합니다.

src/app/member/onboarding/_components/OnboardingFunnel/index.ts (1)

1-1: 모듈 re-export 구조 적절

추가적인 문제 없습니다.

src/app/member/onboarding/page.tsx (1)

1-5: 깔끔한 페이지 컴포넌트 구현입니다.

Next.js App Router의 페이지 컴포넌트 구조를 잘 따르고 있고, 단일 책임 원칙에 맞게 OnboardingFunnel 컴포넌트를 렌더링하는 역할만 담당하고 있습니다.

src/lib/api/client.ts (2)

34-37: 에러 메시지 필드 사용 여부 확인 필요

서버 응답을 message > errorMessage > 기본 메시지 순으로 처리하는 로직 자체는 적절하지만, 실제로 서버에서 어떤 키를 반환하는지 명확히 확인해야 합니다.

  • 백엔드 API 스펙 또는 문서에서 message vs errorMessage 중 어떤 필드를 사용하는지 검증해주세요.
  • 프로젝트 내 ApiError 타입(또는 인터페이스) 정의를 확인해 두 필드 중 하나만 사용된다면 불필요한 분기를 제거하는 방안도 검토해보시기 바랍니다.

75-75: Authorization 헤더 Bearer 접두사 제거 확인 필요

  • 변경 위치: src/lib/api/client.ts
    - request.headers.set("Authorization", `Bearer ${session.accessToken}`);
    + request.headers.set("Authorization", `${session.accessToken}`);
  • 코드베이스 내 다른 Bearer 접두사 사용 사례는 발견되지 않음

서버 API가 토큰만 받도록 스펙이 확정된 것이 맞는지 API 문서 또는 백엔드 담당자에게 최종 확인 부탁드립니다.

src/app/member/onboarding/_types/index.ts (1)

1-6: 타입 구조화가 잘 되어 있습니다.

도메인별 타입 정의를 _types 폴더에 배치하고 인덱스 파일을 통해 re-export하는 구조가 코딩 가이드라인을 잘 준수하고 있습니다.

src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.css.ts (1)

1-11: 스타일 파일 구조가 가이드라인을 잘 준수하고 있습니다.

vanilla-extract를 사용한 스타일 파일이 camelCase 네이밍, .css.ts 확장자, 컴포넌트와 같은 폴더 배치 등 모든 코딩 가이드라인을 준수하고 있습니다. 색상 시스템도 올바르게 사용하고 있습니다.

src/lib/api/type.ts (1)

15-20: 에러 타입 정의가 클라이언트 변경사항과 일치합니다.

ApiError 타입에 message 속성을 추가하고 errorMessage를 optional로 변경한 것이 client.ts의 에러 처리 로직과 일치합니다. JSDoc 주석도 적절히 추가되어 타입 정의가 명확합니다.

src/app/member/onboarding/_components/OnboardingTitle/OnboardingTitle.tsx (1)

1-15: 컴포넌트 구현이 깔끔하고 적절합니다.

OnboardingTitle 컴포넌트가 잘 구현되어 있습니다. TypeScript 타입 정의가 정확하고, 컴포넌트명이 PascalCase 규칙을 따르며, UI 라이브러리를 통한 일관된 스타일링을 사용하고 있습니다.

src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts (1)

1-11: CSS 파일이 코딩 가이드라인을 잘 준수하고 있습니다.

vanilla-extract 스타일 파일이 올바르게 작성되었습니다. camelCase 네이밍 규칙을 따르고, .css.ts 확장자를 사용하며, 해당 컴포넌트와 같은 폴더에 배치되어 있습니다.

src/app/member/onboarding/layout.tsx (1)

1-31: 레이아웃 구조가 잘 설계되었습니다.

온보딩 레이아웃이 깔끔하게 구현되었습니다. GNB와 메인 콘텐츠 영역을 적절히 분리하고, Next.js의 useRouter를 올바르게 활용하고 있습니다.

src/app/(auth)/login/callback/page.tsx (2)

3-3: React Query 통합이 잘 구현되었습니다.

useQueryClient와 QUERY_KEYS 상수를 추가하여 캐시 관리가 개선되었습니다. 중앙화된 쿼리 키 관리와 로그인 후 캐시 업데이트가 적절히 처리되었습니다.

Also applies to: 8-8, 12-12, 28-28


30-34: 온보딩 플로우 라우팅 로직이 명확합니다.

신규 가입자와 기존 사용자를 구분하여 적절한 페이지로 리다이렉트하는 로직이 잘 구현되었습니다. isSignUp 플래그를 활용한 조건부 라우팅이 직관적입니다.

src/app/member/onboarding/layout.css.ts (1)

1-20: 반응형 레이아웃 스타일이 잘 구현되었습니다.

vanilla-extract CSS 파일이 코딩 가이드라인을 준수하고 있으며, 모바일 우선 반응형 디자인이 적절히 적용되었습니다. camelCase 네이밍과 flexbox를 활용한 레이아웃 구조가 깔끔합니다.

src/app/member/onboarding/_constants/agreement.constants.ts (1)

1-19: 상수 정의 구조가 깔끔하게 구현되었습니다.

도메인별 상수가 _constants 폴더에 올바르게 위치하고 있으며, camelCase 네이밍 규칙을 준수하고 있습니다. 타입 안전성과 필수/선택 구분이 명확합니다.

src/app/member/onboarding/_components/AgreeStep/AgreeStep.css.ts (1)

1-30: vanilla-extract 스타일이 가이드라인에 맞게 구현되었습니다.

.css.ts 확장자와 camelCase 네이밍을 사용하고 있으며, 컴포넌트와 같은 폴더에 위치하여 가이드라인을 준수하고 있습니다. 디자인 토큰을 활용한 일관된 스타일링이 좋습니다.

src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx (1)

34-59: 퍼널 구조가 깔끔하게 구현되었습니다.

각 스텝 간 컨텍스트 전달과 히스토리 관리가 체계적으로 구현되어 있으며, 타입 안전성도 잘 보장되어 있습니다.

src/app/member/_api/member.queries.ts (2)

36-43: 쿼리 옵션 설정이 적절합니다.

useMemberQuerystaleTimegcTime 설정이 멤버 데이터 특성에 맞게 적절히 구성되어 있습니다.


23-34: validation 관련 mutation 훅들이 일관되게 구현되었습니다.

닉네임과 전화번호 검증을 위한 mutation 훅들이 타입 안전성과 일관성을 유지하며 구현되어 있습니다.

src/app/member/_schemas/member.schema.ts (1)

3-9: 닉네임 스키마 검증 로직이 올바르게 구현되었습니다.

길이 제한과 정규식 패턴이 적절하게 설정되어 있고, 한국어 에러 메시지도 사용자 친화적입니다.

src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (3)

25-38: 폼 초기화 설정이 적절합니다.

React Hook Form 설정이 올바르게 되어 있고, 기본값으로 포맷팅된 전화번호를 사용하는 것이 좋습니다.


82-87: 입력 필드 onChange 핸들러가 잘 구현되었습니다.

실시간 포맷팅이 적절하게 적용되어 있고, setValue를 통한 상태 업데이트도 올바릅니다.


110-116: 제출 버튼 비활성화 조건이 적절합니다.

검증 에러, 빈 값, 제출 중 상태를 모두 고려한 조건이 올바르게 설정되어 있습니다.

src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx (4)

27-31: 폼 초기화가 적절합니다.

빈 배열로 초기화하여 사용자가 명시적으로 동의를 선택하도록 하는 것이 좋습니다.


35-38: 동의 상태 계산 로직이 올바릅니다.

전체 동의 및 필수 동의 완료 여부를 정확하게 계산하고 있습니다.


40-49: 동의 처리 함수들이 잘 구현되었습니다.

전체 동의와 개별 동의 처리 로직이 명확하고 올바르게 구현되어 있습니다.


104-104: CheckBox 컴포넌트에 register props 전달 방식 확인 필요

  • 파일: src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx 104행
    {...register("agreements")}로 전달된 name, onChange, onBlur, ref 등은 실제 CheckBox 내부의 <input>에 정상 포워딩되는지 확인해주세요.
  • 현재 handleIndividualAgree로 별도 onChange 로직을 관리하고 있어, react-hook-form의 register와 이벤트 핸들링이 중복되거나 충돌될 가능성이 있습니다.

필요하다면 register 사용을 제거하거나, Controller API로 전환해 단일 소스로 상태 관리를 통일하는 방안을 고려해주세요.

src/components/ui/CheckBox/CheckBox.css.ts (3)

6-62: CheckBox 스타일링이 체계적으로 구현되었습니다.

vanilla-extract의 recipe 패턴을 활용하여 variant 기반 스타일링이 잘 구현되어 있습니다. compound variants를 통해 다양한 상태 조합을 효율적으로 처리하고 있습니다.


64-70: 접근성을 위한 숨김 체크박스 스타일이 적절합니다.

스크린 리더 접근성을 위해 네이티브 체크박스를 숨기는 방식이 올바르게 구현되어 있습니다.


72-79: 아이콘 스타일링이 적절합니다.

아이콘 크기와 전환 효과가 잘 설정되어 있습니다.

src/app/member/onboarding/_types/onboarding.types.ts (2)

1-17: 온보딩 단계별 타입 정의가 논리적으로 구성되었습니다.

각 단계별로 필요한 필드를 점진적으로 추가하는 구조가 온보딩 플로우를 잘 반영하고 있습니다. 타입 안전성을 보장하면서도 유연성을 제공합니다.


19-23: 동의 상수 타입이 적절하게 정의되었습니다.

필수 동의 여부를 boolean으로 구분하여 비즈니스 로직에서 활용하기 좋은 구조입니다.

src/components/ui/CheckBox/CheckBox.stories.tsx (1)

1-83: 전체적으로 잘 구성된 Storybook 스토리 파일입니다.

CheckBox 컴포넌트의 문서화와 테스트를 위한 스토리가 적절히 구성되어 있습니다. InteractiveCheckBox를 통한 상태 관리 로직도 올바르게 구현되었습니다.

src/app/member/_api/member.api.ts (1)

11-35: API 함수들의 JSDoc 주석이 잘 작성되어 있습니다.

명확한 설명과 매개변수, 반환 타입 정보가 포함되어 있어 코드의 가독성과 유지보수성이 높습니다.

Comment thread src/app/member/_types/index.ts Outdated
Comment on lines +25 to +32
initial: {
step: "nicknameStep",
context: {
nickname: member?.nickname ?? "",
phoneNumber: member?.phoneNumber ?? "",
},
},
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

초기 컨텍스트 설정 시 데이터 안전성을 검토해주세요.

useMemberQuery에서 반환된 member 데이터가 없을 때의 초기값 처리가 적절한지 확인이 필요합니다. 특히 온보딩 플로우에서 사용자 데이터가 없는 경우가 예상되는 시나리오인지 검토해주세요.


🏁 Script executed:

#!/bin/bash
# 온보딩 플로우에서 member 데이터가 없는 경우를 처리하는 로직 확인
ast-grep --pattern $'useMemberQuery($_)'

Length of output: 41


코드베이스에서 OnboardingFunnel.tsx 파일을 찾아 useMemberQuery 사용 여부를 확인합니다.


🏁 Script executed:

#!/bin/bash
# OnboardingFunnel.tsx 파일 위치 확인 및 상위 200줄 출력
fd OnboardingFunnel.tsx -x sed -n '1,200p' {}

Length of output: 1505


OnboardingFunnel: member 로딩 상태 반영 및 초기 컨텍스트 수정 필요

현재 useFunnelinitial.contextmember 데이터 로딩 전인 시점에 빈 문자열로 고정되어, 이후 실제 API 응답 값이 반영되지 않습니다. 기존 회원의 닉네임·전화번호가 폼에 표시되지 않을 수 있어 아래 개선을 적용해주세요.

  • useMemberQueryisLoading/error 상태 처리
  • 데이터 로딩 완료 후에만 useFunnel을 초기화하거나, initial.context에 실제 member 값을 사용

예시 수정안:

- const { data: member } = useMemberQuery();
+ const { data: member, isLoading, error } = useMemberQuery();

+ if (isLoading) return <LoadingSpinner />;
+ if (error)     return <ErrorMessage message="회원 정보를 불러오는 중 오류가 발생했습니다." />;

  const funnel = useFunnel<{
    nicknameStep: NicknameFunnel;
    phoneNumberStep: PhoneNumberFunnel;
    agreeStep: AgreeFunnel;
  }>({
    id: "onboarding-funnel",
    initial: {
      step: "nicknameStep",
      context: {
-       nickname: member?.nickname ?? "",
-       phoneNumber: member?.phoneNumber ?? "",
+       nickname: member.nickname,
+       phoneNumber: member.phoneNumber,
      },
    },
  });
  • 파일: src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx
  • 위치: useMemberQuery 호출부 및 useFunnel 초기화 구간
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx
around lines 25 to 32, the initial.context for useFunnel is set before the
member data finishes loading, causing the form to show empty strings instead of
actual member info. To fix this, handle the isLoading and error states from
useMemberQuery, and initialize useFunnel only after the member data has loaded
successfully, using the real member.nickname and member.phoneNumber values in
initial.context.

Comment thread src/app/member/_api/member.queries.ts
Comment on lines +11 to +18
export const phoneNumberSchema = z.object({
phoneNumber: z
.string()
.regex(
/^(010|011|016|017|018|019)[-]?\d{3,4}[-]?\d{4}$/,
"유효하지 않은 휴대폰 번호 형식입니다."
),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

전화번호 정규식 패턴을 검증해주세요.

현재 정규식 패턴 /^(010|011|016|017|018|019)[-]?\d{3,4}[-]?\d{4}$/에서 011, 016, 017, 018, 019는 더 이상 사용되지 않는 번호입니다. 현재 한국에서는 주로 010만 사용됩니다.

또한 현재 패턴에서 \d{3,4}는 3자리 또는 4자리를 허용하는데, 한국 휴대폰 번호는 일반적으로 010-XXXX-XXXX 형식으로 4자리가 표준입니다.


🏁 Script executed:

#!/bin/bash
# 한국 휴대폰 번호 체계 확인
echo "한국 휴대폰 번호 체계 확인"
echo "현재 사용 중인 번호: 010"
echo "기존 번호 (현재 거의 사용 안함): 011, 016, 017, 018, 019"

Length of output: 182


전화번호 정규식 패턴 업데이트 필요
한국 휴대폰 번호는 현재 010만 사용되며, 중간 번호도 4자리로 고정되어 있어야 합니다. 아래와 같이 패턴을 수정해주세요.

  • 대상 파일: src/app/member/_schemas/member.schema.ts (11–18행)
 export const phoneNumberSchema = z.object({
   phoneNumber: z
     .string()
-    .regex(
-      /^(010|011|016|017|018|019)[-]?\d{3,4}[-]?\d{4}$/,
-      "유효하지 않은 휴대폰 번호 형식입니다."
-    ),
+    .regex(
+      /^010-?\d{4}-?\d{4}$/,
+      "유효하지 않은 휴대폰 번호 형식입니다."
+    ),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const phoneNumberSchema = z.object({
phoneNumber: z
.string()
.regex(
/^(010|011|016|017|018|019)[-]?\d{3,4}[-]?\d{4}$/,
"유효하지 않은 휴대폰 번호 형식입니다."
),
});
export const phoneNumberSchema = z.object({
phoneNumber: z
.string()
.regex(
/^010-?\d{4}-?\d{4}$/,
"유효하지 않은 휴대폰 번호 형식입니다."
),
});
🤖 Prompt for AI Agents
In src/app/member/_schemas/member.schema.ts between lines 11 and 18, update the
phone number regex pattern to reflect that only '010' is valid as the prefix and
the middle segment must be exactly 4 digits. Modify the regex to enforce the
prefix '010' and a fixed 4-digit middle number segment, ensuring the pattern
matches the current Korean mobile phone number format.

Comment on lines +51 to +67
const onSubmit = () => {
updateMember(
{
nickname: nickname,
phoneNumber: phoneNumber,
optInMarketing: checkedIds.includes("marketing"),
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.member,
});
router.push("/");
},
}
);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

API 호출 및 성공 처리 로직을 개선해주세요.

전반적으로 잘 구현되어 있지만, 에러 처리가 누락되어 있습니다.

  const onSubmit = () => {
    updateMember(
      {
        nickname: nickname,
        phoneNumber: phoneNumber,
        optInMarketing: checkedIds.includes("marketing"),
      },
      {
        onSuccess: () => {
          queryClient.invalidateQueries({
            queryKey: QUERY_KEYS.member,
          });
          router.push("/");
        },
+       onError: (error) => {
+         console.error("회원 정보 업데이트 실패:", error);
+         // 사용자에게 에러 메시지를 표시하는 로직 추가 필요
+       },
      }
    );
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onSubmit = () => {
updateMember(
{
nickname: nickname,
phoneNumber: phoneNumber,
optInMarketing: checkedIds.includes("marketing"),
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.member,
});
router.push("/");
},
}
);
};
const onSubmit = () => {
updateMember(
{
nickname: nickname,
phoneNumber: phoneNumber,
optInMarketing: checkedIds.includes("marketing"),
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.member,
});
router.push("/");
},
onError: (error) => {
console.error("회원 정보 업데이트 실패:", error);
// 사용자에게 에러 메시지를 표시하는 로직 추가 필요
},
}
);
};
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx between lines
51 and 67, the onSubmit function calls updateMember and handles success but
lacks error handling. Add an onError callback to the updateMember call to
properly handle API call failures, such as displaying an error message or
logging the error, ensuring robust error management alongside the existing
success logic.

Comment on lines +94 to +99
const onSubmit = () => {
if (!errors.nickname) {
onNext(nicknameValue);
return;
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

onSubmit 함수의 조건 검사를 개선해보세요.

현재 로직에서 !errors.nickname 조건만 체크하고 있는데, 실제로는 더 많은 조건들이 버튼 비활성화 로직에 포함되어 있습니다. 일관성을 위해 같은 조건들을 사용하는 것이 좋겠습니다.

const onSubmit = () => {
  const canSubmit = !errors.nickname && 
                   nicknameValue && 
                   !isSubmitting && 
                   !isNicknameValidating;
  
  if (canSubmit) {
    onNext(nicknameValue);
  }
};
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx around
lines 94 to 99, the onSubmit function only checks if there is no nickname error
before proceeding, but the button disabling logic uses additional conditions.
Update the onSubmit function to check all relevant conditions consistently:
ensure there is no nickname error, the nicknameValue exists, and both
isSubmitting and isNicknameValidating are false before calling onNext with
nicknameValue.

Comment thread src/app/member/_api/member.api.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (12)
src/constants/queryKeys.constants.ts (1)

1-3: React Query 키 관리를 위한 좋은 접근 방식입니다.

전역 상수로 쿼리 키를 관리하여 일관성을 확보한 점이 좋습니다. 파일 위치와 네이밍 규칙도 코딩 가이드라인에 맞습니다.

향후 다른 도메인의 쿼리 키가 추가될 때를 고려하여 확장 가능한 구조로 설계되었는지 확인해보세요.

src/app/(auth)/login/callback/page.tsx (1)

47-47: 사용하지 않는 파라미터를 제거하세요.

next 파라미터가 선언되었지만 사용되지 않고 있습니다. 현재 로직에서 필요하지 않다면 제거하는 것이 좋습니다.

-  }, [code, login, router, next]);
+  }, [code, login, router]);

만약 향후 사용 예정이라면 ESLint 규칙을 무시하는 주석을 추가할 수 있습니다:

-  }, [code, login, router, next]);
+  }, [code, login, router, next]); // eslint-disable-line react-hooks/exhaustive-deps -- next will be used in future
src/app/member/onboarding/layout.css.ts (1)

15-20: 하드코딩된 값을 디자인 시스템 변수로 대체하는 것을 고려하세요.

padding: "2rem" 같은 하드코딩된 값들을 디자인 시스템의 spacing 변수로 대체하면 일관성과 유지보수성이 향상됩니다.

+import { spacing } from "@/styles";

 export const mainWrapper = style({
   display: "flex",
   flexDirection: "column",
   flex: 1,
-  padding: "2rem",
+  padding: spacing.xl, // 또는 적절한 spacing 변수
 });

만약 @/styles에서 spacing 변수를 제공하지 않는다면, 디자인 시스템에 추가하는 것을 고려해보세요.

src/components/ui/CheckBox/CheckBox.tsx (1)

44-65: 접근성 개선을 위한 ref 전달 및 추가 속성 고려가 필요합니다.

현재 구현은 기본적인 기능을 잘 제공하지만, 접근성과 폼 라이브러리 호환성을 위해 개선할 수 있는 부분이 있습니다.

다음 개선사항을 고려해보세요:

+import { forwardRef } from "react";
+
-export const CheckBox = ({
+export const CheckBox = forwardRef<HTMLInputElement, CheckBoxProps>(({
   checked,
   hasBackground = true,
   className,
   onCheckedChange,
   ...props
-}: CheckBoxProps) => {
+}, ref) => {
   const variantClass = styles.checkboxWrapper({ hasBackground, checked });
 
   return (
     <label className={`${variantClass} ${className ?? ""}`}>
       <input
         type='checkbox'
         className={styles.hiddenCheckbox}
         checked={checked}
         onChange={e => onCheckedChange(e.target.checked)}
+        ref={ref}
+        aria-describedby={props['aria-describedby']}
         {...props}
       />
       <CheckIcon className={styles.icon} />
     </label>
   );
-};
+});
+
+CheckBox.displayName = 'CheckBox';
src/app/member/_schemas/member.schema.ts (1)

11-18: 전화번호 스키마의 정규식 패턴을 확인해보세요.

한국 휴대폰 번호 형식을 잘 반영하고 있으나, 현재 사용되지 않는 일부 번호 대역(011, 016, 017, 018, 019)이 포함되어 있습니다.

현재 주로 사용되는 010 번호에 집중하거나, 최신 번호 체계를 반영하는 것을 고려해보세요:

-      /^(010|011|016|017|018|019)[-]?\d{3,4}[-]?\d{4}$/,
+      /^010[-]?\d{3,4}[-]?\d{4}$/,

또는 미래 확장성을 고려하여 더 포괄적인 패턴으로 변경할 수 있습니다:

-      /^(010|011|016|017|018|019)[-]?\d{3,4}[-]?\d{4}$/,
+      /^01[0-9][-]?\d{3,4}[-]?\d{4}$/,
src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx (3)

16-31: Props 타입을 별도로 정의하는 것을 고려해보세요

인라인 타입 정의 대신 별도의 interface나 type을 정의하면 재사용성과 가독성을 높일 수 있습니다.

+interface AgreeStepProps {
+  nickname: string;
+  phoneNumber: string;
+}
+
-export const AgreeStep = ({
-  nickname,
-  phoneNumber,
-}: {
-  nickname: string;
-  phoneNumber: string;
-}) => {
+export const AgreeStep = ({ nickname, phoneNumber }: AgreeStepProps) => {

54-56: nickname과 phoneNumber 전달 시 축약 표현을 사용하세요

객체 속성과 값이 같은 이름이므로 축약 표현을 사용할 수 있습니다.

{
- nickname: nickname,
- phoneNumber: phoneNumber,
+ nickname,
+ phoneNumber,
  optInMarketing: checkedIds.includes("marketing"),
}

97-105: react-hook-form register 사용이 불필요할 수 있습니다

체크박스 상태를 이미 직접 관리하고 있으므로, register 사용이 중복될 수 있습니다. 현재 구현에서는 필요하지 않을 것 같습니다.

<CheckBox
  value={agreement.id}
  checked={checkedIds.includes(agreement.id)}
  onCheckedChange={value =>
    handleIndividualAgree(agreement.id, value)
  }
  hasBackground={false}
- {...register("agreements")}
/>
src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (1)

16-38: Props 타입을 별도로 정의하는 것을 고려해보세요

AgreeStep과 동일한 패턴으로 Props 타입을 별도로 정의하면 일관성과 재사용성을 높일 수 있습니다.

+interface PhoneNumberStepProps {
+  phoneNumber?: string;
+  onNext: (phoneNumber: string) => void;
+}
+
-export const PhoneNumberStep = ({
-  phoneNumber,
-  onNext,
-}: {
-  phoneNumber?: string;
-  onNext: (phoneNumber: string) => void;
-}) => {
+export const PhoneNumberStep = ({ phoneNumber, onNext }: PhoneNumberStepProps) => {
src/components/ui/CheckBox/CheckBox.css.ts (1)

26-29: 빈 스타일 객체를 명시적으로 정의하는 것을 고려해보세요.

checked 상태의 true/false 변형에서 빈 스타일 객체가 사용되고 있습니다. 이는 compoundVariants에서 처리되지만, 명시적인 스타일 정의가 더 명확할 수 있습니다.

    checked: {
-      true: {},
-      false: {},
+      true: {
+        // compoundVariants에서 처리됨
+      },
+      false: {
+        // compoundVariants에서 처리됨
+      },
    },
src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (2)

43-92: debounce 구현이 잘 되어 있지만 일부 개선이 필요합니다.

useEffect에서 debounce 로직이 적절하게 구현되어 있고, 타이머 정리도 올바르게 처리되고 있습니다. 다만 의존성 배열에서 일부 개선이 가능합니다.

-  }, [nicknameValue, checkNickname, nickname, clearErrors, setError]);
+  }, [nicknameValue, checkNickname, nickname, clearErrors, setError, nicknameSchema]);

또한 schema validation을 useEffect 외부로 분리하여 성능을 개선할 수 있습니다.


94-99: submit 핸들러를 단순화할 수 있습니다.

현재 구현도 정확하지만, 더 간결하게 작성할 수 있습니다.

  const onSubmit = () => {
-    if (!errors.nickname) {
-      onNext(nicknameValue);
-      return;
-    }
+    onNext(nicknameValue);
  };

폼 유효성 검사는 이미 버튼의 disabled 상태에서 처리되고 있어 별도 체크가 불필요합니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9329d46 and ba015cf.

📒 Files selected for processing (39)
  • src/app/(auth)/login/callback/page.tsx (2 hunks)
  • src/app/layout.css.ts (1 hunks)
  • src/app/member/_api/index.ts (1 hunks)
  • src/app/member/_api/member.api.ts (1 hunks)
  • src/app/member/_api/member.queries.ts (1 hunks)
  • src/app/member/_schemas/index.ts (1 hunks)
  • src/app/member/_schemas/member.schema.ts (1 hunks)
  • src/app/member/_types/index.ts (1 hunks)
  • src/app/member/_types/member.types.ts (1 hunks)
  • src/app/member/_utils/index.ts (1 hunks)
  • src/app/member/_utils/phoneNumberUtils.ts (1 hunks)
  • src/app/member/onboarding/_components/AgreeStep/AgreeStep.css.ts (1 hunks)
  • src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx (1 hunks)
  • src/app/member/onboarding/_components/AgreeStep/index.ts (1 hunks)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts (1 hunks)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (1 hunks)
  • src/app/member/onboarding/_components/NicknameStep/index.ts (1 hunks)
  • src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx (1 hunks)
  • src/app/member/onboarding/_components/OnboardingFunnel/index.ts (1 hunks)
  • src/app/member/onboarding/_components/OnboardingTitle/OnboardingTitle.tsx (1 hunks)
  • src/app/member/onboarding/_components/OnboardingTitle/index.ts (1 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.css.ts (1 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (1 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/index.ts (1 hunks)
  • src/app/member/onboarding/_constants/agreement.constants.ts (1 hunks)
  • src/app/member/onboarding/_types/index.ts (1 hunks)
  • src/app/member/onboarding/_types/onboarding.types.ts (1 hunks)
  • src/app/member/onboarding/layout.css.ts (1 hunks)
  • src/app/member/onboarding/layout.tsx (1 hunks)
  • src/app/member/onboarding/page.tsx (1 hunks)
  • src/components/ui/CheckBox/CheckBox.css.ts (1 hunks)
  • src/components/ui/CheckBox/CheckBox.stories.tsx (1 hunks)
  • src/components/ui/CheckBox/CheckBox.tsx (1 hunks)
  • src/components/ui/CheckBox/index.ts (1 hunks)
  • src/constants/index.ts (1 hunks)
  • src/constants/queryKeys.constants.ts (1 hunks)
  • src/lib/api/client.ts (2 hunks)
  • src/lib/api/type.ts (1 hunks)
  • src/styles/reset.css.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
{src/constants/*.{ts,tsx},src/app/**/_constants/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/constants/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
**/*.css.ts

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_constants/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
{src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_types/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
{src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
{src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/styles/*

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_schemas/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/lib/api/*.{ts,tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
src/app/**/_api/*.{api,queries}.ts

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
🧠 Learnings (23)
src/constants/index.ts (4)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_constants/*.{ts,tsx} : 도메인별 상수 정의는 `_constants` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/constants/*.{ts,tsx},src/app/**/_constants/*.{ts,tsx}} : 상수 파일은 camelCase로 네이밍해야 한다 (예: `domain.constants.ts`).
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/onboarding/_constants/agreement.constants.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_constants/*.{ts,tsx} : 도메인별 상수 정의는 `_constants` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/constants/*.{ts,tsx},src/app/**/_constants/*.{ts,tsx}} : 상수 파일은 camelCase로 네이밍해야 한다 (예: `domain.constants.ts`).
src/app/member/_types/index.ts (4)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/types/*.{ts,tsx} : 전역 타입 정의는 `types/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
src/app/member/_utils/index.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}} : 유틸리티 및 API 파일은 camelCase로 네이밍해야 한다 (예: `domainUtils.ts`, `domain.api.ts`).
src/app/member/_api/index.ts (4)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}} : 유틸리티 및 API 파일은 camelCase로 네이밍해야 한다 (예: `domainUtils.ts`, `domain.api.ts`).
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
src/styles/reset.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/styles/* : 전역 스타일 파일은 `styles/` 폴더에 위치해야 한다.
src/app/member/_schemas/index.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_schemas/*.{ts,tsx} : Zod 스키마는 `_schemas` 폴더에 정의해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}} : 타입 및 스키마 파일은 camelCase로 네이밍해야 한다 (예: `domain.types.ts`, `form.schema.ts`).
src/lib/api/client.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
src/app/member/onboarding/_types/index.ts (4)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/types/*.{ts,tsx} : 전역 타입 정의는 `types/` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}} : 타입 및 스키마 파일은 camelCase로 네이밍해야 한다 (예: `domain.types.ts`, `form.schema.ts`).
src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/_utils/phoneNumberUtils.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/utils/*.{ts,tsx} : 전역 공통 유틸리티는 `lib/utils/` 폴더에 기능별로 분리해 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/lib/utils/*.{ts,tsx},src/lib/api/*.{ts,tsx},src/app/**/_utils/*.{ts,tsx},src/app/**/_api/*.{ts,tsx}} : 유틸리티 및 API 파일은 camelCase로 네이밍해야 한다 (예: `domainUtils.ts`, `domain.api.ts`).
src/app/member/onboarding/layout.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/constants/queryKeys.constants.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
src/components/ui/CheckBox/CheckBox.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/app/member/onboarding/_types/onboarding.types.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
src/app/member/_schemas/member.schema.ts (3)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_schemas/*.{ts,tsx} : Zod 스키마는 `_schemas` 폴더에 정의해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/types/*.{ts,tsx},src/app/**/_types/*.{ts,tsx},src/app/**/_schemas/*.{ts,tsx},src/lib/validations/*.{ts,tsx}} : 타입 및 스키마 파일은 camelCase로 네이밍해야 한다 (예: `domain.types.ts`, `form.schema.ts`).
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/validations/*.{ts,tsx} : 전역 검증 로직(스키마 및 함수)은 `lib/validations/` 폴더에 위치해야 한다.
src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/app/**/_components/*.{ts,tsx},src/components/**/*.{ts,tsx}} : 컴포넌트 파일은 PascalCase로 네이밍해야 한다 (예: `Button.tsx`, `DomainLayout.tsx`).
src/app/member/_api/member.queries.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to {src/hooks/*.{ts,tsx},src/app/**/_hooks/*.{ts,tsx},src/app/**/feature/**/_hooks/*.{ts,tsx}} : 훅 파일은 camelCase로, `use` 접두사를 붙여 네이밍해야 한다 (예: `useLocalStorage.ts`, `useDomainData.ts`).
src/components/ui/CheckBox/CheckBox.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/_api/member.api.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API 호출 관련 코드는 `_api` 폴더에 `.api.ts` (ky 사용) 및 `.queries.ts` (TanStack Query) 파일로 분리해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/lib/api/*.{ts,tsx} : 전역 API 클라이언트 및 타입은 `lib/api/` 폴더에 위치해야 한다.
src/app/member/_types/member.types.ts (2)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/app/**/_types/*.{ts,tsx} : 도메인별 타입 정의는 `_types` 폴더에 위치해야 한다.
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to src/types/*.{ts,tsx} : 전역 타입 정의는 `types/` 폴더에 위치해야 한다.
src/app/member/onboarding/_components/AgreeStep/AgreeStep.css.ts (1)
Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-06-30T08:25:30.684Z
Learning: Applies to **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.ts` 확장자를 사용해야 하며, 해당 컴포넌트와 같은 폴더에 배치해야 한다.
🧬 Code Graph Analysis (8)
src/app/member/onboarding/page.tsx (1)
src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx (1)
  • OnboardingFunnel (16-60)
src/app/(auth)/login/callback/page.tsx (2)
src/lib/session/clientSession.ts (1)
  • clearClientSessionCache (16-18)
src/constants/queryKeys.constants.ts (1)
  • QUERY_KEYS (1-3)
src/app/member/_utils/phoneNumberUtils.ts (1)
src/app/member/_utils/index.ts (1)
  • phoneNumberUtils (1-1)
src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx (2)
src/app/member/onboarding/_components/OnboardingFunnel/index.ts (1)
  • OnboardingFunnel (1-1)
src/app/member/_api/member.queries.ts (1)
  • useMemberQuery (36-43)
src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx (4)
src/app/member/onboarding/_components/AgreeStep/index.ts (1)
  • AgreeStep (1-1)
src/app/member/_api/member.queries.ts (1)
  • useUpdateMemberMutation (17-21)
src/app/member/onboarding/_constants/agreement.constants.ts (1)
  • AGREEMENTS (3-19)
src/constants/queryKeys.constants.ts (1)
  • QUERY_KEYS (1-3)
src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (2)
src/app/member/onboarding/_components/NicknameStep/index.ts (1)
  • NicknameStep (1-1)
src/app/member/_api/member.queries.ts (1)
  • useNicknameCheckMutation (23-27)
src/components/ui/CheckBox/CheckBox.stories.tsx (1)
src/components/ui/CheckBox/CheckBox.tsx (1)
  • CheckBox (44-65)
src/app/member/_api/member.queries.ts (3)
src/app/member/_types/member.types.ts (3)
  • UpdateMemberRequest (11-14)
  • NicknameCheckRequest (18-18)
  • PhoneNumberCheckRequest (21-21)
src/app/member/_api/member.api.ts (4)
  • putMember (27-35)
  • getNicknameCheck (44-57)
  • getPhoneNumberCheck (66-80)
  • getMember (17-19)
src/constants/queryKeys.constants.ts (1)
  • QUERY_KEYS (1-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: storybook-preview
  • GitHub Check: test
🔇 Additional comments (51)
src/app/layout.css.ts (1)

13-13: 모바일 뷰포트 호환성 개선을 위한 좋은 변경입니다.

100vh에서 100dvh로 변경하여 모바일 브라우저의 주소 표시줄 등 동적 UI 요소 변화에 대응할 수 있게 되었습니다. 이는 특히 모바일 디바이스에서 더 나은 사용자 경험을 제공합니다.

src/styles/reset.css.ts (1)

43-43: 리스트 스타일 리셋 범위 확장이 적절합니다.

기존 "ol, ul"에서 "ol, ul, li"로 변경하여 리스트 아이템에도 list-style: none이 적용되도록 했습니다. 이는 더 포괄적인 스타일 리셋으로 새로 추가되는 UI 컴포넌트들과 일관성을 유지하는 데 도움이 됩니다.

src/app/member/_types/index.ts (1)

1-1: 타입 모듈의 깔끔한 구조입니다.

도메인별 타입 정의를 _types 폴더에 위치시키고 인덱스 파일을 통해 중앙화된 내보내기를 제공하는 것이 코딩 가이드라인에 맞습니다. 타입 전용 내보내기(export type *)도 적절하게 사용되었습니다.

src/app/member/_utils/index.ts (1)

1-1: 유틸리티 모듈의 올바른 구조입니다.

도메인별 유틸리티를 _utils 폴더에 위치시키고 camelCase 네이밍 규칙(phoneNumberUtils)을 준수했습니다. 인덱스 파일을 통한 중앙화된 내보내기 패턴도 적절합니다.

src/components/ui/CheckBox/index.ts (1)

1-1: 배럴 파일 구성이 명료합니다.

CheckBox 컴포넌트를 디렉터리 루트에서 바로 import 가능하도록 한 점이 가독성과 DX 향상에 도움이 됩니다. 추가 이슈 없습니다.

src/app/member/_schemas/index.ts (1)

1-1: 스키마 배럴 구성 적절 및 네이밍 규칙 준수 확인

  • _schemas 폴더 내에 위치해 폴더 규칙 준수
  • nicknameSchema, phoneNumberSchema 모두 camelCase 네이밍 준수

특이사항 없습니다.

src/app/member/onboarding/_components/AgreeStep/index.ts (1)

1-1: 컴포넌트 배럴(export) 추가 확인

AgreeStep 재-export 처리가 일관된 컴포넌트 구조 유지에 기여합니다. 문제 없습니다.

src/app/member/onboarding/_components/NicknameStep/index.ts (1)

1-1: 컴포넌트 배럴(export) 정상

NicknameStep 재-export 역시 다른 스텝들과 동일한 패턴을 따릅니다. 이상 없습니다.

src/app/member/onboarding/_components/OnboardingTitle/index.ts (1)

1-1: 배럴 파일 이상 무

OnboardingTitle export만 포함된 간결한 배럴 파일, 추가 코멘트 없습니다.

src/app/member/onboarding/_components/OnboardingFunnel/index.ts (1)

1-1: LGTM!

표준적인 컴포넌트 인덱스 파일 패턴을 올바르게 따르고 있습니다.

src/constants/index.ts (1)

1-1: LGTM!

새로운 쿼리 키 상수를 올바르게 re-export하고 있으며, 파일 네이밍이 camelCase 규칙을 따릅니다.

src/app/member/_api/index.ts (1)

1-2: LGTM!

멤버 API와 쿼리 모듈을 올바르게 re-export하고 있으며, 파일 네이밍과 구조가 가이드라인을 준수합니다.

src/app/member/onboarding/_components/PhoneNumberStep/index.ts (1)

1-1: LGTM!

표준적인 컴포넌트 인덱스 파일 패턴을 올바르게 따르고 있습니다.

src/lib/api/client.ts (2)

34-37: 에러 메시지 처리 개선을 승인합니다.

서버에서 전달되는 실제 에러 메시지를 우선적으로 표시하도록 개선한 변경사항이 PR 목표와 일치합니다. 메시지 우선순위가 적절하게 설정되어 있습니다.


75-75: Authorization 헤더 Bearer 접두사 제거 확인 요청
코드베이스 전체를 검색한 결과, 오직 한 곳(src/lib/api/client.ts)에서만 Authorization 헤더를 설정하고 있으며, 기존에 "Bearer " 접두사를 사용한 패턴이나 문서(예: .md) 내 언급은 없습니다.
백엔드 API가 토큰 앞에 Bearer 접두사를 요구하는지 확실하지 않으므로, 다음 위치를 중심으로 API 호환성을 반드시 검증해 주세요:

  • src/lib/api/client.ts (라인 75):
    request.headers.set("Authorization", `${session.accessToken}`);
src/app/member/onboarding/page.tsx (1)

1-5: 깔끔한 페이지 컴포넌트 구현

OnboardingFunnel 컴포넌트를 적절히 import하여 렌더링하는 간단명료한 구조입니다.

src/app/member/onboarding/_types/index.ts (1)

1-6: 타입 구조 및 네이밍 규칙 준수

도메인별 타입 정의가 _types 폴더에 적절히 위치하고 있으며, re-export 구조가 깔끔하게 구성되어 있습니다.

src/app/member/onboarding/_components/OnboardingTitle/OnboardingTitle.tsx (1)

1-15: 재사용 가능한 타이틀 컴포넌트 구현

간단하고 명확한 구조의 타이틀 컴포넌트입니다. 타입 정의가 적절하며 UI 일관성을 위한 좋은 접근법입니다.

src/app/member/onboarding/layout.tsx (1)

1-31: 온보딩 레이아웃 구조 및 네비게이션 기능 구현

클라이언트 컴포넌트로 적절히 정의되었으며, 뒤로가기 기능과 함께 깔끔한 레이아웃 구조를 제공합니다. GNB와 메인 콘텐츠 영역이 잘 분리되어 있습니다.

src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.css.ts (1)

1-11: 코딩 가이드라인을 준수하고 적절한 스타일 정의입니다.

vanilla-extract 스타일 파일이 camelCase 네이밍과 .css.ts 확장자를 사용하여 코딩 가이드라인을 준수하고 있으며, 컴포넌트와 같은 폴더에 적절히 배치되어 있습니다. 스타일 정의도 간결하고 명확합니다.

src/app/(auth)/login/callback/page.tsx (3)

3-3: React Query 통합이 적절하게 구현되었습니다.

useQueryClient를 사용하여 로그인 성공 후 회원 정보 캐시를 업데이트하는 로직이 올바르게 구현되어 있습니다.


8-8: 중앙화된 쿼리 키 상수 사용을 승인합니다.

QUERY_KEYS 상수를 사용하여 쿼리 키를 중앙에서 관리하는 것은 좋은 접근입니다. 일관성과 유지보수성을 향상시킵니다.


25-34: 온보딩 플로우 통합이 올바르게 구현되었습니다.

response.isSignUp 플래그를 사용하여 신규 회원을 온보딩 페이지로, 기존 회원을 홈으로 리다이렉트하는 로직이 적절합니다.

src/app/member/onboarding/layout.css.ts (1)

3-9: 모바일 우선 반응형 디자인이 잘 구현되었습니다.

최대 너비 480px 제한과 전체 뷰포트 높이를 사용한 레이아웃이 모바일 환경에 적합합니다. 플렉스 레이아웃을 사용하여 구조도 명확합니다.

src/app/member/onboarding/_constants/agreement.constants.ts (1)

1-19: 상수 정의가 잘 구성되어 있습니다.

파일 구조와 네이밍 규칙이 코딩 가이드라인을 잘 따르고 있으며, 약관 항목들이 명확하게 정의되어 있습니다. 한국어 라벨과 필수/선택 구분이 적절합니다.

src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx (2)

19-32: useFunnel 훅 사용이 적절하게 구현되었습니다.

펀넬 설정과 초기 컨텍스트 구성이 잘 되어 있으며, 타입 정의도 명확합니다. 각 스텝의 타입이 올바르게 정의되어 있습니다.


34-59: 펀넬 렌더링 로직이 잘 구성되어 있습니다.

각 스텝 컴포넌트로의 props 전달과 히스토리 관리가 적절하게 구현되었습니다. 컨텍스트 업데이트와 네비게이션 흐름이 명확합니다.

src/components/ui/CheckBox/CheckBox.tsx (1)

32-43: 컴포넌트 문서화가 잘 되어 있습니다.

JSDoc을 사용한 상세한 문서화와 사용 예시가 포함되어 있어 개발자가 쉽게 이해하고 사용할 수 있습니다.

src/app/member/_schemas/member.schema.ts (1)

3-9: 닉네임 스키마 검증 규칙이 적절합니다.

2-8자 길이 제한과 한글/영문만 허용하는 정규식이 적절하게 구성되어 있으며, 사용자 친화적인 한글 에러 메시지가 포함되어 있습니다.

src/app/member/onboarding/_components/AgreeStep/AgreeStep.css.ts (1)

1-31: vanilla-extract 스타일링이 적절하게 구현되었습니다.

디자인 토큰 사용, 네이밍 규칙, 파일 위치 모두 코딩 가이드라인을 잘 따르고 있으며, 동의 단계 UI에 적합한 스타일 구성을 보여줍니다.

src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx (2)

1-15: import 구문이 잘 구성되어 있습니다

필요한 라이브러리들이 적절히 import되어 있고, 절대 경로를 사용하여 일관성 있게 작성되었습니다.


35-38: 약관 동의 상태 검증 로직이 올바르게 구현되어 있습니다

필수 약관만 체크하면 다음 단계로 진행할 수 있도록 하는 로직이 정확합니다.

src/app/member/_api/member.queries.ts (4)

1-15: import 구문이 잘 구성되어 있습니다

필요한 타입들과 API 함수들이 적절히 import되어 있고, 코딩 가이드라인을 준수하고 있습니다.


17-21: mutation 구성이 간결하고 명확합니다

함수 구조가 명확하고 타입 안전성이 보장되어 있습니다.


23-34: 중복 체크 mutation들의 일관성이 좋습니다

두 중복 체크 mutation이 동일한 패턴으로 구현되어 있어 일관성이 있습니다.


36-43: 캐싱 설정이 적절합니다

staleTime과 gcTime 설정이 회원 정보의 특성에 맞게 잘 구성되어 있습니다. 1시간 staleTime과 24시간 gcTime은 회원 정보 특성상 적절한 값입니다.

src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (4)

1-14: import 구문이 잘 구성되어 있습니다

필요한 의존성들이 적절히 import되어 있고, 절대 경로를 사용하여 일관성 있게 작성되었습니다.


41-61: 전화번호 검증 로직이 잘 구현되어 있습니다

하이픈을 제거하고 중복 체크를 수행하는 로직이 올바르게 구현되어 있고, 성공/실패 케이스 모두 적절히 처리되고 있습니다.


82-87: onChange 핸들러에서 setValue 사용이 적절합니다

전화번호 포맷팅을 위한 setValue 사용이 올바르게 구현되어 있습니다.


113-113: 버튼 비활성화 조건이 적절합니다

유효성 검사 오류, 빈 입력값, 제출 중 상태를 모두 고려한 비활성화 조건이 잘 구현되어 있습니다.

src/app/member/onboarding/_types/onboarding.types.ts (2)

1-17: 온보딩 단계별 타입 정의가 논리적으로 잘 구성되어 있습니다

각 단계(NicknameFunnel → PhoneNumberFunnel → AgreeFunnel)에서 필요한 데이터가 점진적으로 증가하는 구조가 명확하게 표현되어 있습니다.


19-23: AgreementConstants 타입이 적절하게 정의되어 있습니다

약관 관련 상수들의 구조가 명확하고 필요한 속성들이 포함되어 있습니다.

src/components/ui/CheckBox/CheckBox.stories.tsx (3)

1-37: Storybook 메타 설정이 잘 구성되어 있습니다

컴포넌트 문서화, argTypes 설정, 기본 args 설정이 모두 적절하게 구성되어 있습니다.


42-55: InteractiveCheckBox 구현이 적절합니다

상태 관리와 콜백 처리가 올바르게 구현되어 있어 Storybook에서 인터랙티브한 테스트가 가능합니다.


57-82: 스토리 variants가 잘 구성되어 있습니다

기본 스토리와 배경 없는 스토리가 적절히 구성되어 있고, 각각에 대한 문서화도 잘 되어 있습니다.

src/app/member/_types/member.types.ts (1)

1-22: 타입 정의가 잘 구성되어 있습니다.

Member 타입을 기반으로 한 Pick 유틸리티 타입들의 사용이 적절하며, 도메인별 타입 정의가 _types 폴더에 올바르게 위치해 있습니다. 코드 중복을 방지하고 타입 안정성을 보장하는 좋은 구조입니다.

src/components/ui/CheckBox/CheckBox.css.ts (1)

31-57: compound variants 구현이 우수합니다.

다양한 상태 조합에 대한 스타일 정의가 체계적으로 구성되어 있으며, semantic 색상 토큰을 일관되게 사용하고 있습니다.

src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (1)

135-147: 버튼 비활성화 조건이 적절합니다.

폼 상태, 유효성 검사, 로딩 상태를 모두 고려한 버튼 비활성화 로직이 잘 구현되어 있습니다.

src/app/member/_api/member.api.ts (3)

44-57: 닉네임 중복 체크 API 구현이 적절합니다.

throwHttpErrors: false 옵션을 사용하여 HTTP 에러를 직접 처리하는 방식이 올바르게 구현되어 있습니다.


17-19: 회원 정보 조회 API가 적절합니다.

authHttp 클라이언트를 사용한 인증된 요청 구현이 올바르며, 타입 안정성도 보장되고 있습니다.


27-35: 회원 정보 수정 API 구현이 올바릅니다.

PUT 요청을 통한 회원 정보 수정 로직이 적절하게 구현되어 있으며, JSON 형태의 요청 본문 처리가 올바릅니다.

Comment thread src/lib/api/type.ts
Comment on lines +15 to +20
errorMessage?: string;
/**
* 에러 메시지
* @example "이미 만료된 토큰입니다."
*/
message?: string;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

중복된 에러 메시지 프로퍼티 정리 필요

errorMessagemessage 프로퍼티가 동일한 목적과 설명을 가지고 있어 혼란을 야기할 수 있습니다. 둘 중 하나를 선택하여 일관성을 유지하거나, 각각의 명확한 역할을 정의해야 합니다.

다음 중 하나의 접근법을 권장합니다:

  1. 하나의 프로퍼티만 유지 (예: message)
  2. 각 프로퍼티의 역할을 명확히 구분 (예: errorMessage는 사용자용, message는 디버깅용)
🤖 Prompt for AI Agents
In src/lib/api/type.ts around lines 15 to 20, there are two properties,
errorMessage and message, both representing error messages which causes
redundancy and confusion. Choose one property to keep for consistency,
preferably message, and remove the other, or clearly define distinct roles for
each (e.g., errorMessage for user-facing messages and message for debugging).
Update the code and comments accordingly to reflect this decision.

Comment on lines +1 to +11
import { style } from "@vanilla-extract/css";

import { colors } from "@/styles";

export const wrapper = style({
height: "100%",
});

export const icon = style({
color: colors.neutral[90],
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

코드 중복을 해결하여 공통 스타일을 재사용할 수 있도록 개선하세요.

이 파일은 PhoneNumberStep.css.ts와 동일한 스타일 정의를 포함하고 있어 코드 중복이 발생하고 있습니다. 온보딩 단계들에서 공통으로 사용되는 wrappericon 스타일을 별도의 공통 스타일 파일로 분리하는 것을 고려해보세요.

예를 들어, src/app/member/onboarding/_components/common.css.ts를 생성하여 공통 스타일을 정의하고 각 컴포넌트에서 import하여 사용할 수 있습니다:

+// src/app/member/onboarding/_components/common.css.ts
+import { style } from "@vanilla-extract/css";
+import { colors } from "@/styles";
+
+export const stepWrapper = style({
+  height: "100%",
+});
+
+export const stepIcon = style({
+  color: colors.neutral[90],
+});
-import { style } from "@vanilla-extract/css";
-import { colors } from "@/styles";
-
-export const wrapper = style({
-  height: "100%",
-});
-
-export const icon = style({
-  color: colors.neutral[90],
-});
+import { stepWrapper as wrapper, stepIcon as icon } from "../common.css";
+
+export { wrapper, icon };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { style } from "@vanilla-extract/css";
import { colors } from "@/styles";
export const wrapper = style({
height: "100%",
});
export const icon = style({
color: colors.neutral[90],
});
// src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts
-import { style } from "@vanilla-extract/css";
-
-import { colors } from "@/styles";
-
-export const wrapper = style({
- height: "100%",
-});
-
-export const icon = style({
- color: colors.neutral[90],
-});
+import { stepWrapper as wrapper, stepIcon as icon } from "../common.css";
+
+export { wrapper, icon };
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/NicknameStep/NicknameStep.css.ts lines
1 to 11, the wrapper and icon styles are duplicated from PhoneNumberStep.css.ts.
To fix this, create a new file
src/app/member/onboarding/_components/common.css.ts to define these common
styles once, then import and use them in both NicknameStep.css.ts and
PhoneNumberStep.css.ts to eliminate duplication and promote reuse.

Comment on lines +1 to +11
export const phoneNumberUtils = (value: string) => {
const phoneNumber = value.replace(/[^0-9]/g, "");
if (phoneNumber.length < 4) return phoneNumber;
if (phoneNumber.length < 8)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
if (phoneNumber.length === 10)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`;
if (phoneNumber.length === 11)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
return phoneNumber;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

한국 전화번호 패턴에 맞게 포맷팅 로직을 개선하세요.

현재 구현에서 몇 가지 개선점이 있습니다:

  1. 8-9자리 처리 누락: 8자리와 9자리 전화번호에 대한 처리가 없어 예상치 못한 동작이 발생할 수 있습니다.
  2. 한국 전화번호 패턴 불일치: 11자리 전화번호를 XXX-XXXX-XXXX로 처리하는데, 한국 휴대폰 번호는 010-XXXX-XXXX 형태가 더 적절합니다.
  3. 함수명 개선: phoneNumberUtils보다는 formatPhoneNumber가 더 명확합니다.

다음과 같이 개선할 수 있습니다:

-export const phoneNumberUtils = (value: string) => {
+export const formatPhoneNumber = (value: string) => {
   const phoneNumber = value.replace(/[^0-9]/g, "");
   if (phoneNumber.length < 4) return phoneNumber;
-  if (phoneNumber.length < 8)
+  if (phoneNumber.length < 7)
     return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
+  if (phoneNumber.length < 8)
+    return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
+  if (phoneNumber.length === 8)
+    return `${phoneNumber.slice(0, 4)}-${phoneNumber.slice(4)}`;
+  if (phoneNumber.length === 9)
+    return `${phoneNumber.slice(0, 2)}-${phoneNumber.slice(2, 5)}-${phoneNumber.slice(5)}`;
   if (phoneNumber.length === 10)
     return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`;
   if (phoneNumber.length === 11)
-    return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
+    return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
   return phoneNumber;
 };

또한 src/app/member/_utils/index.ts에서 export하는 이름도 함께 업데이트해야 합니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const phoneNumberUtils = (value: string) => {
const phoneNumber = value.replace(/[^0-9]/g, "");
if (phoneNumber.length < 4) return phoneNumber;
if (phoneNumber.length < 8)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
if (phoneNumber.length === 10)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`;
if (phoneNumber.length === 11)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
return phoneNumber;
};
export const formatPhoneNumber = (value: string) => {
const phoneNumber = value.replace(/[^0-9]/g, "");
if (phoneNumber.length < 4) return phoneNumber;
if (phoneNumber.length < 7)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
if (phoneNumber.length < 8)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
if (phoneNumber.length === 8)
return `${phoneNumber.slice(0, 4)}-${phoneNumber.slice(4)}`;
if (phoneNumber.length === 9)
return `${phoneNumber.slice(0, 2)}-${phoneNumber.slice(2, 5)}-${phoneNumber.slice(5)}`;
if (phoneNumber.length === 10)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`;
if (phoneNumber.length === 11)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
return phoneNumber;
};
🤖 Prompt for AI Agents
In src/app/member/_utils/phoneNumberUtils.ts lines 1 to 11, rename the function
from phoneNumberUtils to formatPhoneNumber for clarity. Update the formatting
logic to handle 8 and 9 digit phone numbers properly by adding conditions for
these lengths. Adjust the 11-digit phone number formatting to match the Korean
pattern "010-XXXX-XXXX" instead of the current slicing. Finally, update the
export name in src/app/member/_utils/index.ts to reflect the new function name
formatPhoneNumber.

import { PhoneNumberStep } from "../PhoneNumberStep";

export const OnboardingFunnel = () => {
const { data: member } = useMemberQuery();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

멤버 쿼리의 로딩 및 에러 상태 처리가 필요합니다.

useMemberQuery 훅의 로딩 상태나 에러 상태를 처리하지 않고 있어, API 호출이 실패하거나 아직 로딩 중일 때 문제가 발생할 수 있습니다.

다음과 같이 수정하는 것을 권장합니다:

-  const { data: member } = useMemberQuery();
+  const { data: member, isLoading, error } = useMemberQuery();
+
+  if (isLoading) {
+    return <div>로딩 중...</div>;
+  }
+
+  if (error) {
+    return <div>오류가 발생했습니다.</div>;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { data: member } = useMemberQuery();
const { data: member, isLoading, error } = useMemberQuery();
if (isLoading) {
return <div>로딩 중...</div>;
}
if (error) {
return <div>오류가 발생했습니다.</div>;
}
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/OnboardingFunnel/OnboardingFunnel.tsx
at line 17, the useMemberQuery hook's loading and error states are not handled,
which can cause issues if the API call is still loading or fails. Update the
code to destructure loading and error states from useMemberQuery, then add
conditional rendering to show a loading indicator while loading and an error
message if an error occurs before rendering the member data.

Comment on lines +51 to +67
const onSubmit = () => {
updateMember(
{
nickname: nickname,
phoneNumber: phoneNumber,
optInMarketing: checkedIds.includes("marketing"),
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.member,
});
router.push("/");
},
}
);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

API 호출 시 오류 처리가 부족합니다

성공 케이스만 처리하고 있고, 실패 케이스에 대한 처리가 없습니다. 사용자에게 적절한 피드백을 제공해야 합니다.

const onSubmit = () => {
  updateMember(
    {
      nickname: nickname,
      phoneNumber: phoneNumber,
      optInMarketing: checkedIds.includes("marketing"),
    },
    {
      onSuccess: () => {
        queryClient.invalidateQueries({
          queryKey: QUERY_KEYS.member,
        });
        router.push("/");
      },
+     onError: (error) => {
+       console.error("회원 정보 업데이트 실패:", error);
+       // 사용자에게 오류 메시지 표시 로직 추가
+     },
    }
  );
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onSubmit = () => {
updateMember(
{
nickname: nickname,
phoneNumber: phoneNumber,
optInMarketing: checkedIds.includes("marketing"),
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.member,
});
router.push("/");
},
}
);
};
const onSubmit = () => {
updateMember(
{
nickname: nickname,
phoneNumber: phoneNumber,
optInMarketing: checkedIds.includes("marketing"),
},
{
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: QUERY_KEYS.member,
});
router.push("/");
},
onError: (error) => {
console.error("회원 정보 업데이트 실패:", error);
// 사용자에게 오류 메시지 표시 로직 추가
},
}
);
};
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/AgreeStep/AgreeStep.tsx between lines
51 and 67, the onSubmit function handles only the success case of the
updateMember API call but lacks error handling. Add an onError callback to
updateMember to handle failure cases, and provide appropriate user feedback such
as displaying an error message or notification to inform the user about the
failure.

Comment on lines +56 to +58
onError: error => {
console.error("휴대폰 번호 중복 확인 실패:", error);
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 로깅만으로는 사용자 경험이 부족할 수 있습니다

콘솔 로그만으로는 사용자가 오류를 인지하기 어렵습니다. 사용자에게 적절한 피드백을 제공하는 것을 고려해보세요.

onError: error => {
  console.error("휴대폰 번호 중복 확인 실패:", error);
+ setError("phoneNumber", {
+   type: "manual",
+   message: "휴대폰 번호 확인 중 오류가 발생했습니다. 다시 시도해주세요.",
+ });
},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onError: error => {
console.error("휴대폰 번호 중복 확인 실패:", error);
},
onError: error => {
console.error("휴대폰 번호 중복 확인 실패:", error);
setError("phoneNumber", {
type: "manual",
message: "휴대폰 번호 확인 중 오류가 발생했습니다. 다시 시도해주세요.",
});
},
🤖 Prompt for AI Agents
In src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx
around lines 56 to 58, the onError handler only logs the error to the console,
which does not inform the user about the failure. Update the error handling to
also display a user-friendly error message or notification in the UI, so the
user is aware that the phone number duplication check failed.

Comment thread src/app/member/_api/member.api.ts
Copy link
Copy Markdown
Member

@Seojunhwan Seojunhwan left a comment

Choose a reason for hiding this comment

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

수빈님 고생하셨어요! 사소한 의견들 슥삭 남겨두었습니다!

Comment thread src/app/member/_api/member.queries.ts Outdated
Comment on lines +36 to +43
export const useMemberQuery = () => {
return useQuery({
queryKey: QUERY_KEYS.member,
queryFn: getMember,
staleTime: 1000 * 60 * 60,
gcTime: 1000 * 60 * 60 * 24,
});
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P3: queryOptions로 분리해서 제공해보는 것은 어떨까요? 사용처에서 useQuery or useSuspenseQuery를 자유롭게 선택해서 사용할 수 있도록요! (커스텀 또한!)

refs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

a3962d7

오홍! 준환님이 말씀해주신 방식대로 queryOptions로 분리하여 수정했습니다 !!

한 가지 궁금한 점이 있습니돠! 저는 평소에 useMemberQuery()처럼 훅으로 한 번 더 래핑해서 사용하는 방식에 익숙했는데 직접 queryOptions를 useQuery()나 useSuspenseQuery()에서 호출하는 방식은 보통 어떤 기준으로 선택하시는지 궁금합니다~!
커스텀해서 재사용하기 더 편한 장점이 있어서일까요?!

Comment thread src/app/(auth)/login/callback/page.tsx Outdated
clearClientSessionCache();

const redirectUrl = next || "/";
queryClient.setQueryData(QUERY_KEYS.member, response);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P3: 이 invalidate 코드는 useLoginMutation내부에 있어도 될 것 같아요! (clearClientSessionCache도!)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

0f1e61f

그릏네요!! 수정했습니다 ✨

Comment thread src/app/member/_api/member.queries.ts Outdated

export const useUpdateMemberMutation = () => {
return useMutation({
mutationFn: (updateMember: UpdateMemberRequest) => putMember(updateMember),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P3: 인자가 하나라 생략해보아도 좋을 것 같아요!

Suggested change
mutationFn: (updateMember: UpdateMemberRequest) => putMember(updateMember),
mutationFn: putMember,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

a3962d7

좋네요~! 수정했슴돠 🙇🏻‍♀️

Comment thread src/app/member/_types/member.types.ts Outdated
phoneNumber: string;
optInMarketing: boolean;
};
export type MemberResponse = Member;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ASK: API 응답, 바디 타입은 api와 가까이 있게끔 두는 건 어때요?!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

옹 그릏다면, Response나 Request 관련 타입은
/app/member/_types/member.types.ts -> /app/member/_api/member.types.ts 으로 관리하자는 말씀이신거죠?!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

맞습니다! 결국 클라에서 필요한 모델과 응답을 분리해서 사용한다면 추후 응답이 변경되었을 때도 유연하게 대응할 수 있어요!

클라 모델, api 응답 분리는 꼭 필요한 작업은 아니옵니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

f3af4f5

/app/member/_api/member.types.ts 하위로 옮겼습니다 ㅎㅎ
다만 파일 네이밍이 살짝 고민이 됩니다! 폴더 위치가 다르긴 하지만, 기존 /app/member/_types/member.types.ts와 파일명이 동일해서.. 네이밍을 조금 달리하는 편이 나을지 궁금합니다!

Comment on lines +1 to +11
export const phoneNumberUtils = (value: string) => {
const phoneNumber = value.replace(/[^0-9]/g, "");
if (phoneNumber.length < 4) return phoneNumber;
if (phoneNumber.length < 8)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3)}`;
if (phoneNumber.length === 10)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 6)}-${phoneNumber.slice(6)}`;
if (phoneNumber.length === 11)
return `${phoneNumber.slice(0, 3)}-${phoneNumber.slice(3, 7)}-${phoneNumber.slice(7)}`;
return phoneNumber;
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

옹 테스트 코드 작성하기 적합한 녀석이네요!

참고로 요런 것도 있습니다!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

요긴 딱히 로딩이나 에러 처리가 필요하지 않은 곳일까요?!

만약 필요하다면 Suspensive를 적극 이용해보아도 좋을 것 같아요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

닉네임/전화번호가 옵셔널일 수도 있어서 별도의 로딩이나 에러 처리없이 구현했습니다!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

@wkdtnqls0506 wkdtnqls0506 Jul 16, 2025

Choose a reason for hiding this comment

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

88b6b7c

a797ce6

64f7190

react-simplikit를 사용해서 디바운스 로직 수정했습니다~!
매우 간단하게 사용할 수 있군요!! 좋은 의견 감사합니다 👍🏻

Comment on lines +94 to +99
const onSubmit = () => {
if (!errors.nickname) {
onNext(nicknameValue);
return;
}
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P2: 아래처럼 개선해보는 건 어떨까요? (아마 useForm이 알아서 처리해줄 거라 리턴이 필요없을 수도 있어요!)

Suggested change
const onSubmit = () => {
if (!errors.nickname) {
onNext(nicknameValue);
return;
}
};
const onSubmit = () => {
if (errors.nickname) {
return;
}
onNext(nicknameValue);
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

88b6b7c

react-hook-form이 onSubmit 실행 전에 에러를 꼼꼼하게 처리해주는군요!
준환님 말씀처럼 에러가 있으면 onSubmit이 호출되지 않아서 조건문을 제거하고 더 간단하게 수정했습니다 🙇🏻‍♀️

};

export const NicknameStep = ({ nickname, onNext }: NicknameStepProps) => {
const [isNicknameValidating, setIsNicknameValidating] = useState(false);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P2: useNicknameCheckMutation의 isPending을 사용해도 될 것 같아요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

디바운스 대기 중에 버튼을 비활성화하지 않으면 닉네임 중복 검사가 끝나기 전에 다음 단계로 넘어갈 수 있을 것 같더라구요..!!
그래서 별도로 isNicknameValidating 상태를 두고 관리했습니다!

clearTimeout(debounceTimer.current);
}

clearErrors("nickname");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

P2: 닉네임 에러를 여기서 클리어해도 괜찮을까요?!

Copy link
Copy Markdown
Contributor Author

@wkdtnqls0506 wkdtnqls0506 Jul 16, 2025

Choose a reason for hiding this comment

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

64f7190

해당 커밋에서 로직 전부 수정했습니다~!🙇🏻‍♀️

Copy link
Copy Markdown
Member

@Seojunhwan Seojunhwan left a comment

Choose a reason for hiding this comment

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

고생하셨어요~! 우선 고고 하시죠!

@wkdtnqls0506 wkdtnqls0506 merged commit 0ca56c5 into main Jul 18, 2025
6 checks passed
@wkdtnqls0506 wkdtnqls0506 deleted the feature/PRODUCT-49 branch July 18, 2025 12:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feature 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PRODUCT-49] 온보딩 페이지 UI 구현

2 participants