Skip to content

refactor: BottomSheet Compound 패턴 적용 (#93)#94

Merged
wkdtnqls0506 merged 6 commits intomainfrom
refactor/PRODUCT-205
Jul 26, 2025
Merged

refactor: BottomSheet Compound 패턴 적용 (#93)#94
wkdtnqls0506 merged 6 commits intomainfrom
refactor/PRODUCT-205

Conversation

@wkdtnqls0506
Copy link
Contributor

@wkdtnqls0506 wkdtnqls0506 commented Jul 25, 2025

✅ 이슈 번호

close #93


🪄 작업 내용 (변경 사항)

  • typography 토큰값 변경
  • 기존 BottomSheet 컴포넌트 Compound 패턴으로 분리
  • TextField 컴포넌트 PolymorphicComponent 적용

📸 스크린샷


💡 설명

1. typography 토큰값 변경 및 수정

  • 피그마 디자인 시스템 변경에 따라 label1, label2 네이밍 및 굵기 값을 수정했습니다.
  • 기존 label 관련 토큰을 사용하고 있다면 꼬옥 확인해주세요 🙇🏻‍♀️

2. BottomSheet 컴포넌트 Compound 패턴으로 분리

  • 준환님 말씀대로 역시나! 여러 케이스를 한가지 스타일로만 대응하기엔 한계가 있었습니다~!
  • BottomSheet.Root / Trigger / Content / Title / Body / Footer 형태로 분리하여 사용할 수 있도록 리팩토링했습니다.
    • 사용 예시를 BottomSheet.tsx에 간단히 정리해두었습니다 ~ . ~

3. TextField 컴포넌트 PolymorphicComponent 적용

  • TextField 컴포넌트가 input뿐만 아니라 textarea까지 디자인이 동일하게 확장되고 있어서, ElementType을 지원할 수 있도록 변경했습니다~!

🗣️ 리뷰어에게 전달 사항

  • 수정 사항 있으면 얘기주십셔 🍀
  • 커밋별로 보시는걸 추천드립니다~!~

📍 트러블 슈팅

Summary by CodeRabbit

  • 신규 기능

    • BottomSheet UI 컴포넌트가 Root, Trigger, Content, Title, Body, Footer 등 하위 컴포넌트로 분리되어 조합형 패턴으로 개선되었습니다.
    • BottomSheet 하위 컴포넌트들이 직접 import 가능해졌습니다.
    • TextField 컴포넌트가 다양한 HTML 요소로 렌더링될 수 있도록 폴리모픽 렌더링을 지원합니다.
  • 버그 수정

    • 일부 스타일에서 고정 뷰포트 높이 단위를 동적 뷰포트 높이 단위(100vh → 100dvh)로 변경하여 모바일 환경에서 레이아웃 문제를 개선했습니다.
  • 스타일

    • 버튼, 텍스트 버튼, 텍스트 필드 등에서 라벨 폰트 스타일이 세분화되어 가독성이 향상되었습니다.
    • BottomSheet, 버튼 등 주요 UI 컴포넌트의 스타일이 개선되었습니다.
    • CSS 리셋에 appearance 속성이 추가되어 브라우저 기본 버튼 스타일이 제거되었습니다.
  • 리팩터

    • BottomSheet 및 TextField 컴포넌트 구조가 개선되어 유지보수성과 확장성이 향상되었습니다.
  • 문서화

    • Storybook 예제가 새로운 BottomSheet 구조에 맞게 업데이트되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jul 25, 2025

Walkthrough

이 변경사항은 BottomSheet UI 컴포넌트의 Compound 패턴 도입을 중심으로, 관련 스토리북, 스타일, 타입 정의, 그리고 일부 타이포그래피 시스템을 전반적으로 리팩토링합니다. 추가적으로 여러 UI 컴포넌트의 폰트 스타일, 뷰포트 단위, 버튼 및 텍스트필드 스타일이 세분화되고 개선되었습니다.

Changes

파일/경로 요약 변경 내용 요약
src/components/ui/BottomSheet/BottomSheet.tsx
src/components/ui/BottomSheet/BottomSheetBody.tsx
src/components/ui/BottomSheet/BottomSheetContent.tsx
src/components/ui/BottomSheet/BottomSheetFooter.tsx
src/components/ui/BottomSheet/BottomSheetRoot.tsx
src/components/ui/BottomSheet/BottomSheetTitle.tsx
src/components/ui/BottomSheet/BottomSheetTrigger.tsx
BottomSheet를 Compound 패턴으로 리팩토링: Root, Trigger, Content, Title, Body, Footer 등 서브컴포넌트 분리 및 도입. 기존 단일 컴포넌트 및 props 타입 제거.
src/components/ui/BottomSheet/BottomSheet.stories.tsx 스토리북 예제를 Compound 패턴 구조에 맞게 전면 리팩토링. Wrapper 컴포넌트, props, 사용 방식 변경.
src/components/ui/BottomSheet/BottomSheet.css.ts BottomSheet 스타일 구조 및 z-index, 레이아웃, 핸들 스타일 등 전반적 수정 및 일부 스타일 제거.
src/components/ui/BottomSheet/index.ts BottomSheet 관련 서브 컴포넌트들 개별 export 추가.
src/styles/typography.css.ts label1, label2 타이포그래피를 label1Sb, label1Md, label1Rg 및 label2Sb, label2Md, label2Rg로 세분화.
src/components/ui/Button/Button.css.ts
src/components/ui/TextButton/TextButton.css.ts
src/components/ui/TextField/TextField.css.ts
버튼, 텍스트버튼, 텍스트필드 등에서 타이포그래피 스타일을 새로운 세분화 스타일로 교체.
src/components/ui/TextField/TextField.tsx TextField를 폴리모픽(Polymorphic) 컴포넌트로 리팩토링, as prop 및 타입 제네릭 적용, label prop 타입 확장.
src/components/ui/TextField/index.ts TextFieldProps 타입 export 추가.
src/app/(auth)/login/_styles/Login.css.ts
src/app/member/onboarding/layout.css.ts
wrapper 스타일의 height 단위를 100vh에서 100dvh로 변경.
src/app/member/profile/_components/Banner/Banner.tsx 텍스트 타이포그래피 스타일 label2 → label2Sb로 변경.
src/styles/reset.css.ts button 리셋 스타일에 appearance 관련 속성(Webkit, Moz, 표준) 추가.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BottomSheet.Root
  participant BottomSheet.Trigger
  participant BottomSheet.Content
  participant BottomSheet.Title
  participant BottomSheet.Body
  participant BottomSheet.Footer

  User->>BottomSheet.Trigger: 트리거 클릭
  BottomSheet.Trigger->>BottomSheet.Root: open 상태 변경
  BottomSheet.Root->>BottomSheet.Content: Portal/Overlay 렌더
  BottomSheet.Content->>BottomSheet.Title: 제목 영역 렌더
  BottomSheet.Content->>BottomSheet.Body: 본문 영역 렌더
  BottomSheet.Content->>BottomSheet.Footer: 푸터(버튼 등) 렌더
  User->>BottomSheet.Footer: 액션(확인/닫기 등)
  BottomSheet.Footer->>BottomSheet.Root: 상태 변경 및 닫힘
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

Poem

🐰
바닥에서 솟아난 시트의 춤,
컴파운드 패턴으로 변신한 숨,
타이포그래피도 세분화되어
버튼과 입력창이 새 옷을 입었네!
리팩토링의 바람 따라
토끼는 오늘도 깡충,
더 깔끔해진 UI에 미소를 짓네.
🩰✨

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/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/(auth)/login/_styles/Login.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/components/ui/BottomSheet/BottomSheet.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.

  • 17 others

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/PRODUCT-205

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 generate unit tests to generate unit tests for 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

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

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: 3

🧹 Nitpick comments (6)
src/components/ui/BottomSheet/BottomSheetContent.tsx (1)

22-26: className 처리 로직을 개선할 수 있습니다.

현재 조건부 className 연결 방식이 작동하지만, 더 간결하게 개선할 수 있습니다.

다음과 같이 템플릿 리터럴을 사용하여 개선할 수 있습니다:

-          className={
-            className
-              ? `${styles.innerContent} ${className}`
-              : styles.innerContent
-          }
+          className={`${styles.innerContent}${className ? ` ${className}` : ''}`}

또는 유틸리티 함수를 사용하는 것도 좋습니다:

+import { cn } from "@/lib/utils"; // 클래스명 유틸리티가 있다면

-          className={
-            className
-              ? `${styles.innerContent} ${className}`
-              : styles.innerContent
-          }
+          className={cn(styles.innerContent, className)}
src/components/ui/BottomSheet/BottomSheetBody.tsx (1)

15-17: className 처리 로직의 일관성 개선을 권장합니다.

BottomSheetContent와 동일한 className 처리 패턴을 사용하고 있습니다. 일관성을 위해 동일한 개선 방법을 적용하는 것이 좋습니다.

템플릿 리터럴을 사용하여 개선:

-      className={
-        className ? `${styles.sheetBody} ${className}` : styles.sheetBody
-      }
+      className={`${styles.sheetBody}${className ? ` ${className}` : ''}`}

또는 유틸리티 함수 사용:

+import { cn } from "@/lib/utils";

-      className={
-        className ? `${styles.sheetBody} ${className}` : styles.sheetBody
-      }
+      className={cn(styles.sheetBody, className)}
src/components/ui/BottomSheet/BottomSheetTitle.tsx (1)

20-24: 클래스 병합 로직 간소화 가능

문자열 보간 대신 clsx 같은 유틸을 사용하면 가독성과 조건부 클래스 관리가 개선됩니다.

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

33-39: 색상 토큰 미사용

핸들 색상이 #D9D9D9로 하드코딩돼 있습니다. 디자인 토큰(colors/semantic)을 사용해 일관성을 유지해 주세요.

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

72-79: 객체 리터럴에 as const 지정 권장

as const를 붙이면 IDE에서 서브컴포넌트 이름 자동완성이 가능해집니다. 런타임에는 변화가 없으니 타입 품질만 향상됩니다.

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

48-50: 열기 상태 관리 중복

Trigger가 자체적으로 Drawer 상태를 토글하는데, onClick으로 setIsOpen(true)를 또 호출하고 있습니다. 한쪽만 사용해 로직을 단순화하세요.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4662e18 and 644c830.

⛔ Files ignored due to path filters (1)
  • src/assets/logo-wordmark.svg is excluded by !**/*.svg
📒 Files selected for processing (20)
  • src/app/(auth)/login/_styles/Login.css.ts (2 hunks)
  • src/app/member/onboarding/layout.css.ts (1 hunks)
  • src/app/member/profile/_components/Banner/Banner.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheet.css.ts (3 hunks)
  • src/components/ui/BottomSheet/BottomSheet.stories.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheet.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheetBody.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheetContent.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheetFooter.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheetRoot.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheetTitle.tsx (1 hunks)
  • src/components/ui/BottomSheet/BottomSheetTrigger.tsx (1 hunks)
  • src/components/ui/BottomSheet/index.ts (1 hunks)
  • src/components/ui/Button/Button.css.ts (1 hunks)
  • src/components/ui/TextButton/TextButton.css.ts (1 hunks)
  • src/components/ui/TextField/TextField.css.ts (1 hunks)
  • src/components/ui/TextField/TextField.tsx (2 hunks)
  • src/components/ui/TextField/index.ts (1 hunks)
  • src/styles/reset.css.ts (1 hunks)
  • src/styles/typography.css.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

All files must use TypeScript.

Files:

  • src/app/member/onboarding/layout.css.ts
  • src/styles/reset.css.ts
  • src/components/ui/Button/Button.css.ts
  • src/app/(auth)/login/_styles/Login.css.ts
  • src/components/ui/TextField/index.ts
  • src/app/member/profile/_components/Banner/Banner.tsx
  • src/components/ui/TextButton/TextButton.css.ts
  • src/components/ui/BottomSheet/BottomSheetTrigger.tsx
  • src/components/ui/BottomSheet/index.ts
  • src/components/ui/BottomSheet/BottomSheetTitle.tsx
  • src/components/ui/BottomSheet/BottomSheetContent.tsx
  • src/components/ui/BottomSheet/BottomSheetFooter.tsx
  • src/components/ui/BottomSheet/BottomSheetBody.tsx
  • src/components/ui/TextField/TextField.css.ts
  • src/components/ui/BottomSheet/BottomSheet.tsx
  • src/components/ui/BottomSheet/BottomSheetRoot.tsx
  • src/components/ui/TextField/TextField.tsx
  • src/styles/typography.css.ts
  • src/components/ui/BottomSheet/BottomSheet.stories.tsx
  • src/components/ui/BottomSheet/BottomSheet.css.ts
{src/styles/**/*.css.ts,src/app/**/_components/**/*.css.ts,src/components/**/*.css.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Style files must use camelCase naming with the .css.ts extension (e.g., Button.css.ts, theme.css.ts).

Files:

  • src/styles/reset.css.ts
  • src/components/ui/Button/Button.css.ts
  • src/components/ui/TextButton/TextButton.css.ts
  • src/components/ui/TextField/TextField.css.ts
  • src/styles/typography.css.ts
  • src/components/ui/BottomSheet/BottomSheet.css.ts
src/styles/*.css.ts

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Global styles should be placed in the styles/ folder, using vanilla-extract.

Files:

  • src/styles/reset.css.ts
  • src/styles/typography.css.ts
{src/app/**/_components/**/*.tsx,src/components/**/*.tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Component files must use PascalCase naming (e.g., Button.tsx, DomainLayout.tsx).

Files:

  • src/app/member/profile/_components/Banner/Banner.tsx
  • src/components/ui/BottomSheet/BottomSheetTrigger.tsx
  • src/components/ui/BottomSheet/BottomSheetTitle.tsx
  • src/components/ui/BottomSheet/BottomSheetContent.tsx
  • src/components/ui/BottomSheet/BottomSheetFooter.tsx
  • src/components/ui/BottomSheet/BottomSheetBody.tsx
  • src/components/ui/BottomSheet/BottomSheet.tsx
  • src/components/ui/BottomSheet/BottomSheetRoot.tsx
  • src/components/ui/TextField/TextField.tsx
  • src/components/ui/BottomSheet/BottomSheet.stories.tsx
🧬 Code Graph Analysis (6)
src/components/ui/Button/Button.css.ts (1)
src/styles/typography.css.ts (1)
  • typography (3-172)
src/app/(auth)/login/_styles/Login.css.ts (1)
src/app/member/onboarding/layout.css.ts (1)
  • wrapper (3-9)
src/components/ui/TextButton/TextButton.css.ts (1)
src/styles/typography.css.ts (1)
  • typography (3-172)
src/components/ui/TextField/TextField.css.ts (1)
src/styles/typography.css.ts (1)
  • typography (3-172)
src/components/ui/TextField/TextField.tsx (1)
src/types/polymorphic.types.ts (1)
  • PolymorphicComponentPropsWithRef (35-38)
src/components/ui/BottomSheet/BottomSheet.css.ts (2)
src/styles/semantic.css.ts (1)
  • semantic (5-68)
src/styles/zIndex.css.ts (1)
  • zIndex (3-9)
⏰ 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: storybook-preview
🔇 Additional comments (22)
src/styles/reset.css.ts (1)

64-66: 브라우저 간 일관된 버튼 스타일링을 위한 적절한 appearance 리셋

WebKit, Mozilla, 표준 CSS에 대한 appearance 속성을 모두 "none"으로 설정하여 네이티브 브라우저 버튼 스타일을 완전히 제거했습니다. 이는 기존의 패딩, 테두리, 배경 리셋과 함께 크로스 브라우저 일관성을 보장하는 모범 사례입니다.

src/app/(auth)/login/_styles/Login.css.ts (3)

3-3: 디자인 시스템 색상 토큰 사용을 위한 적절한 import 추가

logoIcon에 색상을 적용하기 위해 colors 모듈을 가져온 것이 적절합니다.


7-7: 모바일 기기의 동적 뷰포트 대응을 위한 dvh 단위 사용

100vh에서 100dvh로 변경하여 모바일 브라우저에서 주소창 등으로 인한 뷰포트 크기 변화에 더 적절하게 대응할 수 있습니다. 이는 src/app/member/onboarding/layout.css.ts의 동일한 변경사항과 일관성을 유지합니다.


42-42: 디자인 시스템 색상 토큰을 활용한 로고 색상 지정

colors.redOrange[50]을 사용하여 로고 아이콘의 색상을 명확히 지정한 것이 좋습니다. 디자인 시스템의 색상 토큰을 활용하여 일관된 브랜딩을 유지합니다.

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

112-147: 라벨 타이포그래피 토큰의 체계적인 확장

label1label2를 각각 세 가지 폰트 무게 변형(Semi-bold 600, Medium 500, Regular 400)으로 확장한 것이 훌륭합니다. 이는 Figma 디자인 시스템 변경사항과 일치하며, 다음과 같은 장점을 제공합니다:

  • 일관된 명명 규칙 (Sb, Md, Rg 접미사)
  • 각 그룹 내에서 fontSize, lineHeight, letterSpacing 유지
  • fontWeight만 차별화하여 더 세밀한 타이포그래피 제어 가능

이러한 확장은 디자이너와 개발자에게 더 많은 유연성을 제공하는 모범적인 디자인 시스템 구현입니다.

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

6-6: 일관된 뷰포트 높이 처리를 위한 dvh 단위 적용

로그인 페이지와 동일하게 100vh에서 100dvh로 변경하여 애플리케이션 전반에서 일관된 뷰포트 높이 처리를 보장합니다. 모바일 환경에서 동적 뷰포트 변화에 더 적절하게 대응할 수 있습니다.

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

29-29: 새로운 타이포그래피 토큰 체계에 맞는 적절한 업데이트

label2에서 label2Sb로 변경하여 확장된 타이포그래피 시스템과 일치시켰습니다. 세미볼드 변형을 사용하여 기존 라벨의 시각적 일관성을 유지하면서 새로운 토큰 구조를 적용했습니다.

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

46-46: 타이포그래피 토큰 업데이트가 올바르게 적용되었습니다.

label2에서 label2Sb로 변경하여 작은 버튼에 세미볼드 폰트 웨이트(600)가 적용됩니다. 디자인 시스템 업데이트와 일치하는 변경사항입니다.

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

13-13: 타이포그래피 토큰 업데이트가 올바르게 적용되었습니다.

텍스트 필드 라벨에 label1Sb 스타일을 적용하여 세미볼드 폰트 웨이트가 사용됩니다. 다른 UI 컴포넌트들과 일관성을 유지하는 변경사항입니다.

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

1-1: 타입 내보내기 추가가 적절합니다.

TextFieldProps 타입을 함께 내보내어 외부 모듈에서 컴포넌트의 props 타입을 사용할 수 있도록 합니다. 폴리모픽 컴포넌트 리팩토링과 함께 필요한 변경사항입니다.

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

38-38: 타이포그래피 토큰 업데이트가 적용되었습니다.

label1Md로 변경하여 작은 텍스트 버튼에 미디엄 폰트 웨이트(500)가 적용됩니다. 일반 버튼의 세미볼드와 다른 웨이트를 사용하여 텍스트 버튼의 시각적 차별화를 제공합니다.

src/components/ui/TextField/TextField.tsx (5)

1-3: 폴리모픽 컴포넌트를 위한 import가 올바르게 추가되었습니다.

ElementType, ReactNode, 그리고 PolymorphicComponentPropsWithRef 타입들이 폴리모픽 패턴 구현을 위해 적절히 임포트되었습니다.


9-18: 폴리모픽 props 타입 정의가 잘 구현되었습니다.

제네릭 타입 매개변수 T를 사용하여 다양한 HTML 요소로 렌더링할 수 있도록 하는 올바른 타입 정의입니다. label prop이 string | ReactNode로 확장되어 더 유연한 사용이 가능합니다.


20-28: 컴포넌트 문서화가 명확하고 유용합니다.

폴리모픽 사용법에 대한 명확한 설명과 예제 코드가 포함되어 있어 개발자들이 컴포넌트를 올바르게 사용할 수 있도록 도움을 제공합니다.


29-39: 폴리모픽 컴포넌트 구현이 올바릅니다.

제네릭 함수 컴포넌트로 정의되어 타입 안전성을 보장하고, props destructuring이 적절히 수행되었습니다. Component 변수를 통한 동적 요소 렌더링 패턴이 잘 구현되었습니다.


52-59: 요소 렌더링과 props 처리가 올바르게 구현되었습니다.

동적 Component 렌더링, ref 전달, 클래스명 병합, 그리고 조건부 aria-label 설정이 모두 적절히 처리되었습니다. 특히 label이 문자열일 때만 aria-label을 설정하는 것은 접근성 측면에서 올바른 구현입니다.

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

2-7: 복합 컴포넌트 패턴의 올바른 구현입니다.

새로운 BottomSheet 서브컴포넌트들이 적절히 내보내져 있으며, 복합 컴포넌트 패턴을 올바르게 지원합니다. 기존 BottomSheet 내보내기도 유지되어 하위 호환성을 보장합니다.

src/components/ui/BottomSheet/BottomSheetTrigger.tsx (1)

1-15: 잘 구현된 트리거 컴포넌트입니다.

컴포넌트가 복합 패턴의 일부로 올바르게 구현되었습니다:

  • asChild 프롭을 통한 적절한 컴포지션 패턴 사용
  • TypeScript 타입 정의가 명확함
  • displayName 설정으로 디버깅 지원
  • 단일 책임 원칙을 따름
src/components/ui/BottomSheet/BottomSheetRoot.tsx (1)

1-13: 복합 패턴의 루트 컴포넌트가 올바르게 구현되었습니다.

DialogProps를 확장하여 vaul 라이브러리의 모든 기능을 상속받고, props 스프레딩을 통해 적절히 전달합니다. 타입 정의가 명확하고 래퍼 컴포넌트로서의 역할을 잘 수행합니다.

src/components/ui/BottomSheet/BottomSheetContent.tsx (1)

17-33: Portal과 오버레이 구조가 올바르게 구현되었습니다.

Drawer.Portal, Drawer.Overlay, Drawer.Content의 구조가 vaul 라이브러리의 패턴을 올바르게 따르고 있으며, 복합 컴포넌트 패턴에 적절히 통합되었습니다.

src/components/ui/BottomSheet/BottomSheetBody.tsx (1)

12-22: 바디 컴포넌트가 적절히 구현되었습니다.

단순하고 명확한 컨테이너 역할을 하는 컴포넌트로, 복합 패턴에서 콘텐츠 바디 영역을 담당하는 역할이 잘 정의되어 있습니다.

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

27-29: 고정 오프셋 상수 검증 필요

maxHeight: "calc(100dvh - 52px)"에서 52px은 디자인 변경 시 쉽게 잊힐 수 있는 매직 넘버입니다. 토큰화하거나 주석으로 근거를 명시해 주세요.

Comment on lines +31 to +37
<BottomSheet.Root open={isOpen} onOpenChange={setIsOpen}>
<BottomSheet.Trigger>
<Button variant='primary' onClick={() => setIsOpen(true)}>
바텀시트 열기
</Button>
}
content={args.content}
/>
</BottomSheet.Trigger>

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

중첩 button 발생 가능성

<BottomSheet.Trigger>asChild를 주지 않으면 내부에서 <button>이 생성되어 <Button>(또는 <button>)을 감싸게 됩니다. 이는 불법 중첩 HTML이며 접근성 오류를 유발합니다.

-<BottomSheet.Trigger>
+<BottomSheet.Trigger asChild>
📝 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
<BottomSheet.Root open={isOpen} onOpenChange={setIsOpen}>
<BottomSheet.Trigger>
<Button variant='primary' onClick={() => setIsOpen(true)}>
바텀시트 열기
</Button>
}
content={args.content}
/>
</BottomSheet.Trigger>
<BottomSheet.Root open={isOpen} onOpenChange={setIsOpen}>
<BottomSheet.Trigger asChild>
<Button variant='primary' onClick={() => setIsOpen(true)}>
바텀시트 열기
</Button>
</BottomSheet.Trigger>
🤖 Prompt for AI Agents
In src/components/ui/BottomSheet/BottomSheet.stories.tsx around lines 31 to 37,
the BottomSheet.Trigger component wraps a Button without using the asChild prop,
causing nested button elements which is invalid HTML and harms accessibility. To
fix this, add the asChild prop to BottomSheet.Trigger so it forwards its
behavior to the child Button without creating an extra button element.

Comment on lines +7 to +16
export type BottomSheetFooterProps = {
children: ReactNode;
className?: string;
};

export function BottomSheetFooter({
children,
className,
...props
}: BottomSheetFooterProps) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

HTML 속성 전달을 위한 타입 확장 필요

...props를 전달하면서도 BottomSheetFooterProps에 기본 div 속성이 포함되어 있지 않아, onClick, id 같은 표준 속성을 넘기면 TS 오류가 발생합니다.

-export type BottomSheetFooterProps = {
-  children: ReactNode;
-  className?: string;
-};
+export type BottomSheetFooterProps =
+  React.ComponentPropsWithoutRef<"div"> & {
+    children: ReactNode;
+    className?: string;
+  };
📝 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 type BottomSheetFooterProps = {
children: ReactNode;
className?: string;
};
export function BottomSheetFooter({
children,
className,
...props
}: BottomSheetFooterProps) {
export type BottomSheetFooterProps =
React.ComponentPropsWithoutRef<"div"> & {
children: ReactNode;
className?: string;
};
export function BottomSheetFooter({
children,
className,
...props
}: BottomSheetFooterProps) {
// ...
}
🤖 Prompt for AI Agents
In src/components/ui/BottomSheet/BottomSheetFooter.tsx around lines 7 to 16, the
BottomSheetFooterProps type does not extend the default HTML div element
attributes, causing TypeScript errors when passing standard props like onClick
or id. Fix this by extending React.HTMLAttributes<HTMLDivElement> in
BottomSheetFooterProps to include all standard div properties, allowing ...props
to accept and pass these attributes without type errors.

Comment on lines +8 to +17
export type BottomSheetTitleProps = {
children: ReactNode;
className?: string;
};

export function BottomSheetTitle({
children,
className,
...props
}: BottomSheetTitleProps) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

타입 범위 확장 및 접근성 속성 추가 검토

  1. BottomSheetTitleProps 역시 Drawer.Title에 전달될 HTML 속성을 포함해야 합니다.
  2. 상단 핸들(div)는 순수 장식 요소이므로 aria-hidden="true"를 지정해 스크린리더 노이즈를 줄여주세요.
🤖 Prompt for AI Agents
In src/components/ui/BottomSheet/BottomSheetTitle.tsx around lines 8 to 17,
extend the BottomSheetTitleProps type to include all valid HTML attributes for
the element rendered by Drawer.Title, typically by extending
React.HTMLAttributes or a similar interface. Additionally, add
aria-hidden="true" to the top handle div element to mark it as decorative and
prevent screen readers from announcing it, improving accessibility.

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 63eda85 into main Jul 26, 2025
7 of 9 checks passed
@wkdtnqls0506 wkdtnqls0506 deleted the refactor/PRODUCT-205 branch July 26, 2025 08:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

♻️ refactor 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PRODUCT-205] BottomSheet Compound 패턴 적용

2 participants