Skip to content

feat(web): migrate BottomSheet (#55)#73

Merged
widse merged 2 commits into
mainfrom
feat/#55
Oct 6, 2025
Merged

feat(web): migrate BottomSheet (#55)#73
widse merged 2 commits into
mainfrom
feat/#55

Conversation

@widse

@widse widse commented Oct 3, 2025

Copy link
Copy Markdown
Member

📝 PR 유형

  • 🚀 feature 기능 추가
  • 🐞 버그 발생
  • 🔨 리팩토링
  • 📋 문서작성
  • 🌍 빌드 설정 및 문제
  • ETC

📝 PR 설명

BottomSheet 컴포넌트 마이그레이션 작업입니다.

  • BottomSheet headless ui component lib vaul을 추가합니다.
  • Overlay UI 컴포넌트 와 함께 사용할 use-overlay를 추가합니다.

관련된 이슈 넘버

close #55

✅ 작업 목록

  • add vaul
  • add @toss/use-overlay
  • BottomSheet migration

MR하기 전에 확인해주세요

  • local code lint 검사를 진행하셨나요?
  • loca ci test를 진행하셨나요?

📚 논의사항

📚 ETC

Summary by CodeRabbit

  • 새 기능

    • BottomSheet 컴포넌트를 추가했습니다. 잠금, 핸들 표시/드래그, 핸들 전용 모드, 모서리 반경, 라이트/다크 테마, z-index, 오버레이를 지원하며 스크롤/비스크롤 콘텐츠를 처리합니다.
  • 문서

    • Storybook에 BottomSheet 데모를 추가했습니다. 스크롤/비스크롤 변형과 인터랙티브 컨트롤을 제공합니다.
  • 작업(Chores)

    • UI/오버레이 관련 의존성을 업데이트하고 중복 항목을 정리했습니다.

@widse widse requested a review from seungdeok October 3, 2025 01:28
@coderabbitai

coderabbitai Bot commented Oct 3, 2025

Copy link
Copy Markdown

Walkthrough

앱 웹 패키지의 UI/overlay 관련 의존성을 정리·추가하고, Vaul 기반의 새로운 BottomSheet 컴포넌트(타입·스타일·인덱스 재노출 포함)와 Storybook 스토리(스크롤/비스크롤 예시)를 추가했습니다.

Changes

Cohort / File(s) Summary
Dependencies
apps/web/package.json
런타임 의존성 추가/정리: vaul, overlay-kit, vaul 등 추가 및 @radix-ui/react-dropdown-menu, @radix-ui/react-radio-group 중복 제거/정리.
BottomSheet 컴포넌트
apps/web/src/shared/components/BottomSheet/BottomSheet.tsx, apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts, apps/web/src/shared/components/BottomSheet/BottomSheet.style.ts, apps/web/src/shared/components/BottomSheet/index.ts
Vaul Drawer 기반 BottomSheet 신규 구현: props/타입 정의, 스타일(cva 변형: radius/theme), Overlay/Portal로 콘텐츠 렌더링, 핸들/잠금/테마/z-index 등 옵션 지원. 인덱스에서 컴포넌트와 타입 재노출.
Storybook
apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx
Storybook 스토리 추가: @toss/use-overlay 사용해 두 가지 변형(스크롤/비스크롤)으로 BottomSheet 열기 구현, 메타·컨트롤·데코레이터 설정.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Story as Storybook UI
  participant Overlay as useOverlay
  participant BS as BottomSheet
  participant Vaul as Vaul Drawer

  User->>Story: 버튼 클릭 (Open)
  Story->>Overlay: overlay.open(render = <BottomSheet...>)
  Overlay-->>Story: Portal에 렌더링 지시
  Story->>BS: props(open, onClose, theme, radius, ...)
  BS->>Vaul: Drawer.Root(open, modal)
  Vaul->>BS: Overlay/Content 마운트
  User->>BS: 인터랙션(스와이프/닫기)
  BS->>Vaul: onOpenChange(false)
  Vaul-->>BS: 상태 변경 반영
  BS-->>Story: onClose 콜백 실행
  Note right of BS: showHandle/handleOnly/locked에 따라 동작 제약 적용
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • seungdeok

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning 패키지 종속성에 BottomSheet 이관과 무관한 @radix-ui/react-dropdown-menu@radix-ui/react-radio-group가 추가되어 이슈 #55의 범위를 벗어난 변경이 포함되었습니다. 사용되지 않는 Radix UI 패키지의 추가 이유를 명확히 하거나 해당 의존성을 제거하여 PR 범위를 BottomSheet 마이그레이션으로 제한하십시오.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed 제목이 BottomSheet 마이그레이션이라는 핵심 변경을 명확하게 요약하고 있어 관련성이 높고 간결합니다.
Linked Issues Check ✅ Passed 이 PR은 이슈 #55의 BottomSheet 컴포넌트 이관 목표를 충실히 구현하여 vaul 라이브러리와 Overlay(UI) 훅을 추가하고 컴포넌트, 스타일, 타입, 스토리, 재수출을 포함한 전체 마이그레이션을 완료했습니다.
Description Check ✅ Passed 설명은 템플릿의 필수 섹션(유형, 설명, 이슈, 작업 목록, 사전 확인 리스트)을 모두 채우고 있어 요구사항을 충족합니다.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#55

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Oct 3, 2025

Copy link
Copy Markdown

📚 Storybook is ready for review!
🔗 Preview: https://68ceb72871701a8b3beb1d8f-yzbuxfsweo.chromatic.com/

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
apps/web/src/shared/components/BottomSheet/BottomSheet.tsx (1)

29-38: boolean을 문자열로 변환하는 패턴 개선 제안

showHandle boolean을 String()으로 변환하여 SwitchCase에 전달하는 것보다, 단순 조건부 렌더링이 더 명확합니다.

다음과 같이 리팩토링을 고려해보세요:

-          <SwitchCase
-            value={String(showHandle)}
-            caseBy={{
-              true: (
-                <div className={styles.drawerHandle}>
-                  <Drawer.Handle style={{ width: 36 }} />
-                </div>
-              ),
-            }}
-          />
+          {showHandle && (
+            <div className={styles.drawerHandle}>
+              <Drawer.Handle style={{ width: 36 }} />
+            </div>
+          )}
apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx (1)

65-66: eslint-disable 규칙을 구체적으로 명시

eslint-disable 주석이 너무 광범위합니다. 특정 규칙만 비활성화하는 것이 좋습니다.

다음과 같이 개선하세요:

-    // eslint-disable-next-line
+    // eslint-disable-next-line react-hooks/rules-of-hooks
     const overlay = useOverlay();
apps/web/src/shared/components/BottomSheet/BottomSheet.style.ts (2)

29-30: !important 사용 검토

!important는 CSS 특정성(specificity) 문제를 우회하는 방법이지만, 장기적으로 유지보수를 어렵게 만듭니다. vaul의 기본 스타일과 충돌이 있는지 확인하고, 가능하면 더 구체적인 셀렉터로 해결하는 것이 좋습니다.


4-11: 하드코딩된 디자인 토큰

색상 및 spacing 값이 하드코딩되어 있습니다. 디자인 시스템이나 테마 토큰을 사용하면 일관성 있고 유지보수하기 쉬운 코드가 됩니다.

예시:

import { tokens } from "../../../../styled-system/tokens";

const SEED_PALETTE_COLOR_BASE_100 = tokens.colors.base100;
// ...
apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts (1)

1-11: 상수 네이밍 일관성

BottomSheetRadius는 대문자 키(NONE, SMALL, MEDIUM, LARGE)를 사용하는 반면, BottomSheetTheme는 소문자 키(light, dark)를 사용하여 일관성이 부족합니다.

다음과 같이 통일하는 것을 권장합니다:

 export const BottomSheetTheme = {
-  light: "light",
-  dark: "dark",
+  LIGHT: "light",
+  DARK: "dark",
 } as const;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4273c18 and af22b22.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • apps/web/package.json (1 hunks)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx (1 hunks)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.style.ts (1 hunks)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.tsx (1 hunks)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts (1 hunks)
  • apps/web/src/shared/components/BottomSheet/index.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts (1)
apps/web/src/shared/components/BottomSheet/index.ts (1)
  • BottomSheetProps (2-2)
apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx (2)
apps/web/src/shared/components/BottomSheet/BottomSheet.tsx (1)
  • BottomSheet (8-44)
apps/web/src/shared/components/BottomSheet/BottomSheet.style.ts (1)
  • overlay (13-17)
apps/web/src/shared/components/BottomSheet/BottomSheet.tsx (2)
apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts (1)
  • BottomSheetProps (17-28)
apps/web/src/shared/utils/SwitchCase.tsx (1)
  • SwitchCase (9-15)
⏰ 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: Deploy Storybook to Chromatic
🔇 Additional comments (4)
apps/web/src/shared/components/BottomSheet/index.ts (1)

1-2: LGTM!

표준 배럴(barrel) 패턴으로 컴포넌트와 타입을 재노출하고 있으며, 구현이 적절합니다.

apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx (1)

43-57: LGTM!

스크롤 가능/불가능한 두 가지 콘텐츠 예시가 명확하게 구현되어 있어 BottomSheet의 다양한 사용 사례를 잘 보여줍니다.

apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts (1)

17-28: LGTM!

BottomSheetProps 인터페이스가 명확하게 정의되어 있으며, 모든 필수 prop과 선택적 prop이 적절하게 타이핑되어 있습니다.

apps/web/src/shared/components/BottomSheet/BottomSheet.tsx (1)

21-21: vaul Drawer.Root prop명 검증 필요
vaul의 Drawer.Root가 공식적으로 onOpenChange를 사용하는지 문서를 확인하고, 필요 시 onCloseonOpenChange로 교체하세요.

Comment thread apps/web/package.json Outdated
Comment thread apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx Outdated
variants: {
radius: {
none: {
borderTop: SEED_SPACING_100,

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 | 🔴 Critical

CSS 속성 오류

borderTop: SEED_SPACING_100은 유효하지 않은 CSS입니다. borderTopwidth style color 형식이 필요하며, spacing 값만으로는 작동하지 않습니다. borderRadius: SEED_SPACING_100을 의도한 것으로 보입니다.

다음과 같이 수정하세요:

       none: {
-        borderTop: SEED_SPACING_100,
+        borderRadius: SEED_SPACING_100,
       },
📝 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
borderTop: SEED_SPACING_100,
none: {
borderRadius: SEED_SPACING_100,
},
🤖 Prompt for AI Agents
In apps/web/src/shared/components/BottomSheet/BottomSheet.style.ts around line
36, the property borderTop: SEED_SPACING_100 is invalid CSS (borderTop requires
width/style/color) — replace it with borderRadius: SEED_SPACING_100 (or if you
intended a top border, use borderTopWidth, borderTopStyle and borderTopColor) so
the style uses a valid CSS property and value.

handleOnly = false,
radius,
theme = "light",
zIndex = 1,

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 | 🟡 Minor

zIndex 기본값이 낮을 수 있음

오버레이 컴포넌트의 zIndex 기본값이 1로 설정되어 있는데, 일반적으로 모달/바텀시트는 1000 이상의 높은 z-index를 사용합니다. 다른 UI 요소에 가려질 위험이 있습니다.

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

-  zIndex = 1,
+  zIndex = 1000,
📝 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
zIndex = 1,
zIndex = 1000,
🤖 Prompt for AI Agents
In apps/web/src/shared/components/BottomSheet/BottomSheet.tsx around line 16,
the zIndex default is set to 1 which is too low for overlays; update the
component to use a much higher default (e.g., zIndex = 1000) so the bottom
sheet/modal appears above other UI, and ensure any TypeScript prop signature or
defaultProps are adjusted accordingly (and update any Storybook/docs or tests
that rely on the old default).

@seungdeok seungdeok left a comment

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.

배포를 막고자하는 리뷰는 없습니다🙏

Comment thread apps/web/package.json Outdated
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@tanstack/react-query": "^5.80.7",
"@tanstack/react-query-devtools": "^5.80.7",
"@toss/use-overlay": "^1.4.2",

@seungdeok seungdeok Oct 3, 2025

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.

https://www.npmjs.com/package/@toss/use-overlay?activeTab=versions
는 deprecated된 것으로 보이는데요!
overlaykit(https://github.com/toss/overlay-kit) 이 아니라 해당 패키지를 사용한 이유가 있으신지 궁금합니다
🙏

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@seungdeok
잘못사용했었네요...!!! 수정해서 올렸습니다! 자료 감사합니다 :)

Comment on lines +17 to +28
export interface BottomSheetProps {
open?: boolean;
onClose?: () => void;
locked?: boolean;
showHandle?: boolean;
handleOnly?: boolean;
zIndex?: number;
radius?: BottomSheetRadius;
theme?: BottomSheetTheme;
className?: string;
content?: React.ReactNode;
}

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.

Interface가 radix를 따른 것으로 보이긴한데요
props 설명을 알 수 있으면 사용하는 측면에서 더 좋을 것 같습니다!(ex. @description과 같은 주석)
예를 들어서 handleOnly와 같은 props가 직관적으로 어떤 쓰임인지 알기 힘들 것 같습니다🥺

@github-actions

github-actions Bot commented Oct 6, 2025

Copy link
Copy Markdown

📚 Storybook is ready for review!
🔗 Preview: https://68ceb72871701a8b3beb1d8f-nkocnnvihp.chromatic.com/

@widse widse merged commit 9115777 into main Oct 6, 2025
3 of 4 checks passed
@widse widse deleted the feat/#55 branch October 6, 2025 20:04

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af22b22 and a428be3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • apps/web/package.json (1 hunks)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx (1 hunks)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/shared/components/BottomSheet/BottomSheet.type.ts
  • apps/web/package.json
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx (2)
apps/web/src/shared/components/BottomSheet/BottomSheet.tsx (1)
  • BottomSheet (8-44)
apps/web/src/shared/components/BottomSheet/BottomSheet.style.ts (1)
  • overlay (13-17)

Comment on lines +65 to +74
overlay.open(({ isOpen, close }) => (
<BottomSheet
{...args}
open={isOpen}
onClose={() => {
close();
}}
content={<BottomSheetNoScroll />}
/>
));

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 | 🟠 Major

overlay 정리 콜백 누락으로 인한 잠재적 메모리 누수

overlay.open 콜백에서 unmount 파라미터를 사용하지 않고 있습니다. learnings에 따르면, overlay-kit의 콜백은 { isOpen, close, unmount }를 제공하며, unmount는 오버레이가 완전히 종료/언마운트될 때 호출되어야 합니다. 이를 누락하면 오버레이 인스턴스가 메모리에서 제대로 정리되지 않을 수 있습니다.

Based on learnings.

다음 diff를 적용하여 unmount 콜백을 추가하세요:

     const openNoScrollBottomSheet = () => {
-      overlay.open(({ isOpen, close }) => (
+      overlay.open(({ isOpen, close, unmount }) => (
         <BottomSheet
           {...args}
           open={isOpen}
           onClose={() => {
             close();
+            unmount();
           }}
           content={<BottomSheetNoScroll />}
         />
       ));
     };

     const openScrollBottomSheet = () => {
-      overlay.open(({ isOpen, close }) => (
+      overlay.open(({ isOpen, close, unmount }) => (
         <BottomSheet
           {...args}
           open={isOpen}
           onClose={() => {
             close();
+            unmount();
           }}
           content={<BottomSheetScroll />}
         />
       ));
     };

참고: BottomSheet 컴포넌트의 onClose prop이 Drawer.RootonClose와 어떻게 연결되는지 확인하고, Drawer가 완전히 언마운트된 후 unmount가 호출되도록 타이밍을 조정해야 할 수 있습니다.

Also applies to: 78-87

🤖 Prompt for AI Agents
In apps/web/src/shared/components/BottomSheet/BottomSheet.stories.tsx around
lines 65-74 (and likewise 78-87), the overlay.open callback does not accept or
call the provided unmount parameter which can cause overlay instances to not be
fully cleaned up; update the overlay.open callbacks to accept ({ isOpen, close,
unmount }) and ensure unmount() is invoked after the BottomSheet/Drawer has
fully closed (e.g., call close(), then trigger unmount in the BottomSheet
onClose completion or via a post-close callback/animation end so Drawer has time
to unmount before calling unmount()).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BottomSheet] fireeren-component to Time-align Code 이관

2 participants