Conversation
- @storybook/react-webpack5를 @storybook/react-vite로 교체 - .storybook/main.ts 설정 업데이트 - 경로 별칭(alias) 해결을 위해 vite-tsconfig-paths 추가 - 컴포넌트에 React를 임포트하여 ReferenceError 수정
- Modal.stories.tsx 파일 생성하여 다양한 케이스(기본, 설명 없음, 긴 내용) 추가
- Spinner.stories.tsx 파일 생성 (기본, 커스텀 높이 예시) - height prop이 스타일 컴포넌트로 전달되지 않던 버그 수정
- InputField.stories.tsx 파일 생성 (기본, 라벨 포함, 비밀번호, 에러 상태 등 다양한 케이스 추가)
- 기본 상태, 초기값 설정, 커스텀 플레이스홀더 등 다양한 사용 예시 추가 - 인터랙티브한 테스트를 위해 useState를 활용한 상태 관리 로직 포함
- Compound Component 패턴을 활용한 드롭다운 스토리 구현 - 기본 드롭다운 및 메뉴 위치 조정(Top) 예시 추가 - 제네릭 타입 호환성을 위한 타입 캐스팅 적용
- CustomTextArea.stories.tsx 파일 생성 (기본, 라벨 포함, 에러 상태, 글자수 제한 등)
- Storybook ^8.6이 아직 Vite 7을 지원하지 않아 Vite를 ^6.0.0으로 다운그레이드 - 버전 일관성을 위해 모든 Storybook 관련 패키지를 ^8.6.14로 업그레이드 - 호환되지 않는 피어 의존성으로 인한 npm install 에러 해결
- Vite 6 호환성 확보를 위해 Storybook 10으로 업그레이드 - 더 이상 사용되지 않는 패키지 제거: - @storybook/blocks (v9부터 빈 패키지) - @storybook/test (addon-test로 통합) - @storybook/addon-essentials (통합됨) - @storybook/addon-interactions (통합됨) - @storybook/addon-viewport (통합됨) - @storybook/addon-docs 추가 - @chromatic-com/storybook 3.2.4 → 4.1.3 BREAKING CHANGE: Storybook 10은 ESM-only이며 Node.js 20.16+ 필요
- Storybook 10의 ESM-only 패키지 타입 인식을 위해 필요 - Vite 번들러와의 호환성 개선 - '@storybook/react-vite' 모듈 해석 오류 해결
- Storybook 기본 responsive 뷰로 충분하므로 커스텀 viewport 제거 - Footer, Header 스토리에서 Tablet/Mobile/Desktop 변형 제거 - 각 컴포넌트는 Default 스토리만 유지하여 간소화
- @storybook/addon-viewport 제거 (Storybook 10에서 통합됨) - @storybook/addon-essentials 제거 (통합됨) - @storybook/addon-interactions 제거 (통합됨) - addon 미설치 경고 해결
…tps://github.com/Moadong/moadong into feature/#888-common-components-storybook-MOA-390
- vite 6버전과의 호환성을 위해 업그레이드
…torybook-MOA-390 [feature] 공통 컴포넌트 스토리 파일을 제작한다
- localhost에서 Mixpanel 비활성화 - session_id 파라미터로 사용자 식별 - 프로덕션 호스트네임 체크 로직 제거
- 페이지 방문 및 체류 시간 이벤트에 session_id 파라미터 연동
- 세션 탈취 및 데이터 오염 방지를 위해 Mixpanel 식별 후 URL 세탁 로직 추가
…t-disable-session-id-MOA-481 [feature] Mixpanel 로컬 환경 비활성화 및 세션 ID 연동
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
Storybook 설정 및 Ignore frontend/.gitignore, frontend/.storybook/main.ts, frontend/.storybook/preview.ts |
Storybook을 Webpack→Vite로 전환: 타입 및 framework.name을 @storybook/react-vite로 변경, webpackFinal 제거 및 viteFinal 추가(mergeConfig + vite-tsconfig-paths), 뷰포트 관련 설정 제거. /storybook-static을 .gitignore에 추가. |
의존성 업데이트 frontend/package.json |
Storybook 관련 패키지 집단 업그레이드(≈10.x), 일부 Webpack 전용 애드온 제거, @chromatic-com/storybook 업그레이드, vite/vite-tsconfig-paths 버전 업데이트, eslint-plugin-storybook 버전 업데이트. |
스토리 파일 추가/수정 frontend/src/components/common/... CustomDropDown/CustomDropDown.stories.tsx, CustomTextArea/CustomTextArea.stories.tsx, InputField/InputField.stories.tsx, Modal/Modal.stories.tsx, SearchField/SearchField.stories.tsx, Spinner/Spinner.stories.tsx |
여러 컴포넌트에 대해 새로운 Storybook 스토리 추가(메타, argTypes, 로컬 상태 기반 렌더러, 다양한 변형들). |
기존 스토리 정리 Footer/Footer.stories.tsx, Header/Header.stories.tsx |
Footer의 Tablet, Mobile 스토리 및 Header의 Desktop 스토리 제거(뷰포트별 스토리 삭제). |
컴포넌트 구현 변경 Spinner/Spinner.tsx, Modal/Modal.tsx |
Spinner: height prop을 SpinnerWrapper에 전달하도록 변경(실행 경로에 영향). Modal: JSX 포맷/인용 부호 정규화(동작 불변). |
분석/추적 초기화 및 훅 변경 hooks/useTrackPageView.ts, utils/initSDK.ts, index.tsx |
useTrackPageView: named visibilitychange handler로 변경 및 pageName 의존성 추가. initSDK: 호스트 검사 로직을 localhost 기반으로 변경, Mixpanel 초기화 흐름 변경 및 URL에서 session_id 추출·identify 수행 및 replaceState 처리. index.tsx: initializeChannelService() 호출 제거. |
TypeScript 설정 frontend/tsconfig.json |
compilerOptions.moduleResolution을 "node"에서 "bundler"로 변경. |
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
- [feature] 공통 컴포넌트 스토리 파일을 제작한다 #889 — 동일한 Storybook Webpack→Vite 마이그레이션 및 스토리 추가 변경과 중복됨.
- [feature] Mixpanel 로컬 환경 비활성화 및 세션 ID 연동 #1004 — initSDK, useTrackPageView, index.tsx 관련 분석/초기화 변경과 중첩되는 수정사항 포함.
- [feature] Spinner 컴포넌트 접근성 개선 및 높이 prop 추가 #391 — Spinner height prop 전달 관련 변경과 직접적으로 연관됨.
Suggested labels
⚙ Setting
Suggested reviewers
- lepitaaar
- oesnuj
Pre-merge checks and finishing touches
❌ Failed checks (1 warning, 1 inconclusive)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
|
| Title check | ❓ Inconclusive | PR 제목은 '[release] v1.1.10'으로, 릴리스 버전 업데이트를 명확히 나타내지만, 광범위한 변경 사항(Storybook 마이그레이션, 컴포넌트 스토리 추가, 의존성 업그레이드 등)의 구체적인 내용을 충분히 반영하지 못합니다. | 제목이 버전 번호만 포함하므로, 주요 변경 사항의 성격(예: 'Storybook 마이그레이션' 또는 주요 기능)을 명시하거나, 릴리스 노트와의 일관성을 확인하세요. |
✅ Passed checks (1 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
📜 Recent review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
frontend/src/utils/initSDK.ts
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
frontend/src/components/common/CustomDropDown/CustomDropDown.stories.tsx (1)
81-81:args.options대신 로컬OPTIONS를 사용하는 불일치
selectedLabel계산 시 로컬OPTIONS배열을 사용하고 있지만, 컴포넌트에는args.options를 전달하고 있습니다. Storybook Controls에서options를 변경해도 트리거 레이블이 업데이트되지 않는 불일치가 발생할 수 있습니다.🔎 제안된 수정
- const selectedLabel = OPTIONS.find(opt => opt.value === selected)?.label || '선택하세요'; + const options = args.options as readonly { label: string; value: string }[]; + const selectedLabel = options.find(opt => opt.value === selected)?.label || '선택하세요';frontend/src/components/common/CustomTextArea/CustomTextArea.stories.tsx (1)
59-151: render 함수 중복 코드 추출 고려Default, WithLabel, ErrorState, WithMaxLength 스토리의 render 함수가 거의 동일합니다. 코딩 가이드라인에 따라 중복을 줄이기 위해 공통 render 함수를 추출할 수 있습니다.
🔎 제안된 리팩토링
// 공통 render 함수 추출 const renderWithState = (args: typeof Default.args) => { const [value, setValue] = useState(args?.value || ''); return ( <CustomTextArea {...args} value={value} onChange={(e) => { setValue(e.target.value); args?.onChange?.(e); }} /> ); }; // 각 스토리에서 사용 export const Default: Story = { args: { /* ... */ }, render: renderWithState, };frontend/src/components/common/InputField/InputField.stories.tsx (1)
88-104: render 함수 중복을 추출하여 재사용성을 높이세요.5개의 스토리에서 동일한 render 함수 패턴이 반복됩니다. 헬퍼 함수로 추출하면 유지보수성이 향상됩니다.
🔎 제안된 리팩토링
// 파일 상단에 헬퍼 함수 추가 const createInputFieldRender = (args: typeof InputField extends React.FC<infer P> ? P : never) => { const [value, setValue] = useState(args.value || ''); return ( <InputField {...args} value={value} onChange={(e) => { setValue(e.target.value); args.onChange?.(e); }} onClear={() => { setValue(''); args.onClear?.(); }} /> ); }; // 각 스토리에서 사용 export const Default: Story = { args: { /* ... */ }, render: (args) => createInputFieldRender(args), };Also applies to: 116-132, 145-161, 174-190, 203-219
frontend/src/components/common/Modal/Modal.stories.tsx (1)
53-67: render 함수 중복을 헬퍼로 추출할 수 있습니다.3개의 스토리에서 동일한 모달 열기/닫기 로직이 반복됩니다.
🔎 제안된 리팩토링
// 파일 상단에 헬퍼 컴포넌트 추가 const ModalStoryWrapper = ({ args }: { args: typeof Modal extends React.FC<infer P> ? P : never }) => { const [isOpen, setIsOpen] = useState(args.isOpen); const handleClose = () => { setIsOpen(false); args.onClose(); }; return ( <> <Button onClick={() => setIsOpen(true)}>모달 열기</Button> <Modal {...args} isOpen={isOpen} onClose={handleClose} /> </> ); }; // 각 스토리에서 사용 export const Default: Story = { args: { /* ... */ }, render: (args) => <ModalStoryWrapper args={args} />, };Also applies to: 78-92, 119-133
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (18)
frontend/.gitignorefrontend/.storybook/main.tsfrontend/.storybook/preview.tsfrontend/package.jsonfrontend/src/components/common/CustomDropDown/CustomDropDown.stories.tsxfrontend/src/components/common/CustomTextArea/CustomTextArea.stories.tsxfrontend/src/components/common/Footer/Footer.stories.tsxfrontend/src/components/common/Header/Header.stories.tsxfrontend/src/components/common/InputField/InputField.stories.tsxfrontend/src/components/common/Modal/Modal.stories.tsxfrontend/src/components/common/Modal/Modal.tsxfrontend/src/components/common/SearchField/SearchField.stories.tsxfrontend/src/components/common/Spinner/Spinner.stories.tsxfrontend/src/components/common/Spinner/Spinner.tsxfrontend/src/hooks/useTrackPageView.tsfrontend/src/index.tsxfrontend/src/utils/initSDK.tsfrontend/tsconfig.json
💤 Files with no reviewable changes (4)
- frontend/src/components/common/Footer/Footer.stories.tsx
- frontend/.storybook/preview.ts
- frontend/src/components/common/Header/Header.stories.tsx
- frontend/src/index.tsx
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/components/common/Spinner/Spinner.tsxfrontend/src/hooks/useTrackPageView.tsfrontend/src/utils/initSDK.tsfrontend/src/components/common/InputField/InputField.stories.tsxfrontend/src/components/common/SearchField/SearchField.stories.tsxfrontend/src/components/common/Modal/Modal.stories.tsxfrontend/src/components/common/Spinner/Spinner.stories.tsxfrontend/src/components/common/CustomTextArea/CustomTextArea.stories.tsxfrontend/src/components/common/CustomDropDown/CustomDropDown.stories.tsxfrontend/src/components/common/Modal/Modal.tsx
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/components/common/Spinner/Spinner.tsxfrontend/src/components/common/InputField/InputField.stories.tsxfrontend/src/components/common/SearchField/SearchField.stories.tsxfrontend/src/components/common/Modal/Modal.stories.tsxfrontend/src/components/common/Spinner/Spinner.stories.tsxfrontend/src/components/common/CustomTextArea/CustomTextArea.stories.tsxfrontend/src/components/common/CustomDropDown/CustomDropDown.stories.tsxfrontend/src/components/common/Modal/Modal.tsx
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/components/common/Spinner/Spinner.tsxfrontend/src/hooks/useTrackPageView.tsfrontend/src/utils/initSDK.tsfrontend/src/components/common/InputField/InputField.stories.tsxfrontend/src/components/common/SearchField/SearchField.stories.tsxfrontend/src/components/common/Modal/Modal.stories.tsxfrontend/src/components/common/Spinner/Spinner.stories.tsxfrontend/src/components/common/CustomTextArea/CustomTextArea.stories.tsxfrontend/src/components/common/CustomDropDown/CustomDropDown.stories.tsxfrontend/src/components/common/Modal/Modal.tsx
🧠 Learnings (6)
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Use Component Composition instead of Props Drilling to reduce coupling
Applied to files:
frontend/src/components/common/Spinner/Spinner.tsxfrontend/src/components/common/Modal/Modal.tsx
📚 Learning: 2025-05-09T08:11:51.820Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 388
File: frontend/src/components/common/Spinner/Spinner.tsx:1-31
Timestamp: 2025-05-09T08:11:51.820Z
Learning: 로딩 스피너(Spinner) 컴포넌트에는 웹 접근성을 위해 `role="status"` 및 `aria-label` 같은 ARIA 속성을 추가해야 합니다. 이는 스크린 리더와 같은 보조 기술이 로딩 상태를 사용자에게 적절히 전달할 수 있게 합니다.
Applied to files:
frontend/src/components/common/Spinner/Spinner.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Applied to files:
frontend/src/components/common/InputField/InputField.stories.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/tsconfig.json
📚 Learning: 2025-11-15T08:13:58.854Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 844
File: frontend/package.json:97-97
Timestamp: 2025-11-15T08:13:58.854Z
Learning: In frontend/package.json, the `tough-cookie` package is a dependency of `msw` (Mock Service Worker) used for cookie parsing and management. Both packages should be in devDependencies together.
Applied to files:
frontend/package.json
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/components/common/Modal/Modal.tsx
🧬 Code graph analysis (1)
frontend/src/components/common/CustomDropDown/CustomDropDown.stories.tsx (1)
frontend/src/components/common/CustomDropDown/CustomDropDown.tsx (1)
CustomDropDown(94-115)
🔇 Additional comments (13)
frontend/src/hooks/useTrackPageView.ts (3)
44-50: 메모리 누수 방지를 위한 올바른 리팩토링입니다.익명 함수 대신 명명된 함수
handleVisibilityChange를 사용함으로써addEventListener와removeEventListener에서 동일한 함수 참조를 사용할 수 있게 되었습니다. 이는 이전에 발생할 수 있었던 메모리 누수 문제를 해결합니다.
55-55: 이벤트 리스너 정리가 올바르게 구현되었습니다.명명된 함수 참조를 사용하여 이벤트 리스너가 effect 정리 시 제대로 제거됩니다.
57-57: 의존성 배열에pageName추가가 올바릅니다.
pageName은 effect 내부에서 사용되므로(23, 35번째 줄) 의존성 배열에 포함되어야 합니다. 이 변경으로pageName이 변경될 때 트래킹이 올바르게 재초기화됩니다.frontend/src/components/common/Spinner/Spinner.tsx (1)
36-40: LGTM! height prop이 이제 올바르게 전달됩니다.
SpinnerWrapper에heightprop을 전달하도록 수정된 것이 좋습니다. 이전에는SpinnerProps에height가 정의되어 있었지만 실제로 전달되지 않아 커스텀 높이 설정이 작동하지 않는 버그가 있었습니다. 또한 접근성을 위한role="status"와aria-label속성도 적절히 구현되어 있습니다.frontend/.gitignore (1)
18-18: LGTM!Storybook 정적 빌드 출력 디렉토리(
storybook-static)를 gitignore에 추가한 것은 적절합니다.frontend/src/components/common/Spinner/Spinner.stories.tsx (1)
1-44: LGTM! Spinner 스토리가 잘 구성되어 있습니다.Default와 CustomHeight 스토리가 명확하게 구분되어 있고,
CustomHeight스토리의 docs description이 사용 사례를 잘 설명하고 있습니다.Spinner.tsx의 height prop 수정과 함께 잘 작동합니다.frontend/src/components/common/SearchField/SearchField.stories.tsx (1)
1-116: LGTM! SearchField 스토리가 잘 구성되어 있습니다.각 스토리가 다양한 사용 사례(기본, 미리 채워진 값, 커스텀 플레이스홀더)를 잘 보여주고 있습니다. argTypes의 한국어 설명도 명확합니다. CustomTextArea와 마찬가지로 render 함수 중복이 있지만, Storybook 스토리에서는 일반적인 패턴입니다.
frontend/src/components/common/InputField/InputField.stories.tsx (1)
222-231: LGTM!Disabled 스토리는 상호작용이 필요 없으므로 render 함수 없이 args만 정의한 것이 적절합니다.
frontend/src/components/common/Modal/Modal.tsx (1)
33-37: LGTM!포매팅 변경만 있으며, 접근성 속성(
aria-modal,aria-label)이 올바르게 적용되어 있습니다.frontend/src/components/common/Modal/Modal.stories.tsx (1)
101-116: 스토리 데모 콘텐츠로 적절합니다.LongContent 스토리에서 스크롤 동작을 테스트하기 위한 충분한 콘텐츠가 포함되어 있습니다.
frontend/.storybook/main.ts (1)
1-24: Vite 기반 Storybook 설정으로 잘 마이그레이션되었습니다.Webpack에서 Vite로의 전환이 올바르게 구현되었습니다:
@storybook/react-vite프레임워크 사용viteFinal에서tsconfigPaths플러그인 통합mergeConfig를 통한 Vite 설정 확장frontend/tsconfig.json (1)
8-8: Vite 기반 환경에 최적화된 적절한 변경입니다.
moduleResolution: "bundler"는 Vite 및 ESM 우선 번들러 환경의 표준 설정이며, TypeScript 5.7.2 및 Vite 7.3.0 버전과 완벽하게 호환됩니다. 프로젝트는 이미 경로 별칭(@/*)을 올바르게 구성하고 있으며, 모든 import문에서 적절한 파일 확장자를 사용하고 있으므로 모듈 해석 방식 변경으로 인한 호환성 문제는 없습니다.frontend/package.json (1)
48-55: 모든 패키지 버전이 존재하며 호환 가능합니다.Storybook v10.1.11과 Vite v7.3.0은 모두 npm 레지스트리에 안정 버전으로 존재하며 함께 설치 가능합니다. 단, Vite 7.3.0은 Node.js ^20.19.0 이상이 필요합니다 (PR 설명의 20.16+보다 더 엄격한 요구사항).
- VITE_MIXPANEL_TOKEN 환경변수가 없는 경우 함수 실행을 조기 종료하도록 guard clause 추가 - 토큰 누락 시 경고 메시지 출력 추가 - 초기화되지 않은 인스턴스에서 identify()가 호출되어 발생할 수 있는 잠재적 오류 방지
…t-disable-session-id-MOA-481 [fix] Mixpanel 초기화 로직 방어 코드 추가
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
개선사항
업데이트
정리
✏️ Tip: You can customize this high-level summary in your review settings.