Skip to content

feat: 마이페이지 UI 구현 (#43)#89

Merged
wkdtnqls0506 merged 9 commits intomainfrom
feature/PRODUCT-55
Jul 20, 2025
Merged

feat: 마이페이지 UI 구현 (#43)#89
wkdtnqls0506 merged 9 commits intomainfrom
feature/PRODUCT-55

Conversation

@wkdtnqls0506
Copy link
Contributor

@wkdtnqls0506 wkdtnqls0506 commented Jul 18, 2025

✅ 이슈 번호

close #43


🪄 작업 내용 (변경 사항)

  • 마이페이지 UI 구현 : /member/profile

📸 스크린샷

스크린샷 2025-07-19 오전 1 17 51

💡 설명

  • Profile, Banner, MenuList 3개의 컴포넌트로 분리하여 페이지를 구성했습니다.
  • 메뉴 링크 이동은 아직 미정이라 전달받으면 추후 반영 예정입니다! (TODO)

🗣️ 리뷰어에게 전달 사항

로그아웃은 다음 PR에서 진행할 예정입니다 ~ . ~!!


📍 트러블 슈팅

Summary by CodeRabbit

  • 신규 기능

    • 마이페이지(프로필) 화면이 추가되었습니다. 프로필 정보, 가게 소개 배너, 메뉴 리스트(공지사항, 고객센터, 이용가이드, 로그아웃 등)를 확인할 수 있습니다.
    • 프로필 정보는 닉네임과 이메일을 표시하며, 로딩 시 스켈레톤 UI가 제공됩니다.
  • 스타일

    • 마이페이지 및 하위 컴포넌트(프로필, 배너, 메뉴 리스트)에 대한 스타일이 적용되었습니다.
  • 아이콘 및 UI 개선

    • 일부 아이콘이 새로운 디자인(예: ChevronLeftIcon, CircleCloseIcon, LogoWordmarkIcon)으로 교체되었습니다.

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

coderabbitai bot commented Jul 18, 2025

Walkthrough

마이페이지 UI를 위한 새로운 컴포넌트 구조와 스타일이 대거 추가되었습니다. Profile, Banner, MenuList 등 주요 UI 컴포넌트와 그에 따른 CSS, 타입, 상수 파일이 신설되었으며, 레이아웃 및 페이지 파일도 새로 작성되었습니다. 일부 기존 아이콘 사용처가 새로운 아이콘으로 교체되었습니다.

Changes

파일/경로 요약 변경 내용 요약
.../profile/_components/Banner/Banner.css.ts
.../MenuList/MenuList.css.ts
.../Profile/Profile.css.ts
.../profile/layout.css.ts
마이페이지 각 컴포넌트 및 레이아웃용 CSS 모듈 신설, 스타일 클래스 다수 추가
.../profile/_components/Banner/Banner.tsx
.../MenuList/MenuList.tsx
.../Profile/Profile.tsx
.../Profile/ProfileLayout.tsx
.../Profile/ProfileSkeleton.tsx
Banner, MenuList, Profile, ProfileLayout, ProfileSkeleton 등 주요 UI 컴포넌트 신설
.../profile/_components/Banner/index.ts
.../MenuList/index.ts
.../Profile/index.ts
각 컴포넌트의 index 파일에서 컴포넌트 재수출 추가
.../profile/_constants/menuList.constants.ts
.../profile/_constants/index.ts
메뉴 리스트 상수 및 상수 index 파일 신설 및 재수출
.../profile/types/menuList.types.ts
.../profile/types/index.ts
메뉴 리스트 관련 타입 정의 및 타입 index 파일 신설, 재수출
.../profile/layout.tsx 마이페이지 레이아웃 컴포넌트 신설, GNB 및 네비게이션 버튼 포함
.../profile/page.tsx 마이페이지 메인 페이지 컴포넌트 신설, Profile, Banner, MenuList 렌더링 및 Suspense 적용
.../onboarding/_components/NicknameStep/NicknameStep.tsx
.../PhoneNumberStep/PhoneNumberStep.tsx
.../onboarding/layout.tsx
.../login/page.tsx
.../ui/GNB/GNB.stories.tsx
.../ui/TextButton/TextButton.stories.tsx
.../ui/TextField/TextField.stories.tsx
일부 아이콘(ClearIcon → CircleCloseIcon, LeftArrowIcon → ChevronLeftIcon, KoreanOrangeLogo → LogoWordmarkIcon 등) 교체

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ProfilePage
    participant Profile
    participant Banner
    participant MenuList

    User->>ProfilePage: 페이지 접근
    ProfilePage->>Profile: Suspense로 프로필 정보 로딩
    Profile->>Profile: 회원 정보 fetch 및 표시
    ProfilePage->>Banner: 배너 컴포넌트 렌더링
    ProfilePage->>MenuList: 메뉴 리스트 렌더링
Loading

Possibly related PRs

  • YAPP-Github/26th-Web-Team-1-FE#69: Banner 등에서 사용하는 Bleed, HStack, VStack, Text 등 유틸리티 컴포넌트와 스타일링 도구를 제공하며, 본 PR의 UI 구현과 직접적으로 연결됨.

Poem

🥕
마이페이지에 햇살이 쏟아져
프로필, 배너, 메뉴가 반짝여
토끼는 뛰며 버튼을 눌러
새 아이콘과 스타일로 옷을 갈아입어
오늘도 코드밭에 꽃이 피었네!
🐰✨

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/(auth)/login/page.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.

src/app/member/profile/_components/Banner/index.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/profile/_components/Banner/Banner.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.

  • 22 others

📜 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 20127e5 and deaed52.

⛔ Files ignored due to path filters (6)
  • public/images/store.png is excluded by !**/*.png
  • src/assets/chevron-left.svg is excluded by !**/*.svg
  • src/assets/chevron-right.svg is excluded by !**/*.svg
  • src/assets/circle-close.svg is excluded by !**/*.svg
  • src/assets/logo-wordmark.svg is excluded by !**/*.svg
  • src/assets/logo/front-bapurit.svg is excluded by !**/*.svg
📒 Files selected for processing (25)
  • src/app/(auth)/login/page.tsx (2 hunks)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (2 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (2 hunks)
  • src/app/member/onboarding/layout.tsx (2 hunks)
  • src/app/member/profile/_components/Banner/Banner.css.ts (1 hunks)
  • src/app/member/profile/_components/Banner/Banner.tsx (1 hunks)
  • src/app/member/profile/_components/Banner/index.ts (1 hunks)
  • src/app/member/profile/_components/MenuList/MenuList.css.ts (1 hunks)
  • src/app/member/profile/_components/MenuList/MenuList.tsx (1 hunks)
  • src/app/member/profile/_components/MenuList/index.ts (1 hunks)
  • src/app/member/profile/_components/Profile/Profile.css.ts (1 hunks)
  • src/app/member/profile/_components/Profile/Profile.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/ProfileLayout.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/ProfileSkeleton.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/index.ts (1 hunks)
  • src/app/member/profile/_constants/index.ts (1 hunks)
  • src/app/member/profile/_constants/menuList.constants.ts (1 hunks)
  • src/app/member/profile/layout.css.ts (1 hunks)
  • src/app/member/profile/layout.tsx (1 hunks)
  • src/app/member/profile/page.tsx (1 hunks)
  • src/app/member/profile/types/index.ts (1 hunks)
  • src/app/member/profile/types/menuList.types.ts (1 hunks)
  • src/components/ui/GNB/GNB.stories.tsx (6 hunks)
  • src/components/ui/TextButton/TextButton.stories.tsx (2 hunks)
  • src/components/ui/TextField/TextField.stories.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (25)
  • src/app/(auth)/login/page.tsx
  • src/app/member/profile/_components/Banner/index.ts
  • src/app/member/profile/_components/Profile/index.ts
  • src/app/member/profile/types/index.ts
  • src/app/member/profile/_components/MenuList/index.ts
  • src/app/member/profile/_constants/index.ts
  • src/app/member/onboarding/layout.tsx
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx
  • src/components/ui/TextButton/TextButton.stories.tsx
  • src/components/ui/GNB/GNB.stories.tsx
  • src/components/ui/TextField/TextField.stories.tsx
  • src/app/member/profile/page.tsx
  • src/app/member/profile/_components/Profile/Profile.css.ts
  • src/app/member/profile/layout.tsx
  • src/app/member/profile/_constants/menuList.constants.ts
  • src/app/member/profile/_components/Profile/Profile.tsx
  • src/app/member/profile/_components/MenuList/MenuList.css.ts
  • src/app/member/profile/_components/MenuList/MenuList.tsx
  • src/app/member/profile/_components/Profile/ProfileLayout.tsx
  • src/app/member/profile/_components/Banner/Banner.css.ts
  • src/app/member/profile/types/menuList.types.ts
  • src/app/member/profile/_components/Banner/Banner.tsx
  • src/app/member/profile/_components/Profile/ProfileSkeleton.tsx
  • src/app/member/profile/layout.css.ts
⏰ 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: test
  • GitHub Check: storybook-preview
✨ 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

github-actions bot commented Jul 18, 2025

🎨 Storybook Preview: https://685a32a1c0bbd269fdb67af4-bmyeebvpjl.chromatic.com/
🔗 Chromatic Build: https://www.chromatic.com/build?appId=685a32a1c0bbd269fdb67af4&number=121
🕖 Updated at: 2025년 07월 20일 23시 43분 52초

Copy link

@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: 2

🧹 Nitpick comments (13)
src/app/member/profile/_constants/menuList.constants.ts (1)

1-1: TODO 주석은 이슈 번호로 연결하거나 FIXME로 구체화하세요
작업이 미뤄진 상태를 명확히 추적할 수 있도록 Jira‧GitHub Issue 번호를 포함하거나 FIXME: 형태로 변환해 주세요.

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

11-17: button 클래스 이름이 지나치게 범용적입니다
컴포넌트 레벨에서만 쓰인다고 해도 전역 충돌 위험이 있습니다. backButton 등 의미를 담은 네이밍으로 변경을 고려해 주세요.

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

7-7: 불필요한 async 키워드 제거

이 컴포넌트는 비동기 작업을 수행하지 않으므로 async 키워드가 불필요합니다.

-export default async function ProfilePage() {
+export default function ProfilePage() {
src/app/member/profile/layout.tsx (1)

35-40: 중복된 onClick 핸들러 제거

아이콘에 별도의 onClick 핸들러가 있는 것은 불필요합니다. 버튼의 onClick으로 충분합니다.

            <LeftArrowIcon
              width={24}
              height={24}
-             onClick={handleClick}
              className={styles.icon}
            />
src/app/member/profile/_components/MenuList/MenuList.tsx (1)

28-28: 로그아웃 모달 구현 계획

TODO 코멘트에 명시된 로그아웃 모달 기능을 구현하시겠습니까? 이슈를 생성하여 추적하는 것을 도울 수 있습니다.

src/app/member/profile/_components/Profile/Profile.tsx (2)

13-13: 에러 처리 추가 고려

현재 로딩 상태만 처리하고 있습니다. 데이터 fetch 실패 시 에러 처리도 추가하는 것을 고려해보세요.

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

그리고 에러 상태에 대한 UI 처리를 추가할 수 있습니다.


15-15: 로딩 UI 개선 계획

TODO 코멘트에 명시된 로딩 스피너나 스켈레톤 UI 구현을 도와드릴 수 있습니다.

src/app/member/profile/_components/MenuList/MenuList.css.ts (1)

5-7: 패딩 값 하드코딩 대신 디자인 토큰 사용 제안
"1.6rem" 을 직접 기입하기보다는 디자인 시스템의 spacing 토큰(예: spacing[16])을 사용하면 전역 일관성이 높아집니다.

src/app/member/profile/_components/Banner/Banner.css.ts (2)

5-8: 배경·패딩 값도 토큰으로 치환 권장
colors.redOrange[90], "1.4rem 2.4rem" 모두 하드코딩보다 디자인 토큰을 사용하면 테마 변경 시 유지보수가 쉬워집니다.


10-14: 링크 요소에 커서 스타일 추가 고려
linkWrappera 태그지만 flex wrapper라 사용자 포인터가 pointer 로 드러나지 않을 수도 있습니다. 명시적으로 cursor: pointer 를 부여하면 UX 가 향상됩니다.

src/app/member/profile/_components/Banner/Banner.tsx (3)

17-22: 이미지 alt 문구 구체화 권장
'가게 이미지' 는 스크린리더에서 의미가 모호합니다. 예: '등록된 가게 대표 이미지' 처럼 더 구체적으로 작성해 주세요.


24-26: 텍스트 하드코딩 국제화 고려
Korean 문자열이 직접 삽입되어 있습니다. I18n 전략(예: next-intl)을 도입 중이라면 메시지 파일로 분리해 주세요.


28-32: 앵커 접근성 속성 추가 제안
링크 텍스트가 충분하긴 하나, 아이콘만 클릭될 때 스크린리더가 인지하지 못할 수 있습니다.

-<Link href='/' className={styles.linkWrapper}>
+<Link href='/' className={styles.linkWrapper} aria-label='가게 등록하기'>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0ca56c5 and dd03c7d.

⛔ Files ignored due to path filters (3)
  • public/images/store.png is excluded by !**/*.png
  • src/assets/logo/front-bapurit.svg is excluded by !**/*.svg
  • src/assets/right-arrow.svg is excluded by !**/*.svg
📒 Files selected for processing (13)
  • src/app/member/profile/_components/Banner/Banner.css.ts (1 hunks)
  • src/app/member/profile/_components/Banner/Banner.tsx (1 hunks)
  • src/app/member/profile/_components/Banner/index.ts (1 hunks)
  • src/app/member/profile/_components/MenuList/MenuList.css.ts (1 hunks)
  • src/app/member/profile/_components/MenuList/MenuList.tsx (1 hunks)
  • src/app/member/profile/_components/MenuList/index.ts (1 hunks)
  • src/app/member/profile/_components/Profile/Profile.css.ts (1 hunks)
  • src/app/member/profile/_components/Profile/Profile.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/index.ts (1 hunks)
  • src/app/member/profile/_constants/menuList.constants.ts (1 hunks)
  • src/app/member/profile/layout.css.ts (1 hunks)
  • src/app/member/profile/layout.tsx (1 hunks)
  • src/app/member/profile/page.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
src/app/member/profile/_constants/menuList.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/profile/_components/Banner/Banner.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/profile/_components/Banner/Banner.css.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 **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.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/constants/*.{ts,tsx} : 전역 상수 정의는 `constants/` 폴더에 위치해야 한다.
src/app/member/profile/_components/MenuList/MenuList.css.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 **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.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: 스타일 근접성: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/profile/layout.css.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 **/*.css.ts : vanilla-extract 스타일 파일은 camelCase로, `.css.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: 스타일 근접성: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치해야 한다.
src/app/member/profile/_components/Profile/Profile.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 (3)
src/app/member/profile/_components/MenuList/MenuList.tsx (2)
src/app/member/profile/_components/MenuList/index.ts (1)
  • MenuList (1-1)
src/app/member/profile/_constants/menuList.constants.ts (1)
  • MENU_LIST (2-7)
src/app/member/profile/layout.tsx (1)
src/app/member/profile/layout.css.ts (1)
  • button (11-17)
src/app/member/profile/_components/Profile/Profile.tsx (2)
src/app/member/profile/_components/Profile/index.ts (1)
  • Profile (1-1)
src/app/member/_api/member.queries.ts (1)
  • memberQueryOptions (44-49)
⏰ 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
🔇 Additional comments (13)
src/app/member/profile/layout.css.ts (1)

19-21: semantic.icon.black 컬러 토큰 존재 여부 확인 요청
토큰이 제거되거나 변경되면 빌드 타임에 오류가 나지 않습니다. 존재 여부를 한 번 더 확인하고, 없을 경우 공통 색상 토큰으로 교체해 주세요.

src/app/member/profile/_components/Profile/index.ts (1)

1-1: 배럴(export) 패턴 적용 확인 완료
컴포넌트 재사용성을 높이는 적절한 패턴입니다.

src/app/member/profile/_components/MenuList/index.ts (1)

1-1: 배럴(export) 패턴 적용 확인 완료

src/app/member/profile/_components/Banner/index.ts (1)

1-1: 배럴(export) 패턴 적용 확인 완료

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

8-14: 컴포넌트 구조가 잘 설계되었습니다

VStack을 사용한 레이아웃 구성과 컴포넌트 분리가 깔끔하게 구현되었습니다. 각 컴포넌트의 역할이 명확하게 분리되어 있어 유지보수성이 좋습니다.

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

29-33: 접근성 고려가 잘 되어있습니다

aria-label 속성으로 스크린 리더 사용자를 위한 접근성이 잘 구현되었습니다.

src/app/member/profile/_components/Profile/Profile.css.ts (1)

1-19: CSS 스타일링이 잘 구현되었습니다

디자인 토큰을 활용한 일관된 스타일링과 vanilla-extract 사용이 적절합니다. 원형 아바타 배경 스타일링도 올바르게 구현되었습니다.

src/app/member/profile/_components/MenuList/MenuList.tsx (1)

13-27: 메뉴 리스트 구조가 잘 설계되었습니다

시멘틱 HTML과 조건부 렌더링을 활용한 메뉴 구현이 깔끔합니다. 키 속성과 스타일 적용도 적절히 처리되었습니다.

src/app/member/profile/_components/Profile/Profile.tsx (1)

31-45: 데이터 렌더링이 안전하게 구현되었습니다

옵셔널 체이닝을 사용하여 안전한 데이터 접근이 구현되었고, 컴포넌트 구조도 깔끔합니다.

src/app/member/profile/_components/MenuList/MenuList.css.ts (1)

9-11: 중첩 패딩 정의 여부 확인 필요
wrapperlist가 모두 상·하 패딩을 지정하고 있어 실제 트리 구조에서 중복 적용될 수 있습니다. 의도된 중첩인지 한쪽만 필요한지 한 번 더 점검해 주세요.

src/app/member/profile/_components/Banner/Banner.css.ts (1)

16-18: 아이콘 색상 지정 방식 점검
SVG 아이콘이 fill 속성을 사용한다면 color 가 적용되지 않을 수 있습니다. 필요 시 fill 또는 stroke 토큰 사용을 검토해 주세요.

src/app/member/profile/_components/Banner/Banner.tsx (2)

27-33: TODO 경로 미정 – 실서비스 노출 위험
href='/' 는 홈으로 이동해 기능이 사라집니다. 릴리즈 전에 반드시 실제 등록 페이지로 교체하거나 disabled 처리해 주세요.


15-15: inline={20} 단위 명확화
Bleed 컴포넌트가 어떤 단위를 기대하는지 확인 필요합니다. 디자인 토큰 기반 spacing 값이면 변수로 치환해 주세요.

{menu.label}
</button>
) : (
<Link href={menu.link!} className={styles.menuItem}>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

안전한 타입 처리 개선

menu.link!에서 non-null assertion 연산자 사용은 런타임 오류를 발생시킬 수 있습니다. 타입 가드나 기본값 사용을 고려해보세요.

-              <Link href={menu.link!} className={styles.menuItem}>
+              <Link href={menu.link || '/'} className={styles.menuItem}>

또는 타입 정의에서 link 속성을 isLogout이 false일 때 필수로 만드는 방법도 있습니다.

🤖 Prompt for AI Agents
In src/app/member/profile/_components/MenuList/MenuList.tsx at line 21, avoid
using the non-null assertion operator on menu.link as it can cause runtime
errors. Instead, add a type guard to check if menu.link exists before using it
or provide a safe default value. Alternatively, adjust the type definitions so
that the link property is required only when isLogout is false, ensuring safer
type handling.

Copy link
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.

수빈님 고생하셨어요! 코멘트 조금 남겨뒀습니다!

Copy link
Member

Choose a reason for hiding this comment

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

P3: 수빈님 요것 피그마 아이콘 이름을 그대로 따라가는 것은 어떨까요?! chevron-right 요런 거로?,,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

34deddc

오!! 좋은거 같습니다 ㅎㅎ 다른 아이콘들도 모두 피그마에 맞게 변경해두었어요!

Copy link
Member

Choose a reason for hiding this comment

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

P3: useSuspenseQuery, suspense, error boundary로 loading, error 처리의 관심을 분리해보는 건 어떨까요?!

Copy link
Contributor Author

@wkdtnqls0506 wkdtnqls0506 Jul 20, 2025

Choose a reason for hiding this comment

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

deaed52

오.. 그렇게 분리하면 컴포넌트에서는 성공한 상태만 관리하면 되니 좋군요..!! 수정했습니다 ~ . ~👍🏻👍🏻

Copy link
Contributor Author

Choose a reason for hiding this comment

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

한가지 궁금한 점이 있습니다 ~ . ~
닉네임과 전화번호만 필요한 데이터라 해당 부분만 스켈레톤 처리가 필요해서 ProfileLayout을 상위에 따로 생성하고 컴포넌트와 Skeleton을 따로 선언해서 사용했는데요!!
일부 데이터만 로딩 처리가 필요한 경우엔 보통 어떤 방식으로 처리하시나용?!

Copy link
Member

Choose a reason for hiding this comment

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

수빈님 하신 것처럼 처리하기도 합니다!

최대한 먼저 보여줄 수 있는 부분은 보여주는 것이 경험에 더 좋아서, 적절히 영역을 나눠서 처리하면 좋아요~! ex) a 데이터 가져온 후 b 데이터 가져와서 a+b를 보여줄 때, a 데이터를 먼저 보여줘도 괜찮으면 그거 먼저 보여주고 b 로딩되면 b도 보여주고,,

이 질문이 맞으실까요?,,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

오 정확히 맞습니다~!~! Profile 전체를 처리할지, 아니면 필요한 부분만 레아이웃으로 분리해서 관리하는지 궁금해서 여쭤봤습니다 ㅎㅎ
감사해요!!🙇🏻‍♀️👍🏻

Comment on lines 38 to 41
{member?.nickname}
</Text>
<Text typo='caption1Md' color='neutral.50'>
{member?.email}
Copy link
Member

Choose a reason for hiding this comment

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

P3: isLoading,isError 처리를 하거나, useSuspenseQuery를 사용하면 data가 optional하지 않다고 판단할 수 있을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

deaed52

여기도 수정했습니다!! 꼼꼼한 확인 매우 감사드립니돠 🙇🏻‍♀️

{menu.label}
</button>
) : (
<Link href={menu.link!} className={styles.menuItem}>
Copy link
Member

Choose a reason for hiding this comment

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

P2: non-null assertion 대신 메뉴의 타입을 조금 명확하게 처리해보는 것은 어떨까요? union타입 사용하거나 다른 방법으로 개선해볼 수 있을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

248e9af

수정했습니다~!🙇🏻‍♀️

Comment on lines 2 to 7
export const MENU_LIST = [
{ id: "notice", label: "공지사항", link: "/" },
{ id: "customer_service", label: "고객센터", link: "/" },
{ id: "guide", label: "잇다 이용가이드", link: "/" },
{ id: "logout", label: "로그아웃", isLogout: true },
];
Copy link
Member

Choose a reason for hiding this comment

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

P2: 래빗 or 아래처럼 개선해보는 것은 어떨까요? 혹은 로그아웃만 따로 렌더링해도 괜찮아요~!

// 1. 메뉴 타입 정의
type LinkMenu = {
  type: 'link';
  id: string;
  label: string;
  link: string;
};
type ActionMenu = {
  type: 'action';
  id: string;
  label: string;
  onClick: () => void;
};

// 2. Union 타입으로 묶기
export type MenuListItem = LinkMenu | ActionMenu;

// 3. 메뉴 리스트 선언
export const MENU_LIST: readonly MenuListItem[] = [
  { type: 'link', id: 'notice', label: '공지사항', link: '/' },
  { type: 'link', id: 'customer_service', label: '고객센터', link: '/' },
  { type: 'link', id: 'guide', label: '잇다 이용가이드', link: '/' },
  {
    type: 'action',
    id: 'logout',
    label: '로그아웃',
    onClick: () => {
      // 로그아웃 로직
      alert('로그아웃!');
    },
  },
] as const;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

248e9af

수정했습니다~!🙇🏻‍♀️

Copy link

@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: 0

🧹 Nitpick comments (2)
src/components/ui/AlertModal/AlertModal.tsx (1)

58-58: 트리거 요소 처리 방식을 개선할 수 있습니다.

현재 trigger가 없으면 AlertDialog.Trigger가 렌더링되지 않아 모달을 열 수 없습니다. 외부에서 AlertDialog.Rootopen 상태를 제어할 수 있도록 하거나, 기본 트리거를 제공하는 것을 고려해보세요.

다음과 같이 openonOpenChange props를 추가하여 외부 상태 제어를 가능하게 할 수 있습니다:

export type AlertModalProps = {
+ /** 모달의 열림/닫힘 상태 */
+ open?: boolean;
+ /** 모달 상태 변경 핸들러 */
+ onOpenChange?: (open: boolean) => void;
  /** 모달의 제목 */
  title?: string;
  // ... 기존 props
};

export const AlertModal = ({
+ open,
+ onOpenChange,
  title,
  trigger,
  content,
  footer,
}: AlertModalProps) => {
  return (
-   <AlertDialog.Root>
+   <AlertDialog.Root open={open} onOpenChange={onOpenChange}>
      {trigger && <AlertDialog.Trigger asChild>{trigger}</AlertDialog.Trigger>}
      // ... 나머지 코드
src/app/member/profile/page.tsx (1)

10-20: Suspense를 활용한 깔끔한 페이지 구조입니다.

Profile 컴포넌트를 Suspense로 감싸고 ProfileSkeleton을 fallback으로 사용하는 구조가 적절합니다. 로딩 상태 처리가 잘 되어있습니다.

향후 에러 상태 처리를 위해 Error Boundary 추가를 고려해보세요:

<Suspense fallback={<ProfileSkeleton />}>
  <ErrorBoundary fallback={<ProfileError />}>
    <Profile />
  </ErrorBoundary>
</Suspense>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 16b63b9 and 20127e5.

⛔ Files ignored due to path filters (5)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/assets/chevron-left.svg is excluded by !**/*.svg
  • src/assets/chevron-right.svg is excluded by !**/*.svg
  • src/assets/circle-close.svg is excluded by !**/*.svg
  • src/assets/logo-wordmark.svg is excluded by !**/*.svg
📒 Files selected for processing (30)
  • package.json (1 hunks)
  • src/app/(auth)/login/page.tsx (2 hunks)
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx (2 hunks)
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx (2 hunks)
  • src/app/member/onboarding/layout.tsx (2 hunks)
  • src/app/member/profile/_components/Banner/Banner.tsx (1 hunks)
  • src/app/member/profile/_components/MenuList/MenuList.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/Profile.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/ProfileLayout.tsx (1 hunks)
  • src/app/member/profile/_components/Profile/ProfileSkeleton.tsx (1 hunks)
  • src/app/member/profile/_constants/index.ts (1 hunks)
  • src/app/member/profile/_constants/menuList.constants.ts (1 hunks)
  • src/app/member/profile/layout.tsx (1 hunks)
  • src/app/member/profile/page.tsx (1 hunks)
  • src/app/member/profile/types/index.ts (1 hunks)
  • src/app/member/profile/types/menuList.types.ts (1 hunks)
  • src/components/ui/AlertModal/AlertModal.css.ts (1 hunks)
  • src/components/ui/AlertModal/AlertModal.stories.tsx (1 hunks)
  • src/components/ui/AlertModal/AlertModal.tsx (1 hunks)
  • src/components/ui/AlertModal/index.ts (1 hunks)
  • src/components/ui/GNB/GNB.css.ts (2 hunks)
  • src/components/ui/GNB/GNB.stories.tsx (6 hunks)
  • src/components/ui/Skeleton/Skeleton.css.ts (1 hunks)
  • src/components/ui/Skeleton/Skeleton.stories.tsx (1 hunks)
  • src/components/ui/Skeleton/Skeleton.tsx (1 hunks)
  • src/components/ui/Skeleton/index.ts (1 hunks)
  • src/components/ui/TextButton/TextButton.stories.tsx (2 hunks)
  • src/components/ui/TextField/TextField.stories.tsx (2 hunks)
  • src/styles/reset.css.ts (1 hunks)
  • src/styles/zIndex.css.ts (1 hunks)
✅ Files skipped from review due to trivial changes (22)
  • src/components/ui/Skeleton/index.ts
  • src/app/member/profile/types/index.ts
  • src/app/(auth)/login/page.tsx
  • src/app/member/profile/_constants/index.ts
  • package.json
  • src/components/ui/GNB/GNB.stories.tsx
  • src/components/ui/TextButton/TextButton.stories.tsx
  • src/components/ui/Skeleton/Skeleton.css.ts
  • src/app/member/profile/_components/Profile/ProfileSkeleton.tsx
  • src/components/ui/AlertModal/index.ts
  • src/app/member/profile/_components/Profile/ProfileLayout.tsx
  • src/components/ui/GNB/GNB.css.ts
  • src/styles/zIndex.css.ts
  • src/styles/reset.css.ts
  • src/app/member/onboarding/_components/NicknameStep/NicknameStep.tsx
  • src/components/ui/TextField/TextField.stories.tsx
  • src/app/member/onboarding/_components/PhoneNumberStep/PhoneNumberStep.tsx
  • src/app/member/profile/types/menuList.types.ts
  • src/components/ui/Skeleton/Skeleton.stories.tsx
  • src/components/ui/AlertModal/AlertModal.css.ts
  • src/components/ui/AlertModal/AlertModal.stories.tsx
  • src/app/member/onboarding/layout.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/app/member/profile/_constants/menuList.constants.ts
  • src/app/member/profile/layout.tsx
  • src/app/member/profile/_components/MenuList/MenuList.tsx
  • src/app/member/profile/_components/Banner/Banner.tsx
🧰 Additional context used
📓 Path-based instructions (2)
{src/app/**/_components/**/*.tsx,src/components/**/*.tsx}

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

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

Instructions used from:

Sources:
📄 CodeRabbit Inference Engine

  • .cursor/rules/nextjs-folder-structure.mdc
🧬 Code Graph Analysis (2)
src/app/member/profile/page.tsx (2)
src/app/member/profile/_components/Profile/ProfileSkeleton.tsx (1)
  • ProfileSkeleton (6-13)
src/app/member/profile/_components/Profile/Profile.tsx (1)
  • Profile (10-23)
src/components/ui/Skeleton/Skeleton.tsx (1)
src/lib/utils/coerceCssRemValue.ts (1)
  • coerceCssRemValue (12-14)
⏰ 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 (6)
src/components/ui/AlertModal/AlertModal.tsx (4)

1-4: 임포트 구조가 깔끔하게 구성되어 있습니다.

Radix UI와 React 타입, 그리고 스타일 파일을 논리적으로 그룹화하여 임포트했습니다. 명명 규칙도 적절합니다.


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

각 props에 대한 한국어 JSDoc 주석이 명확하고, footer prop에 대한 사용 예시가 특히 도움이 됩니다. 모든 props가 optional로 설정되어 유연한 사용이 가능합니다.


32-49: JSDoc 예제가 매우 유용합니다.

실제 사용 예시를 통해 컴포넌트 사용법을 명확히 보여주고 있습니다. 다만 예제의 Button 컴포넌트가 실제 프로젝트의 Button 컴포넌트와 일치하는지 확인이 필요할 수 있습니다.


50-75: 컴포넌트 구현이 올바르게 되어 있습니다.

Radix UI AlertDialog의 패턴을 정확히 따르고 있으며, 조건부 렌더링도 적절합니다. 컴포넌트 구조가 논리적이고 접근성도 고려되어 있습니다.

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

1-44: 잘 구현된 재사용 가능한 Skeleton 컴포넌트입니다.

TypeScript 타입 정의, JSDoc 문서화, vanilla-extract를 활용한 동적 스타일링이 모두 적절히 구현되어 있습니다. coerceCssRemValue 유틸리티를 통한 CSS 값 변환도 깔끔하게 처리되었습니다.

src/app/member/profile/_components/Profile/Profile.tsx (1)

1-24: useSuspenseQuery를 올바르게 활용한 구현입니다.

Suspense와 함께 사용되어 data가 optional하지 않다는 점이 잘 처리되었습니다. 이전 리뷰 코멘트에서 제기된 로딩/에러 처리에 대한 관심사 분리도 Suspense 패턴으로 적절히 해결되었습니다.

Copy link
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 ee8d451 into main Jul 20, 2025
6 checks passed
@wkdtnqls0506 wkdtnqls0506 deleted the feature/PRODUCT-55 branch July 20, 2025 14:53
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-55] 마이페이지 UI 구현

2 participants