Conversation
|
Warning Rate limit exceeded@jllee000 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 22 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
Walkthrough디자인 시스템에 Badge 컴포넌트를 추가하고, Storybook/Vitest 브라우저 테스트 환경을 프로젝트 루트에 도입했다. 디자인 시스템의 경로 별칭을 @lib로 정비하며 Switch 컴포넌트의 import를 갱신했다. 루트/앱 패키지 설정을 정리하고, 스토리 및 테스트 설정과 스크립트를 추가했다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User as User
participant Badge as Badge Component
participant React as React State
User->>Badge: Click
Note over Badge: onClick 핸들러
Badge->>React: setState(isClick = true)
React-->>Badge: Re-render with state
Badge->>User: 텍스트/배경 스타일 변형 적용
Note over Badge: cva variant로 스타일 분기
sequenceDiagram
autonumber
participant Dev as Dev
participant SB as Storybook (Vite)
participant VT as Vitest
participant PW as Playwright (Chromium)
participant DS as Design System
Dev->>SB: 스토리 실행 (storybook dev)
Dev->>VT: 테스트 실행 (vitest - project "storybook")
VT->>SB: storybookTest 플러그인 초기화
VT->>PW: 브라우저 세션 시작 (headless)
PW->>DS: 스토리 렌더(컴포넌트)
Note over PW,DS: .storybook/vitest.setup.ts 어노테이션 적용
VT-->>Dev: 테스트 결과 보고
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
packages/design-system/tsconfig.json (1)
1-21: 전역 타입 사용 전환에 맞춰 typeRoots 제한 해제 권장현재 파일 상단의
"typeRoots": ["node_modules/@types"]설정은 타입 검색 경로를 로컬 패키지로만 제한합니다. 루트로 타입을 승격시켰으므로 기본 TS 해석(상위 디렉터리node_modules/@types까지 탐색)을 활용하도록typeRoots제거를 제안합니다.아래는 참고용 JSON 스니펫입니다(변경은 선택):
{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"], "@lib/*": ["./src/lib/*"] }, "jsx": "react-jsx" } }apps/client/package.json (1)
18-22: @pinback/design-system: 런타임 의존성 이동 및 peerDependencies 정의 필요앱에서 디자인시스템 컴포넌트를 직접 import하고 있으므로,
apps/client/package.json의devDependencies에서dependencies로 반드시 옮겨주세요.
또한,packages/design-system/package.json에는 현재peerDependencies가 정의되어 있지 않으므로, React 및 React-DOM 19 계열을 peerDependencies에 추가해 주시기 바랍니다.· 앱 클라이언트 의존성 이동 (
apps/client/package.json)--- a/apps/client/package.json +++ b/apps/client/package.json @@ -14,13 +14,14 @@ "react": "^19.1.1", "react-dom": "^19.1.1" }, "dependencies": { + "@pinback/design-system": "workspace:*", "react": "^19.1.1", "react-dom": "^19.1.1" }, "devDependencies": { - "@pinback/design-system": "workspace:*", "@eslint/js": "^9.33.0", "@pinback/eslint-config": "workspace:*", "@pinback/tailwind-config": "workspace:*", "@pinback/typescript-config": "workspace:*",· 디자인시스템 패키지 peerDependencies 추가 (
packages/design-system/package.json)--- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -X,Y +X,Y @@ "name": "@pinback/design-system", "version": "…", + "peerDependencies": { + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, // 기존 필드들…packages/design-system/src/components/switch/switch.tsx (1)
10-23: Tailwind 클래스 중복(두 개의 높이) — 마지막 선언만 적용됨
SwitchPrimitive.Root의className에h-[1.15rem] h-[2rem]가 동시에 존재합니다. Tailwind 특성상 뒤쪽h-[2rem]만 적용되어 혼동을 줄 수 있습니다. 의도된 크기만 남기거나, 크기 변형이 목적이면 variant/size prop으로 분기하는 것을 권장합니다.가능한 수정 예:
- '... inline-flex h-[1.15rem] h-[2rem] w-[4rem] shrink-0 ...' + '... inline-flex h-[2rem] w-[4rem] shrink-0 ...'packages/design-system/src/components/badge/Badge.tsx (3)
3-6: Badge API 확장 제안: 제어/비제어 선택 상태, 스타일 확장성DS 컴포넌트 특성상 외부 제어가 필요할 가능성이 높습니다. 다음 속성 추가를 고려해 주세요:
selected?,defaultSelected?,onSelectedChange?,className?,countClassName?,disabled?. 이러면 내부 상태 강제/영구 선택 문제도 해소됩니다.예시 타입 확장:
export interface BadgeProps { text: string; countNum?: number; + selected?: boolean; // 제어 모드 + defaultSelected?: boolean; // 비제어 초기값 + onSelectedChange?: (v: boolean) => void; + className?: string; + countClassName?: string; + disabled?: boolean; }추가로 내부
useState를selected/defaultSelected와 동기화하도록 리팩터 필요합니다. 원하시면 패치 제안 드리겠습니다.
39-41: 빈 카운트 뱃지 렌더 방지 및 표기 규칙(예: 99+) 적용
countNum가 없을 때 빈 뱃지 캡슐이 그려집니다. 정의된 값일 때만 렌더하고 큰 수는 축약 표기를 권장합니다.다음 패치를 제안합니다:
- <span className={BadgeStyleVariants({ click: isClick })}>{countNum}</span> + {countNum != null && ( + <span className={BadgeStyleVariants({ click: isClick })}> + {countNum > 99 ? '99+' : countNum} + </span> + )}
7-17: 네이밍 정교화: variant 키click→selected상태 의미상
click보다는selected가 자연스럽습니다. 스타일 호출부도 동일하게 갱신해 주세요.제안 패치:
-const BadgeTxtStyleVariants = cva('sub3-b', { +const BadgeTxtStyleVariants = cva('sub3-b', { variants: { - click: { + selected: { true: 'text-font-black-1', false: 'text-font-ltgray-4', } as const, }, defaultVariants: { - click: false, + selected: false, }, }); @@ - variants: { - click: { + variants: { + selected: { true: 'bg-main500', false: 'bg-gray300', } as const, }, defaultVariants: { - click: false, + selected: false, }, } ); @@ - <span className={BadgeTxtStyleVariants({ click: isClick })}>{text}</span> - <span className={BadgeStyleVariants({ click: isClick })}>{countNum}</span> + <span className={BadgeTxtStyleVariants({ selected: isClick })}>{text}</span> + <span className={BadgeStyleVariants({ selected: isClick })}>{countNum}</span>Also applies to: 21-29, 39-41
packages/design-system/src/components/index.ts (1)
3-3: 배럴에 타입도 함께 노출 제안소비자가 타입 재사용하기 편하도록
BadgeProps도 export 해 주세요.제안 패치:
export { default as Badge } from './badge/Badge'; +export type { BadgeProps } from './badge/Badge';
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
apps/client/package.json(1 hunks)apps/client/src/App.tsx(2 hunks)package.json(1 hunks)packages/design-system/package.json(0 hunks)packages/design-system/src/components/badge/Badge.tsx(1 hunks)packages/design-system/src/components/index.ts(1 hunks)packages/design-system/src/components/switch/switch.tsx(1 hunks)packages/design-system/tsconfig.json(1 hunks)packages/design-system/vite.config.ts(1 hunks)
💤 Files with no reviewable changes (1)
- packages/design-system/package.json
🔇 Additional comments (8)
package.json (1)
15-16: 디자인 시스템의tsconfig.jsontypeRoots설정 검토 및 조정 필요
packages/design-system/tsconfig.json에서"compilerOptions": { … "typeRoots": ["node_modules/@types"], … }로 명시되어 있어, 패키지 루트(
packages/design-system/node_modules/@types)에서만 타입 선언을 검색하게 됩니다. 모노레포 루트(node_modules/@types/@types/react)에 설치된 전역 React 타입을 참조하지 못해, 하위 패키지에서 React 관련 타입을 인식하지 못할 수 있습니다.따라서 아래 중 하나로 조정할 것을 권장합니다.
typeRoots옵션 제거 (기본값 사용 → 상위/루트node_modules/@types까지 모두 참조)- 또는 모노레포 루트까지 포함하도록 경로 확장
"compilerOptions": { - "typeRoots": ["node_modules/@types"], + "typeRoots": [ + "node_modules/@types", + "../../node_modules/@types" + ], … }실제 전역 타입 해석이 제대로 동작하는지, 아래 커맨드로 로컬 환경(의존성 설치 완료 상태)에서 확인해 주세요:
# 디자인 시스템 패키지 루트로 이동 후 pnpm install # 또는 npm/yarn install pnpm tsc --noEmit # 빌드 오류 확인 # 또는 rg -n --regexp '\bReact\.' packages/design-systempackages/design-system/tsconfig.json (1)
9-11: 경로 별칭(@lib) 추가는 적절 — Vite 별칭과 정확히 일치하도록 동기화 필요
"@lib/*": ["./src/lib/*"]추가는 합리적입니다. 다만 Vite 설정에서@lib가 절대경로(/src/lib)로 되어 있어 현재 TS 설정과 불일치합니다. Vite 쪽을"src/lib"로 맞추지 않으면 런타임/번들 시 경로 해석이 깨질 가능성이 큽니다(아래 Vite 코멘트 참조).packages/design-system/src/components/switch/switch.tsx (1)
3-3: 별칭 전환 자체는 적절 — Vite 별칭 수정 선행 필요
@/lib/utils→@lib/utils전환은 tsconfig 경로와 일치합니다. 다만 현재 Vite 별칭이 절대경로로 잘못 설정되어 있어(위 코멘트 참고) 빌드 실패 가능성이 큽니다. Vite 수정 후 이 변경은 정상 동작할 것입니다.packages/design-system/src/components/badge/Badge.tsx (1)
1-2: 토큰/유틸 클래스 검증 권고
sub3-b,sub5-sb,text-font-*,bg-main500등 커스텀 유틸이 Tailwind 설정(또는 토큰 레이어)에 존재하는지 확인 부탁드립니다. 런타임에 클래스를 문자열로 조합하므로 미정의 토큰은 빌드 타임엔 탐지되지 않습니다.원하시면 저장소에서 해당 토큰 사용/정의를 스캔하는 스크립트를 제공해 드립니다.
apps/client/src/App.tsx (2)
20-20: 샘플 사용 👍Badge 기본 사용 예로 충분합니다. 이후 제어형 API가 도입되면
selected/onSelectedChange예제도 추가하면 좋겠습니다.
5-5: 확인 완료:@pinback/design-system/ui경로가 exports에 정의되어 있습니다design-system 패키지의
package.jsonexports에"./ui": "./src/components/index.ts"엔트리가 명시되어 있어
import { Badge } from '@pinback/design-system/ui';는 배포 시에도 정상 작동합니다.
추가 조치 불필요합니다.packages/design-system/src/components/index.ts (2)
3-3: PR 목적과 변경 내용 불일치 확인 필요연결된 이슈(#25)는 Progress 컴포넌트 구현인데, 본 PR은 Badge 추가입니다. 이슈 링크/PR 제목/설명 정합성을 맞춰 주세요.
원하시면 체크리스트/설명 템플릿을 자동 보정하는 가이드를 드리겠습니다.
3-3: 배럴 업데이트 자체는 적절합니다UI 퍼블릭 표면에 Badge를 추가한 점 문제 없습니다.
| const BadgeStyleVariants = cva( | ||
| 'text-white-bg sub5-sb rounded-[0.4rem] px-[0.8rem] py-[0.4rem]', | ||
| { |
There was a problem hiding this comment.
오타 추정: text-white-bg → text-white
Tailwind/DS 네이밍 관례상 text-white-bg는 비정상입니다. 베이스에 텍스트 색만 두고 배경은 variant에서 제어 중이므로 text-white가 맞습니다.
다음 패치를 제안합니다:
-const BadgeStyleVariants = cva(
- 'text-white-bg sub5-sb rounded-[0.4rem] px-[0.8rem] py-[0.4rem]',
+const BadgeStyleVariants = cva(
+ 'text-white sub5-sb rounded-[0.4rem] px-[0.8rem] py-[0.4rem]',📝 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.
| const BadgeStyleVariants = cva( | |
| 'text-white-bg sub5-sb rounded-[0.4rem] px-[0.8rem] py-[0.4rem]', | |
| { | |
| // packages/design-system/src/components/badge/Badge.tsx | |
| const BadgeStyleVariants = cva( | |
| 'text-white sub5-sb rounded-[0.4rem] px-[0.8rem] py-[0.4rem]', | |
| { | |
| /* variants… */ | |
| } | |
| ); |
🤖 Prompt for AI Agents
In packages/design-system/src/components/badge/Badge.tsx around lines 18 to 20,
the base cva class string contains a typo "text-white-bg" which should be
"text-white"; update the class list to replace "text-white-bg" with "text-white"
so text color is applied in the base and background variants remain controlled
by the variant definitions.
| return ( | ||
| <div | ||
| className="flex cursor-pointer items-center justify-center gap-[0.8rem]" | ||
| onClick={() => setIsClick(true)} | ||
| > | ||
| <span className={BadgeTxtStyleVariants({ click: isClick })}>{text}</span> | ||
| <span className={BadgeStyleVariants({ click: isClick })}>{countNum}</span> | ||
| </div> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
클릭 가능한 요소의 접근성/키보드 조작 개선 (div → button)
현재 div에 onClick만 있어 스크린리더/키보드 접근성이 떨어집니다. 토글 동작도 없고 한 번 true가 되면 해제 불가합니다. 버튼 요소와 aria-pressed를 사용하고 토글 처리로 개선하는 것이 좋습니다.
다음 패치를 제안합니다:
- return (
- <div
- className="flex cursor-pointer items-center justify-center gap-[0.8rem]"
- onClick={() => setIsClick(true)}
- >
+ return (
+ <button
+ type="button"
+ className="inline-flex items-center gap-[0.8rem] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main500"
+ aria-pressed={isClick}
+ onClick={() => setIsClick((prev) => !prev)}
+ >
@@
- </div>
+ </button>📝 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.
| return ( | |
| <div | |
| className="flex cursor-pointer items-center justify-center gap-[0.8rem]" | |
| onClick={() => setIsClick(true)} | |
| > | |
| <span className={BadgeTxtStyleVariants({ click: isClick })}>{text}</span> | |
| <span className={BadgeStyleVariants({ click: isClick })}>{countNum}</span> | |
| </div> | |
| return ( | |
| - <div | |
| - className="flex cursor-pointer items-center justify-center gap-[0.8rem]" | |
| - onClick={() => setIsClick(true)} | |
| <button | |
| type="button" | |
| className="inline-flex items-center gap-[0.8rem] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main500" | |
| aria-pressed={isClick} | |
| onClick={() => setIsClick((prev) => !prev)} | |
| > | |
| <span className={BadgeTxtStyleVariants({ click: isClick })}>{text}</span> | |
| <span className={BadgeStyleVariants({ click: isClick })}>{countNum}</span> | |
| </button> | |
| ); |
| '@': path.resolve(__dirname, 'src'), | ||
| '@utils': path.resolve(__dirname, './src/utils'), | ||
| '@lib': path.resolve(__dirname, '/src/lib'), | ||
| }, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
중대: @lib 별칭이 절대경로(/src/lib)로 설정됨 — 빌드/스토리북 경로 해석 오류 유발
path.resolve(__dirname, '/src/lib')는 루트 절대경로로 해석됩니다. 로컬 소스에 맞춰 상대 경로 'src/lib'를 사용해야 TS paths와 일관성이 맞고, 번들/테스트에서도 정상 동작합니다.
아래 패치 적용을 권장합니다.
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
- '@lib': path.resolve(__dirname, '/src/lib'),
+ '@lib': path.resolve(__dirname, 'src/lib'),
},
},적용 후 @lib/utils import가 정상적으로 해석되는지 스토리북/빌드를 통해 확인해 주세요.
다음 스크립트로 최소한의 정적 확인을 할 수 있습니다.
🏁 Script executed:
#!/bin/bash
# @lib 사용처 나열 및 실존 파일 확인
rg -nP --type=ts --type=tsx "\@lib/" packages/design-system | sed -n '1,100p'
test -d packages/design-system/src/lib && echo "src/lib 디렉터리 존재" || echo "src/lib 없음"Length of output: 187
중대한 경로 해석 오류: /src/lib → src/lib로 수정 필요
- 파일:
packages/design-system/vite.config.ts
대상 라인: 27–29 - 현상:
path.resolve(__dirname, '/src/lib')호출 시 최상위 루트(/src/lib)로 해석되어 실제 코드 위치(packages/design-system/src/lib)를 가리키지 않습니다.- 스크립트 실행 결과
packages/design-system/src/lib디렉터리가 존재함이 확인되었습니다.
- 영향:
@lib/...import가 빌드 및 스토리북에서 올바르게 해석되지 않아 모듈을 찾지 못하는 오류가 발생합니다.
아래와 같이 패치 적용을 권장합니다:
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
- '@lib': path.resolve(__dirname, '/src/lib'),
+ '@lib': path.resolve(__dirname, 'src/lib'),
},
},적용 후 @lib/utils 등 모든 @lib/ import가 스토리북 및 번들 빌드에서 정상 해석되는지 반드시 확인해 주세요.
🤖 Prompt for AI Agents
In packages/design-system/vite.config.ts around lines 27–29, the alias for
'@lib' uses path.resolve(__dirname, '/src/lib') which resolves to the filesystem
root (/src/lib) instead of the package's src; change it to
path.resolve(__dirname, 'src/lib') (remove the leading slash) so it points to
packages/design-system/src/lib, then rebuild and verify all @lib/... imports
(e.g., @lib/utils) resolve correctly in Storybook and bundle builds.
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (40)
packages/design-system/src/components/badge/Badge.stories.tsx (8)
3-3: 미사용 타입 import 제거로 린트 경고 해소
BadgeProps가 사용되지 않아 lint 경고가 발생합니다. 불필요한 import는 제거해주세요.-import Badge, { type BadgeProps } from './Badge'; +import Badge from './Badge';
13-19: Controls UX 미세 개선: step 추가 및 테이블 요약 명시Number 컨트롤에
step: 1을 추가해 조작성을 높이고, Docs 테이블에 타입 요약을 제공하는 것을 제안합니다.countNum: { - control: { type: 'number', min: 0 }, + control: { type: 'number', min: 0, step: 1 }, description: '카운트 숫자(옵션)', + table: { type: { summary: 'number | undefined' } }, },
20-27: 문구 다듬기: 내부 구현 누설 최소화 및 문장부호 정리Docs에 내부 상태명(
isClick) 언급은 유지보수 시 문서/코드 불일치 리스크가 있습니다. 의미는 유지하면서 구현 세부는 숨기는 편이 좋습니다. 불필요한.!도 정리합니다.- '클릭 시 내부 state(isClick)가 true로 바뀌며 스타일이 활성화됩니다. 토글 방식은 아닙니다.!', + '클릭 시 활성 상태로 바뀌며 스타일이 적용됩니다. 토글 방식은 아닙니다.',
35-37:undefined명시 대신 속성 생략 권장Storybook args에선 선택적 프로퍼티는 생략하는 편이 일반적입니다. 의미 동일하며 UI도 더 깔끔합니다.
export const NoCount: Story = { - args: { text: '카운트 없음', countNum: undefined }, + args: { text: '카운트 없음' }, };
39-41: 대용량 카운트 시나리오에 대한 Docs 보강Overflow 표기 정책(예: 999+)을 문서화하면 디자인 의도가 명확해집니다.
export const LargeCount: Story = { args: { text: '메시지', countNum: 12000 }, + parameters: { + docs: { + description: { story: '매우 큰 카운트 값을 전달했을 때의 표시 방식(예: 999+ 등)을 확인합니다.' }, + }, + }, };
43-48: play 함수: 접근성 쿼리와 상호작용 검증 고려현재 텍스트 매칭 클릭은 동작은 하지만, 인터랙티브 역할(버튼 등)을 갖는다면 role 기반 쿼리가 더 견고합니다. 또한 클릭 후 상태 변화(예: aria-pressed, data-state 등)를 가볍게 검증하면 회귀 방지에 도움됩니다. 컴포넌트 역할/속성 노출 여부에 따라 적용을 검토해주세요.
예시(컴포넌트가 버튼 역할을 노출한다면):
- play: async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - await userEvent.click(await canvas.findByText(String(args.text))); - }, + play: async ({ canvasElement, args }) => { + const canvas = within(canvasElement); + const btn = await canvas.findByRole('button', { name: String(args.text) }); + await userEvent.click(btn); + // TODO: aria-pressed 또는 data-state 등이 있다면 아래와 같이 검증 추가 + // await expect(btn).toHaveAttribute('aria-pressed', 'true'); + },확인 요청:
- Badge가 키보드로도 활성화 가능한지(Tab/Enter/Space) 확인 바랍니다. 접근성 요구가 있다면
play에 키보드 상호작용도 추가하는 것을 권장합니다.
1-55: PR 메타와 변경 내용 불일치 가능성: 이슈 연결 상태 점검현재 PR은 Badge 컴포넌트(스토리 포함) 추가인데, 연결된 이슈(#25)는 Progress 컴포넌트 구현 목적입니다. 의도된 링크인지 확인 부탁드립니다. 잘못 연결된 경우 PR 설명/이슈 링크 정정 및 스크린샷/체크리스트 업데이트를 권장합니다.
원하시면 PR 템플릿 보강 초안을 드리겠습니다(사용 예, 접근성 요건, 디자인 토큰 참조, 스크린샷 섹션 등).
5-28: 타입 추론 강화:satisfies패턴 적용 검증 완료프로젝트에서 사용 중인 TypeScript 버전(5.9.2)이 4.9 이상을 충족하므로, Storybook
Meta객체에satisfies키워드를 안전하게 도입할 수 있습니다. 이를 통해 제네릭 선언(Meta<typeof Badge>)을 제거해도 속성 추론을 유지하면서 타입 안전성을 확보하고,StoryObj<typeof meta>를 사용해 컴포넌트 교체 시 제네릭 변경을 줄일 수 있습니다.적용 예시 (
packages/design-system/src/components/badge/Badge.stories.tsx, 5–28, 31 라인):-const meta: Meta<typeof Badge> = { +const meta = { title: 'UI/Badge', component: Badge, tags: ['autodocs'], args: { text: '알림', countNum: 3, }, argTypes: { text: { control: 'text', description: '뱃지 라벨 텍스트' }, countNum: { control: { type: 'number', min: 0 }, description: '카운트 숫자(옵션)', }, }, parameters: { docs: { description: { component: '클릭 시 내부 state(isClick)가 true로 바뀌며 스타일이 활성화됩니다. 토글 방식은 아닙니다.!', }, }, }, -}; +} satisfies Meta<typeof Badge>; -type Story = StoryObj<typeof Badge>; +type Story = StoryObj<typeof meta>;이 변경은 선택적 리팩터로, 필요에 따라 적용 가능합니다.
vitest.shims.d.ts (1)
1-1: 전역 타입 주입 범위 제한 권장현재 워크스페이스 루트와
packages/design-system패키지에 위치한 두 개의vitest.shims.d.ts파일이/// <reference types="@vitest/browser/providers/playwright" />를 통해 Playwright/Vitest 타입을 전역으로 주입하고 있습니다. 이로 인해 앱 내부 타입 체킹 단계에 테스트 전용 타입이 누수될 수 있으므로, 테스트 전용으로 범위를 한정할 것을 권장합니다.
수정 대상 파일
vitest.shims.d.ts(루트)packages/design-system/vitest.shims.d.ts- (
참고)packages/design-system/vite.config.ts2행:/// <reference types="vitest/config" />권장 옵션
- 옵션 A: 테스트 전용 tsconfig (
vitest.tsconfig.json) 추가 후, Vitest 실행 시--config vitest.tsconfig.json으로 지정{ "extends": "./tsconfig.json", "compilerOptions": { "types": ["@vitest/browser/providers/playwright"] }, "include": [ "**/*.test.ts", "**/*.test.tsx", "**/*.stories.tsx", ".storybook/vitest.setup.ts" ] }- 옵션 B: 위
*.shims.d.ts파일들을tests/또는.storybook/등 테스트 전용 디렉터리로 이동하고, tsconfig의include설정에만 포함되도록 구성현재 tsconfig 및 triple-slash 참조 위치 확인 스크립트
#!/bin/bash # 루트 및 패키지 tsconfig에서 types/include/files 설정 점검 fd -a -t f -g "tsconfig*.json" | while read -r f; do echo "---- $f ----" jq '.compilerOptions.types, .include, .files' "$f" 2>/dev/null || cat "$f" done # 모든 triple-slash 타입 참조 검색 rg -nP --glob '!node_modules' --glob '!*dist*' '^///\s*<reference\s+types='.storybook/preview.ts (1)
1-1: Preview 타입 import 경로를 '@storybook/react'로 조정 권장SB v8/v9 기준 Preview 타입은 일반적으로 '@storybook/react'에서 제공되고, '@storybook/react-vite'는 주로 StorybookConfig 타입을 노출합니다. 현 경로에서도 동작할 수는 있으나, 향후 타입 호환성 측면에서 '@storybook/react'로 맞추는 편이 안전합니다.
-import type { Preview } from '@storybook/react-vite'; +import type { Preview } from '@storybook/react';package.json (1)
10-12: 스토리북/브라우저 테스트 스크립트 보강 제안CI와 로컬 확인을 위해 스토리북-스토리 기반 브라우저 테스트 스크립트를 추가해 두면 편리합니다.
"check-types": "turbo run check-types", "test": "turbo run test", "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" + "build-storybook": "storybook build", + "test:ui": "vitest --browser", + "test:ui:coverage": "vitest --browser --coverage".storybook/main.ts (3)
9-11: getAbsolutePath 반환 타입을 string으로 명시암시적 any는 추후 TS 설정 강화 시 에러가 될 수 있습니다. 반환 타입을 명시해 주세요.
-function getAbsolutePath(value: string): any { +function getAbsolutePath(value: string): string { return dirname(require.resolve(join(value, 'package.json'))) }
13-16: 스토리 검색 범위를 패키지 스토리까지 확장 제안디자인 시스템 패키지 내부의 스토리까지 자동 수집하려면 글롭을 확장하는 편이 편리합니다.
"stories": [ - "../stories/**/*.mdx", - "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)" + "../stories/**/*.mdx", + "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)", + "../packages/**/src/**/*.mdx", + "../packages/**/src/**/*.stories.@(js|jsx|mjs|ts|tsx)" ],
17-22: 애드온 구성 보완 검토(선택)
기본 액션/컨트롤/백그라운드/뷰포트 등을 한 번에 활성화하려면@storybook/addon-essentials사용을 고려해 볼 수 있습니다(중복 추가 시 Storybook이 내부적으로 중복 제거). 현재 요구사항에 충분하다면 유지하셔도 됩니다."addons": [ getAbsolutePath('@chromatic-com/storybook'), getAbsolutePath('@storybook/addon-docs'), getAbsolutePath("@storybook/addon-a11y"), - getAbsolutePath("@storybook/addon-vitest") + getAbsolutePath("@storybook/addon-vitest"), + getAbsolutePath("@storybook/addon-essentials") ],tsconfig Node 타입 확인 요청
.storybook디렉터리에 별도의tsconfig.json파일이 없습니다.- 앱 레벨(예:
apps/client/tsconfig.json)에도"types"설정이 누락되어 있습니다.- Storybook 설정 파일(
.storybook/main.ts) 내에서require.resolve를 사용할 경우 Node 전역 타입이 필요합니다.
→ Storybook이 참조하는 tsconfig에 아래와 같이"types": ["node"]를 명시하거나,
.storybook/tsconfig.json을 생성하여 Node 타입을 포함시키는지 검토해 주세요.stories/button.css (1)
1-9: 키보드 포커스 가시성 및 disabled 상태 스타일 추가 권장접근성 관점에서 포커스 링과 비활성화 상태 스타일을 명시해 두면 좋습니다. 또한 disabled일 때 커서가 pointer로 유지되는 것도 피하는 편이 낫습니다.
.storybook-button { display: inline-block; cursor: pointer; border: 0; border-radius: 3em; font-weight: 700; line-height: 1; font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; } + +.storybook-button:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; +} + +.storybook-button:disabled { + opacity: 0.5; + cursor: not-allowed; +}stories/Configure.mdx (4)
1-1: Storybook MDX v2/8 권장사항: Meta import 경로 최신화구버전 경로("@storybook/addon-docs/blocks") 대신 최신 권장 경로("@storybook/blocks")를 사용하면 호환성과 타입 안정성이 좋아집니다.
아래와 같이 변경을 제안합니다:
-import { Meta } from "@storybook/addon-docs/blocks"; +import { Meta } from '@storybook/blocks';
37-41: 시맨틱 마크업: 제목은 실제 헤딩 태그로 표기div 내부에
# Configure your project텍스트 대신 h2를 사용하면 접근성과 문서 구조가 개선됩니다. 본문도 p 태그로 감싸는 것을 권장합니다.- <div className='sb-section-title'> - # Configure your project - - Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community. - </div> + <div className='sb-section-title'> + <h2>Configure your project</h2> + <p> + Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. + Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you + can ask for help from our community. + </p> + </div>
83-88: 시맨틱 마크업: 두 번째 섹션 제목도 h2 사용두 번째 섹션도 동일한 방식으로 h2 태그 사용을 권장합니다.
- <div className='sb-section-title'> - # Do more with Storybook - - Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs. - </div> + <div className='sb-section-title'> + <h2>Do more with Storybook</h2> + <p> + Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list + is just to get you started. You can customise Storybook in many ways to fit your needs. + </p> + </div>
221-224: 스타일 범위 축소: 전역 img 셀렉터로 인한 부작용 가능성
img { object-fit: cover; }는 페이지 전역 이미지에 적용됩니다. 본 문서 블록으로 범위를 한정하세요.- img { + .sb-container img, + .sb-addon-img img, + .sb-socials img { object-fit: cover; }.storybook/vitest.setup.ts (1)
2-2: Framework 패키지 import 통일 검토:@storybook/react-vite→@storybook/react(선택적 리팩토링)현재
.storybook디렉터리 내에서 네 곳에서@storybook/react-vite를 import 하고 있습니다. 공식 가이드에서는 프레임워크 패키지를@storybook/react로 통일할 것을 권장하므로, 버전 업/마이그레이션 시 호환성 이슈를 줄이기 위해 변경을 검토해 보시기 바랍니다.
- .storybook/vitest.setup.ts:
setProjectAnnotations- .storybook/preview.ts:
Preview타입- .storybook/main.ts:
StorybookConfig타입- .storybook/main.ts:
getAbsolutePath인자변경 예시:
--- a/.storybook/vitest.setup.ts +++ b/.storybook/vitest.setup.ts -import { setProjectAnnotations } from '@storybook/react-vite'; +import { setProjectAnnotations } from '@storybook/react';--- a/.storybook/preview.ts +++ b/.storybook/preview.ts -import type { Preview } from '@storybook/react-vite'; +import type { Preview } from '@storybook/react';--- a/.storybook/main.ts +++ b/.storybook/main.ts -import type { StorybookConfig } from '@storybook/react-vite'; +import type { StorybookConfig } from '@storybook/react'; … - "name": getAbsolutePath('@storybook/react-vite'), + "name": getAbsolutePath('@storybook/react'),검증 스크립트:
#!/bin/bash # 숨김 디렉터리 포함하여 Storybook import 현황 확인 rg --hidden -n "@storybook/react-vite|@storybook/react" -g ".storybook/**" -Sstories/Page.tsx (3)
11-11: 초깃값 명시 및 타입 일관성(선택 사항)
undefined대신null을 사용하면 의도된 "비로그인" 상태를 더 분명히 표현할 수 있습니다.- const [user, setUser] = React.useState<User>(); + const [user, setUser] = React.useState<User | null>(null);
10-10: React.FC 사용 여부 정리(선택 사항)
React.FC는 암묵적 children 포함 등 논쟁 요소가 있어 단순 함수 선언을 권장하는 코드베이스도 많습니다. 팀 컨벤션에 맞춰 통일해 주세요.-export const Page: React.FC = () => { +export const Page = () => {
59-67: 팁 아이콘 SVG 접근성 마무리장식용 아이콘이면 스크린리더에서 숨기는 것이 좋습니다.
- <svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"> + <svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">stories/Page.stories.ts (2)
1-1: Storybook 타입 import 경로 정렬타입은 보통
@storybook/react에서 가져옵니다. 현재도 동작할 수 있으나, 빌더/프레임워크 분리 관점에서 타입은 프레임워크 패키지로 통일하는 편이 안전합니다.-import type { Meta, StoryObj } from '@storybook/react-vite'; +import type { Meta, StoryObj } from '@storybook/react';
23-33: 상호작용 테스트 안정성 개선클릭 후 동일 DOM 노드 참조에 대한 존재 여부 검증 대신, 쿼리를 다시 수행해 확인하면 안정적입니다.
play: async ({ canvasElement }) => { const canvas = within(canvasElement); const loginButton = canvas.getByRole('button', { name: /Log in/i }); - await expect(loginButton).toBeInTheDocument(); + await expect(loginButton).toBeInTheDocument(); await userEvent.click(loginButton); - await expect(loginButton).not.toBeInTheDocument(); + await expect(canvas.queryByRole('button', { name: /Log in/i })).not.toBeInTheDocument(); const logoutButton = canvas.getByRole('button', { name: /Log out/i }); await expect(logoutButton).toBeInTheDocument(); },stories/Header.stories.ts (1)
1-1: Storybook 타입 import 경로 정렬여기도 타입을
@storybook/react에서 가져오도록 통일하는 편을 권장합니다.-import type { Meta, StoryObj } from '@storybook/react-vite'; +import type { Meta, StoryObj } from '@storybook/react';stories/page.css (3)
3-8: 타이포/스페이싱을 디자인 토큰·rem 기반으로 통일 제안px·하드코딩 색상 대신 DS 토큰과 rem 비율을 사용하면 테마/접근성 대응과 일관성이 좋아집니다.
아래와 같이 최소 변경을 제안합니다(토큰 미정 시 우측의 폴백 사용):
- padding: 48px 20px; - max-width: 600px; - color: #333; - font-size: 14px; - line-height: 24px; + padding: var(--space-12, 3rem) var(--space-5, 1.25rem); + max-width: 37.5rem; + color: var(--color-fg-muted, #333); + font-size: var(--font-size-body, 0.875rem); + line-height: var(--line-height-body, 1.714);토큰 정의가 없다면, 아래처럼 공용 레벨에 추가하는 것을 권장합니다(별도 파일 혹은 Storybook preview 스타일):
:root { --space-5: 1.25rem; --space-12: 3rem; --color-fg-muted: #333; --font-size-body: 0.875rem; --line-height-body: 1.714; --color-accent: #1ea7fd; }
57-64: SVG 채색은 currentColor 사용으로 테마 친화성 향상path에 고정 색상을 지정하기보다 currentColor를 쓰고 상위 요소에 색을 지정하면 테마 일관성이 좋아집니다.
.storybook-page .tip-wrapper svg { display: inline-block; vertical-align: top; margin-top: 3px; margin-right: 4px; width: 12px; height: 12px; + color: var(--color-accent, #1ea7fd); } .storybook-page .tip-wrapper svg path { - fill: #1ea7fd; + fill: currentColor; }Also applies to: 66-68
24-26: 링크 포커스/호버 가시성 보강 제안현재는 색상 상속만 있어 키보드 포커스 가시성이 약합니다. Storybook 페이지 용도라면 아래 보강을 권장합니다.
.storybook-page a { color: inherit; text-decoration: underline; } .storybook-page a:hover { color: var(--color-accent, #1ea7fd); } .storybook-page a:focus-visible { outline: 2px solid var(--color-accent, #1ea7fd); outline-offset: 2px; }stories/Header.tsx (3)
42-45: 시맨틱 강조는 대신 사용 권장는 단순 시각 강조, 는 의미 강조입니다. 환영 문구에는 strong이 더 적합합니다.
- <span className="welcome"> - Welcome, <b>{user.name}</b>! - </span> + <span className="welcome"> + Welcome, <strong>{user.name}</strong>! + </span>
10-15: 브랜딩/문구 하드코딩 제거로 재사용성·i18n 대비디자인 시스템 관점에서 'Acme' 같은 고정 문구는 props로 추출하는 편이 좋습니다.
export interface HeaderProps { user?: User; onLogin?: () => void; onLogout?: () => void; onCreateAccount?: () => void; + brand?: string; } -export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => ( +export const Header = ({ user, onLogin, onLogout, onCreateAccount, brand = 'Acme' }: HeaderProps) => ( ... - <h1>Acme</h1> + <h1>{brand}</h1>원하시면 로그인/로그아웃 문구도 props(i18n)로 분리한 패치 제안 드리겠습니다.
Also applies to: 17-17, 37-37
45-51: 콜백 미지정 시 버튼 비활성화로 의도 명확화onLogin/onCreateAccount/onLogout이 없을 때 버튼을 disabled 처리하면 UX 오해를 줄일 수 있습니다.
- <Button size="small" onClick={onLogout} label="Log out" /> + <Button size="small" onClick={onLogout} disabled={!onLogout} label="Log out" /> ... - <Button size="small" onClick={onLogin} label="Log in" /> - <Button primary size="small" onClick={onCreateAccount} label="Sign up" /> + <Button size="small" onClick={onLogin} disabled={!onLogin} label="Log in" /> + <Button primary size="small" onClick={onCreateAccount} disabled={!onCreateAccount} label="Sign up" />vitest.config.ts (1)
32-33: setupFiles는 절대경로 사용 권장(CWD 의존 제거)CI/IDE 실행 위치가 달라도 안전하도록 dirname 기반으로 해주세요.
- setupFiles: ['.storybook/vitest.setup.ts'], + setupFiles: [path.join(dirname, '.storybook', 'vitest.setup.ts')],stories/Button.stories.ts (2)
8-17: Story 카테고리 네이밍을 DS 규칙에 맞게 정리 제안
Example/Button대신Components/Button같이 제품 내 탐색 체계에 맞추면 스토리 탐색성이 좋아집니다.- title: 'Example/Button', + title: 'Components/Button',
29-34: 상호작용 테스트(play) 추가로 Vitest 연동 가치 극대화Primary 스토리에 간단한 상호작용 테스트를 추가하면 Vitest+Storybook 통합이 실제 가치(회귀 방지)를 가집니다.
예시(참고용, import 보강 필요):
// 상단에 추가: // import { expect, userEvent, within } from '@storybook/test'; export const Primary: Story = { args: { primary: true, label: 'Button' }, play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); const btn = await canvas.findByRole('button', { name: args.label }); await userEvent.click(btn); // onClick은 meta.args로 fn()이 주입되어 있음 expect(args.onClick).toHaveBeenCalled(); }, };원하시면 필요한 import/타입까지 포함한 패치를 만들어 드리겠습니다.
stories/header.css (2)
6-7: 스페이싱/타이포 rem·토큰화 제안px와 하드코딩 값을 토큰/상대 단위로 전환하면 테마 일관성과 접근성이 향상됩니다.
- padding: 15px 20px; + padding: var(--space-4, 1rem) var(--space-5, 1.25rem); ... - font-size: 20px; + font-size: var(--font-size-h4, 1.25rem);Also applies to: 20-21
1-8: 다크 모드 대비 색상 토큰 사용 제안기본 텍스트 색(#333)과 보더 rgba는 다크 모드에서 대비가 부족할 수 있습니다. 토큰으로 전환하고 prefers-color-scheme에 대응을 권장합니다.
예시:
.storybook-header { border-bottom: 1px solid var(--color-border-subtle, rgba(0,0,0,0.1)); } .storybook-header .welcome { color: var(--color-fg-muted, #333); } @media (prefers-color-scheme: dark) { :root { --color-border-subtle: rgba(255,255,255,0.16); --color-fg-muted: #c9d1d9; } }stories/Button.tsx (3)
18-37: focus 관리/접근성 호환을 위해 ref 전달을 지원하세요디자인 시스템의 Button은 포커스 제어나 스크롤/측정 목적의 ref 전달이 빈번합니다. forwardRef를 적용하는 것을 권장합니다.
-export const Button = ({ +export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ primary = false, size = 'medium', backgroundColor, label, className, style, ...rest -}: ButtonProps) => { +}: ButtonProps, ref) => { const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; const classes = ['storybook-button', `storybook-button--${size}`, mode, className] .filter(Boolean) .join(' '); const mergedStyle = { ...(style ?? {}), ...(backgroundColor ? { backgroundColor } : {}) }; return ( <button + ref={ref} type="button" className={classes} style={mergedStyle} {...rest} > {label} </button> ); -}; +});
6-11: 불린 primary 대신 variant 문자열을 사용하면 확장이 쉽습니다boolean(primary) 보다는 variant: 'primary' | 'secondary'로 명시하면 향후 'tertiary', 'danger' 등 확장이 수월합니다. 지금 단계에서는 선택 사항이지만 디자인 토큰/테마와의 매핑에도 이점이 있습니다.
- primary?: boolean; + variant?: 'primary' | 'secondary'; ... -export const Button = ({ - primary = false, +export const Button = ({ + variant = 'secondary', ... - const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; + const mode = variant === 'primary' ? 'storybook-button--primary' : 'storybook-button--secondary';Also applies to: 20-21, 26-31
1-1: React 자동 JSX 런타임 적용 여부 확인 요청:stories/Button.tsx의import React
stories/Button.tsx상단의import React from 'react';는 React 17+ 자동 JSX 런타임(react-jsx)을 사용 중이라면 불필요할 수 있습니다. 현재 프로젝트 내 주요
tsconfig.json들의jsx설정은 다음과 같습니다:
- apps/extension/tsconfig.json:
compilerOptions.jsx = react-jsx- packages/design-system/tsconfig.json:
compilerOptions.jsx = react-jsx- apps/client/tsconfig.json:
compilerOptions.jsx미설정 (기본값 사용)- apps/landing/tsconfig.json:
compilerOptions.jsx미설정 (기본값 사용)∘
stories/Button.tsx파일이 어떤tsconfig.json(또는 별도 Storybook 전용 설정)을 상속받는지 확인
∘ TS 컴파일러 측에서 자동 런타임이 활성화되어 있다면 불필요한 import 제거 가능
∘ 필요 시 Babel(@babel/preset-react) 혹은 Storybook 메인 설정(.storybook/main.js)에서의 automatic runtime 설정도 함께 검토위 사항을 점검 후, 자동 런타임이 확실히 적용된 환경이라면 해당
import React from 'react';를 제거해주세요.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (16)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlstories/assets/accessibility.pngis excluded by!**/*.pngstories/assets/accessibility.svgis excluded by!**/*.svgstories/assets/addon-library.pngis excluded by!**/*.pngstories/assets/assets.pngis excluded by!**/*.pngstories/assets/context.pngis excluded by!**/*.pngstories/assets/discord.svgis excluded by!**/*.svgstories/assets/docs.pngis excluded by!**/*.pngstories/assets/figma-plugin.pngis excluded by!**/*.pngstories/assets/github.svgis excluded by!**/*.svgstories/assets/share.pngis excluded by!**/*.pngstories/assets/styling.pngis excluded by!**/*.pngstories/assets/testing.pngis excluded by!**/*.pngstories/assets/theming.pngis excluded by!**/*.pngstories/assets/tutorials.svgis excluded by!**/*.svgstories/assets/youtube.svgis excluded by!**/*.svg
📒 Files selected for processing (17)
.storybook/main.ts(1 hunks).storybook/preview.ts(1 hunks).storybook/vitest.setup.ts(1 hunks)package.json(1 hunks)packages/design-system/src/components/badge/Badge.stories.tsx(1 hunks)stories/Button.stories.ts(1 hunks)stories/Button.tsx(1 hunks)stories/Configure.mdx(1 hunks)stories/Header.stories.ts(1 hunks)stories/Header.tsx(1 hunks)stories/Page.stories.ts(1 hunks)stories/Page.tsx(1 hunks)stories/button.css(1 hunks)stories/header.css(1 hunks)stories/page.css(1 hunks)vitest.config.ts(1 hunks)vitest.shims.d.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-23T18:42:42.797Z
Learnt from: jjangminii
PR: Pinback-Team/pinback-client#22
File: packages/design-system/package.json:32-35
Timestamp: 2025-08-23T18:42:42.797Z
Learning: Storybook packages have different release cycles and stable versions. As of 2025, storybook/test and storybook/blocks have their latest stable versions in the 8.x line (8.6.14), while other packages like storybook/react-vite are stable in 9.x line (9.1.3). Mixed major versions across Storybook packages can be intentional and correct when using the latest stable version of each package.
Applied to files:
package.json
🧬 Code graph analysis (5)
stories/Header.stories.ts (1)
stories/Header.tsx (1)
Header(17-56)
stories/Page.stories.ts (2)
stories/Page.tsx (1)
Page(10-73)stories/Header.stories.ts (2)
LoggedOut(34-34)LoggedIn(26-32)
stories/Page.tsx (1)
stories/Header.tsx (1)
Header(17-56)
stories/Header.tsx (1)
stories/Button.tsx (1)
Button(19-37)
stories/Button.stories.ts (1)
stories/Button.tsx (1)
Button(19-37)
🪛 GitHub Check: lint
packages/design-system/src/components/badge/Badge.stories.tsx
[warning] 3-3:
'BadgeProps' is defined but never used
🔇 Additional comments (14)
packages/design-system/src/components/badge/Badge.stories.tsx (2)
9-12: 기본 args 합리적 설정 확인기본 값
text: '알림',countNum: 3은 컴포넌트 동작을 직관적으로 보여줍니다. 이 상태 유지에 동의합니다.
1-1: import 경로 유지:@storybook/react-vite설치된 Storybook 패키지 중
@storybook/react-vite는 9.x(현재 v9.1.3) 라인으로, stories 파일에서 사용하는 React-Vite 프레임워크에 맞는 올바른 import 경로입니다.@storybook/blocks와@storybook/test만 8.x 라인이지만, 이는 별도 모듈이므로 React용 타입 import에는 영향이 없습니다.
- packages/design-system/src/components/badge/Badge.stories.tsx (1행)
→ import type { Meta, StoryObj } from '@storybook/react-vite'; (변경 불필요)Likely an incorrect or invalid review comment.
.storybook/preview.ts (1)
12-17: @storybook/addon-a11y ‘test’ 파라미터 지원 여부 확인 요청사용 중인 @storybook/addon-a11y 패키지 버전과 함께, 로컬에서 아래 명령어들을 실행해 결과를 공유해 주세요.
이 정보가 있어야 ‘test: "todo"’가 공식 스키마에 포함되어 있는지, 그리고 CI 실패 여부를 결정짓는 설정인지 정확히 검증할 수 있습니다.• 패키지 버전 확인
npm ls @storybook/addon-a11y # 또는 yarn why @storybook/addon-a11y• 타입 정의에서 ‘test’ 키 존재 여부 검색 (TypeScript 프로젝트의 경우)
grep -R "interface A11yParameters" -n node_modules/@storybook/addon-a11y sed -n '1,200p' node_modules/@storybook/addon-a11y/dist/index.d.ts | sed -n '/A11yParameters/,/}/p'• 런타임 스키마(JSON)에서 ‘test’ 키 검색
grep -R "\"test\"" -n node_modules/@storybook/addon-a11y위 결과를 알려주시면, 해당 파라미터의 공식 지원 여부를 확정하고
‘todo’ vs ‘error’ 정책 적용 방안 또는 대안(별도 a11y 테스트 러너 등)을 제안드리겠습니다.stories/button.css (1)
11-13: 색상 대비 AA 기준 충족 확인됨
- 대상 파일:
stories/button.css(11–13행)- 코드 스니펫:
background-color: #555ab9; color: white; }- 측정된 대비: 5.92:1 (WCAG AA 일반 텍스트 기준 4.5:1 이상 충족)
현재 상태 그대로 유지해도 무방합니다. 추후 다크 테마 도입 시 대비만 재점검 부탁드립니다.
.storybook/vitest.setup.ts (1)
5-7: 구성 적용 순서 적절(LGTM)애드온 애너테이션 후 프로젝트 애너테이션을 적용해 프로젝트 설정이 우선하도록 한 점이 좋습니다.
stories/Page.tsx (1)
10-20: 상태 전환 플로우 명확(LGTM)헤더 콜백과 로컬 상태만으로 로그인/로그아웃이 깔끔히 재현됩니다. 스토리 상호작용 테스트와도 정합성이 좋습니다.
stories/Page.stories.ts (1)
10-13: 스토리 구성 합리적(LGTM)풀스크린 레이아웃 파라미터 지정이 페이지 성격에 적절합니다.
stories/Header.stories.ts (1)
26-34: 스토리 구성 간결(LGTM)
LoggedIn/LoggedOut스토리로 상태 차이를 명확히 드러내고 있어 문서성과 테스트 재사용성이 좋습니다.stories/page.css (1)
1-9: 범위 한정(.storybook-page)으로 전역 오염 방지한 점 좋습니다Storybook 전용 스타일을 상위 클래스에 네임스페이스해 전역 CSS를 오염시키지 않는 방식은 적절합니다.
stories/Header.tsx (1)
17-56: 조건부 렌더링과 props 설계가 간결합니다user 유무에 따른 분기와 콜백 전달이 직관적입니다. Storybook 데모 컴포넌트로서 구조가 명료합니다.
vitest.config.ts (2)
18-19:extends: true사용 의도 확인 요청프로젝트별 설정이 상위 test 옵션을 상속하도록 한 듯하나, 현 설정엔 상속할 상위 test 옵션이 없습니다(projects만 존재). 불필요하면 제거해 단순화하세요.
확인 지침:
- 상위
test루트에 공통 옵션이 없다면extends: true제거 시 동작 동일해야 합니다.- 제거 후 Vitest가 동일하게 Storybook 프로젝트만 실행되는지 로컬에서 한 번 검증 부탁드립니다.
6-7: 필수 devDependencies 누락:@storybook/test설치 필요스크립트 실행 결과, 아래 패키지들은 모두 존재하지만
@storybook/test만 누락된 것으로 확인되었습니다. 스토리북 Vitest 통합을 위해 반드시 추가해주세요.검사 결과 요약:
- OK – @storybook/addon-vitest ^9.1.3
- OK – @storybook/react-vite ^9.1.3
- MISS – @storybook/test
- OK – vitest ^3.2.4
- OK – playwright ^1.55.0
- OK – .storybook/vitest.setup.ts (파일 존재)
해결 방안:
# npm 사용 시 npm install -D @storybook/test@^9.1.3 # yarn 사용 시 yarn add -D @storybook/test@^9.1.3설치 후 다시 의존성 및 설정 파일 로딩을 확인해주세요.
⛔ Skipped due to learnings
Learnt from: jjangminii PR: Pinback-Team/pinback-client#22 File: packages/design-system/package.json:32-35 Timestamp: 2025-08-23T18:42:42.797Z Learning: Storybook packages have different release cycles and stable versions. As of 2025, storybook/test and storybook/blocks have their latest stable versions in the 8.x line (8.6.14), while other packages like storybook/react-vite are stable in 9.x line (9.1.3). Mixed major versions across Storybook packages can be intentional and correct when using the latest stable version of each package.stories/header.css (1)
1-8: 헤더 컨테이너 스코프와 flex 레이아웃 구성 적절컴포넌트 스코프로 네임스페이스했고 배치·정렬 속성이 명확합니다.
stories/Button.tsx (1)
1-38: PR 범위 확인: 뱃지 컴포넌트 PR에 Button 스토리 파일이 포함됨PR 제목/목표가 Badge 컴포넌트 추가인데, 본 파일은 Storybook용 Button 구현입니다. 의도된 동반 변경인지, 템플릿 잔재인지 확인 부탁드립니다. 불필요하다면 PR에서 제외해 diff 노이즈를 줄이는 것을 권장합니다.
| @@ -0,0 +1,7 @@ | |||
| import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; | |||
There was a problem hiding this comment.
🛠️ Refactor suggestion
addon-a11y 애너테이션 import 방식 수정
namespace import(* as) 대신 기본(default) import를 사용해야 애너테이션 객체가 올바르게 전달됩니다.
-import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
+import a11yAddonAnnotations from '@storybook/addon-a11y/preview';📝 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.
| import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; | |
| import a11yAddonAnnotations from '@storybook/addon-a11y/preview'; |
🤖 Prompt for AI Agents
In .storybook/vitest.setup.ts around line 1, the file currently uses a namespace
import for the addon-a11y annotations which prevents the annotations object from
being passed correctly; replace the namespace import with a default import from
"@storybook/addon-a11y/preview" (i.e. import the default export) and ensure the
default-imported value is used where the annotations are registered or exported
so the addon receives the correct annotations object.
| "@chromatic-com/storybook": "^4.1.1", | ||
| "@pivanov/vite-plugin-svg-sprite": "^3.1.3", | ||
| "@storybook/addon-a11y": "^9.1.3", | ||
| "@storybook/addon-docs": "^9.1.3", | ||
| "@storybook/addon-vitest": "^9.1.3", | ||
| "@storybook/react-vite": "^9.1.3", | ||
| "@trivago/prettier-plugin-sort-imports": "^5.2.2", | ||
| "@types/react": "19", | ||
| "@types/react-dom": "19", | ||
| "prettier": "^3.6.2", | ||
| "prettier-plugin-tailwindcss": "^0.6.14", | ||
| "storybook": "^9.1.3", | ||
| "turbo": "^2.5.6", | ||
| "typescript": "5.9.2", | ||
| "vite": "7.1.2" | ||
| "vite": "7.1.2", | ||
| "vitest": "^3.2.4", | ||
| "@vitest/browser": "^3.2.4", | ||
| "playwright": "^1.55.0", | ||
| "@vitest/coverage-v8": "^3.2.4" | ||
| }, |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
React 타입 버전 정합성 확인(워크스페이스 전반)
루트 devDeps에 @types/react, @types/react-dom가 "19"로 지정되어 있습니다. 워크스페이스 내 실제 react, react-dom 런타임 메이저와 어긋나면 타입 불일치가 발생할 수 있습니다. 각 앱/패키지의 런타임 버전과 맞춰 주세요(예: 모두 18이면 타입도 18로). 루트에만 타입을 고정하면 하위 패키지의 로컬 타입 해상도에 간섭할 수 있으니, 필요 시 패키지별 devDeps로 내려 배치하는 것도 고려해 주세요.
🏁 Script executed:
#!/bin/bash
# 워크스페이스 전체의 react/react-dom 및 타입 버전 매트릭스 출력
rg -nP --glob '!node_modules' --glob '!*dist*' '"react(?:-dom)?"\s*:\s*"[^\"]+"' package.json -C2
fd -a -t f -g "package.json" apps packages | xargs -I{} sh -c 'echo "---- {} ----"; jq -r ".dependencies.react, .dependencies[\"react-dom\"], .devDependencies[\"@types/react\"], .devDependencies[\"@types/react-dom\"]" "{}" 2>/dev/null'Length of output: 946
React 타입 버전 불일치: apps/extension 패키지 수정 필요
apps/extension/package.json의 런타임 React 버전(^19.1.1)과 로컬에 선언된 타입 패키지(@types/react ^18.3.5, @types/react-dom ^18.3.0)가 맞지 않아 타입 에러가 발생할 수 있습니다. 아래와 같이 조치해주세요.
apps/extension/package.jsondevDependencies에서"@types/react": "^18.3.5"→"^19.1.0"(또는 루트devDependencies에 맞춘"19")"@types/react-dom": "^18.3.0"→"^19.1.0"(또는"19")
- 필요 시 해당 로컬 타입 선언을 제거하고 루트에 선언된
@types/react,@types/react-dom을 사용하도록 조정
- (선택) 워크스페이스 전반에서 타입 동결을 원한다면 루트에만
@types/react/@types/react-dom"19"를 유지하고, 하위 패키지에 별도 선언은 제거
위 변경 후 pnpm install 또는 yarn install 실행하여 의존성 충돌이 없는지 확인 바랍니다.
| "vite": "7.1.2", | ||
| "vitest": "^3.2.4", | ||
| "@vitest/browser": "^3.2.4", | ||
| "playwright": "^1.55.0", | ||
| "@vitest/coverage-v8": "^3.2.4" |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Vite 7.1.2와 @storybook/react-vite 9.1.3 호환성 점검
pnpm.overrides로 Vite 7.1.2를 강제 고정하고 있습니다. 현재 @storybook/react-vite@^9.1.3과의 공식 호환 범위를 한 번 확인해 주세요. 호환 문제가 없으면 고정 유지, 경고가 있다면 Vite를 권장 범위로 조정하는 것이 안전합니다.
🌐 Web query:
@storybook/react-vite 9.1.3이 공식적으로 지원하는 Vite 메이저/마이너 버전 범위를 확인해 주세요. Vite 7.1.x 호환 여부도 함께 확인 바랍니다.
💡 Result:
검색을 수행해 관련 문서를 확인했습니다.
결론 요약:
- @storybook/react-vite 9.1.3의 package.json engines / peerDependencies 등에 명시된 공식적으로 지원하는 Vite 버전 범위는 "vite": "^5.0.0 || ^6.0.0"으로 표시되어 있습니다 [1][2]. 즉 Vite 메이저 버전으로는 5.x 및 6.x 를 공식 지원 대상으로 명시하고 있습니다.
- 따라서 Vite 7.1.x 는 공식 지원 범위에 포함되지 않습니다(호환성 보장은 없음). 실제로 Storybook 9.2 이상 또는 관련 릴리스 노트를 확인해야 Vite 7 지원 여부를 확정할 수 있습니다 [1][3].
근거(참고 사이트):
- npm 패키지 소스(package.json) 또는 GitHub 배포의 peerDependencies/engines 항목 — @storybook/react-vite 9.1.3 (해당 파일의 Vite 버전 범위 표기) [링크 제공 원문 소스].
- npmjs.org의 @storybook/react-vite@9.1.3 페이지 (peerDependencies 정보) [링크 제공 원문 소스].
- Storybook 릴리스 노트 / 마이그레이션 가이드 (Vite 관련 지원 변경사항 확인용) [링크 제공 원문 소스].
원하시면 제가 각 출처의 정확한 URL(패키지.json 스니펫, npm 페이지, 릴리스 노트)을 첨부해 드리겠습니다.
@storybook/react-vite@9.1.3 호환 Vite 버전 범위 확인 필요
공식 peerDependencies에 “vite”: “^5.0.0 || ^6.0.0”으로만 명시되어 있어 Vite 7.x(예: 7.1.2)는 지원 범위에 포함되지 않습니다 [1][2]. 이 상태로 유지할 경우 Storybook 실행 시 호환성 문제가 발생할 수 있으니 아래 중 하나를 적용해주세요.
• Vite 버전을 6.x (예: 6.9.5)로 다운그레이드
• 또는 Storybook을 9.2 이상으로 업그레이드하여 Vite 7.x 지원 여부를 확인 후 버전 조정
References:
[1] @storybook/react-vite@9.1.3 package.json peerDependencies
[2] npmjs.org의 @storybook/react-vite@9.1.3 peerDependencies 정보
🤖 Prompt for AI Agents
In package.json around lines 29-33, the declared Vite version "7.1.2" is outside
the peerDependency range of @storybook/react-vite@9.1.3; either pin Vite to a
compatible 6.x release (e.g., "vite": "6.9.5") or upgrade @storybook/react-vite
to a version that explicitly supports Vite 7.x (e.g., >=9.2) and then align
package.json versions accordingly, run a fresh install, and verify Storybook
starts without peerDependency warnings or runtime errors.
stories/Button.stories.ts
Outdated
| import { fn } from 'storybook/test'; | ||
|
|
There was a problem hiding this comment.
테스트 유틸 import 경로 오류: '@storybook/test' 사용 필요
storybook/test는 유효하지 않습니다. Storybook의 테스트 유틸은 @storybook/test에서 가져와야 합니다.
-import { fn } from 'storybook/test';
+import { fn } from '@storybook/test';📝 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.
| import { fn } from 'storybook/test'; | |
| import { fn } from '@storybook/test'; |
🤖 Prompt for AI Agents
stories/Button.stories.ts lines 3-4: the import path is incorrect — replace the
invalid "storybook/test" import with the correct "@storybook/test" package;
update the import statement to import fn from '@storybook/test' so the Storybook
testing utilities are resolved from the proper package.
stories/Button.tsx
Outdated
| export interface ButtonProps { | ||
| /** Is this the principal call to action on the page? */ | ||
| primary?: boolean; | ||
| /** What background color to use */ | ||
| backgroundColor?: string; | ||
| /** How large should the button be? */ | ||
| size?: 'small' | 'medium' | 'large'; | ||
| /** Button contents */ | ||
| label: string; | ||
| /** Optional click handler */ | ||
| onClick?: () => void; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
표준 버튼 속성(ARIA/disabled/className 등) 수용을 위해 타입을 확장하세요
현재 ButtonProps는 onClick만 선언되어 있어 JSX에서 disabled, aria-*, className 같은 표준 속성을 전달하면 타입 오류가 납니다. 또한 onClick의 타입도 () => void 보다는 React.MouseEventHandler가 적절합니다. 디자인 시스템 컴포넌트는 기본 버튼 속성을 모두 통과시킬 수 있어야 하므로 ButtonHTMLAttributes로 확장하는 편이 안전합니다.
아래와 같이 최소 수정안을 제안합니다:
-export interface ButtonProps {
+export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/** Is this the principal call to action on the page? */
primary?: boolean;
/** What background color to use */
backgroundColor?: string;
/** How large should the button be? */
size?: 'small' | 'medium' | 'large';
/** Button contents */
label: string;
- /** Optional click handler */
- onClick?: () => void;
}📝 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.
| export interface ButtonProps { | |
| /** Is this the principal call to action on the page? */ | |
| primary?: boolean; | |
| /** What background color to use */ | |
| backgroundColor?: string; | |
| /** How large should the button be? */ | |
| size?: 'small' | 'medium' | 'large'; | |
| /** Button contents */ | |
| label: string; | |
| /** Optional click handler */ | |
| onClick?: () => void; | |
| } | |
| export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { | |
| /** Is this the principal call to action on the page? */ | |
| primary?: boolean; | |
| /** What background color to use */ | |
| backgroundColor?: string; | |
| /** How large should the button be? */ | |
| size?: 'small' | 'medium' | 'large'; | |
| /** Button contents */ | |
| label: string; | |
| } |
🤖 Prompt for AI Agents
In stories/Button.tsx around lines 5 to 16, ButtonProps currently only declares
a few custom props and an onClick typed as () => void, which prevents passing
standard button attributes (disabled, aria-*, className, etc.) and uses the
wrong event type; update the interface to extend
React.ButtonHTMLAttributes<HTMLButtonElement> (or
React.ButtonHTMLAttributes<HTMLButtonElement>) so all native props are accepted,
change onClick to React.MouseEventHandler<HTMLButtonElement> (or remove it to
inherit the correct type from ButtonHTMLAttributes), and ensure React is
imported for the type references.
stories/Button.tsx
Outdated
| export const Button = ({ | ||
| primary = false, | ||
| size = 'medium', | ||
| backgroundColor, | ||
| label, | ||
| ...props | ||
| }: ButtonProps) => { | ||
| const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; | ||
| return ( | ||
| <button | ||
| type="button" | ||
| className={['storybook-button', `storybook-button--${size}`, mode].join(' ')} | ||
| style={{ backgroundColor }} | ||
| {...props} | ||
| > |
There was a problem hiding this comment.
🛠️ Refactor suggestion
props 스프레드 순서로 className/style이 덮어쓰기 됩니다 — 내부 클래스/스타일과 병합하세요
지금은 {...props}가 뒤에 와서 외부에서 전달된 className, style이 내부 계산값을 통째로 덮습니다. className은 병합하고, style은 배경색을 조건부로 덧씌우는 방식이 바람직합니다. 또한 타입 확장과 함께 className, style을 구조분해하여 rest와 분리해 주세요.
-export const Button = ({
- primary = false,
- size = 'medium',
- backgroundColor,
- label,
- ...props
-}: ButtonProps) => {
- const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
- return (
- <button
- type="button"
- className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
- style={{ backgroundColor }}
- {...props}
- >
+export const Button = ({
+ primary = false,
+ size = 'medium',
+ backgroundColor,
+ label,
+ className,
+ style,
+ ...rest
+}: ButtonProps) => {
+ const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
+ const classes = ['storybook-button', `storybook-button--${size}`, mode, className]
+ .filter(Boolean)
+ .join(' ');
+ const mergedStyle = { ...(style ?? {}), ...(backgroundColor ? { backgroundColor } : {}) };
+ return (
+ <button
+ type="button"
+ className={classes}
+ style={mergedStyle}
+ {...rest}
+ >
{label}
</button>
);
};📝 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.
| export const Button = ({ | |
| primary = false, | |
| size = 'medium', | |
| backgroundColor, | |
| label, | |
| ...props | |
| }: ButtonProps) => { | |
| const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; | |
| return ( | |
| <button | |
| type="button" | |
| className={['storybook-button', `storybook-button--${size}`, mode].join(' ')} | |
| style={{ backgroundColor }} | |
| {...props} | |
| > | |
| export const Button = ({ | |
| primary = false, | |
| size = 'medium', | |
| backgroundColor, | |
| label, | |
| className, | |
| style, | |
| ...rest | |
| }: ButtonProps) => { | |
| const mode = primary | |
| ? 'storybook-button--primary' | |
| : 'storybook-button--secondary'; | |
| const classes = [ | |
| 'storybook-button', | |
| `storybook-button--${size}`, | |
| mode, | |
| className | |
| ] | |
| .filter(Boolean) | |
| .join(' '); | |
| const mergedStyle = { | |
| ...(style ?? {}), | |
| ...(backgroundColor ? { backgroundColor } : {}) | |
| }; | |
| return ( | |
| <button | |
| type="button" | |
| className={classes} | |
| style={mergedStyle} | |
| {...rest} | |
| > | |
| {label} | |
| </button> | |
| ); | |
| }; |
🤖 Prompt for AI Agents
In stories/Button.tsx around lines 19-33, props are currently spread after
computed className/style which allows external className/style to overwrite
internal values; destructure className and style from props (e.g., const {
className: externalClassName, style: externalStyle, ...rest } = props), compute
a mergedClassName that joins internal classes with externalClassName, compute
mergedStyle by shallow-merging externalStyle with the internal style (applying
backgroundColor conditionally so internal backgroundColor overrides only when
provided), then spread rest last onto the button; also update ButtonProps/type
to include optional className and style if not already present.
stories/Configure.mdx
Outdated
| href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure" | ||
| target="_blank" | ||
| >Learn more<RightArrow /></a> |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
보안: target="_blank" 사용시 rel="noopener noreferrer" 추가
탭낚시(reverse tabnabbing) 예방을 위해 외부 링크에 rel 속성 추가가 필요합니다. 본 파일의 모든 target="_blank" 링크에 동일하게 적용해 주세요.
- <a
- href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
- target="_blank"
- >Learn more<RightArrow /></a>
+ <a
+ href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
+ target="_blank"
+ rel="noopener noreferrer"
+ >Learn more<RightArrow /></a>다른 앵커에도 동일 패턴 적용 권장. 자동 점검 스크립트:
🏁 Script executed:
#!/bin/bash
# rel 누락된 target=_blank 링크 찾기
rg -nP 'target="_blank"(?![^>]*rel=)' stories/Configure.mdxLength of output: 469
필수 보안 수정: 모든 target="_blank" 링크에 rel="noopener noreferrer" 추가하기
아래 위치에서 외부 링크에 rel 속성이 누락되어 있습니다. 탭낚시(reverse tabnabbing) 공격을 예방하려면 각 링크에 rel="noopener noreferrer"를 추가해주세요.
• stories/Configure.mdx
• 52행, 64행, 76행, 98행, 107행, 117행, 127행, 136행, 145행, 157행, 172행, 182행, 193행, 203행
예시 수정:
- <a
- href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
- target="_blank"
- >Learn more<RightArrow /></a>
+ <a
+ href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
+ target="_blank"
+ rel="noopener noreferrer"
+ >Learn more<RightArrow /></a>다른 파일 또는 컴포넌트에서도 동일한 패턴이 있다면 마찬가지로 적용해 주세요.
📝 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.
| href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure" | |
| target="_blank" | |
| >Learn more<RightArrow /></a> | |
| <a | |
| href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| >Learn more<RightArrow /></a> |
🤖 Prompt for AI Agents
In stories/Configure.mdx around lines 51 to 53 (and also at lines 64, 76, 98,
107, 117, 127, 136, 145, 157, 172, 182, 193, 203), external anchor tags that use
target="_blank" are missing rel="noopener noreferrer"; update each anchor
element that opens in a new tab to include rel="noopener noreferrer" alongside
target="_blank" (e.g., change <a ... target="_blank"> to <a ... target="_blank"
rel="noopener noreferrer">) ensuring all listed lines are fixed and scan for any
other target="_blank" occurrences in the file to apply the same change.
stories/Header.tsx
Outdated
| <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> | ||
| <g fill="none" fillRule="evenodd"> | ||
| <path |
There was a problem hiding this comment.
🛠️ Refactor suggestion
SVG 접근성: 장식용이면 숨김 처리, 의미가 있다면 대체 텍스트 추가
로고가 장식용이면 스크린리더에서 숨기는 편이 바람직합니다. 의미가 있으면 role/aria-label을 부여하세요.
- <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
+ <svg aria-hidden="true" focusable="false" width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">의미 있는 로고일 경우 대안:
- <svg aria-hidden="true" focusable="false" ...
+ <svg role="img" aria-label="Acme logo" focusable="false" ...📝 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.
| <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> | |
| <g fill="none" fillRule="evenodd"> | |
| <path | |
| <svg aria-hidden="true" focusable="false" width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> | |
| <g fill="none" fillRule="evenodd"> | |
| <path |
| <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> | |
| <g fill="none" fillRule="evenodd"> | |
| <path | |
| <svg role="img" aria-label="Acme logo" focusable="false" width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> | |
| <g fill="none" fillRule="evenodd"> | |
| <path |
🤖 Prompt for AI Agents
In stories/Header.tsx around lines 21-23 the SVG logo currently lacks
accessibility attributes; if the logo is purely decorative add
aria-hidden="true" and focusable="false" to the svg element (and remove
interactive handlers), otherwise mark it as an image by adding role="img" plus a
descriptive aria-label or include a <title> element inside the svg for screen
readers and ensure focusable="false" is set; update the JSX accordingly so
decorative icons are hidden from assistive tech and meaningful logos expose an
accessible name.
constantly-dev
left a comment
There was a problem hiding this comment.
수고하셨습니다~
처음 스토리북 세팅할 때 파일이 남아있는 것처럼 보이는데 코멘트 확인해주세요 👍
| import Badge, { type BadgeProps } from './Badge'; | ||
|
|
||
| const meta: Meta<typeof Badge> = { | ||
| title: 'UI/Badge', |
There was a problem hiding this comment.
title: 'Components/Badge'로 통일합시다!
| } | ||
| const BadgeTxtStyleVariants = cva('sub3-b', { | ||
| variants: { | ||
| click: { |
There was a problem hiding this comment.
boolean값이니 isClick으로 바꾸는 것은 어떨까요?
There was a problem hiding this comment.
오 boolean은 최대한 가독성 위해서 is~으로 맞춰서 작업해두겠습니다
stories/Header.tsx
Outdated
There was a problem hiding this comment.
초기 설정할 때 나오는 Header나 Page이런 부분이 지금 생성되었나요????
전에 지웠던 걸로 기억하는데! 밑에 assets도!
There was a problem hiding this comment.
제가 init 잘못 초기세팅을 중복하게 하면서 생긴 폴더 같네요..! 확인해보겠습니다!
| docs: { | ||
| description: { | ||
| component: | ||
| '클릭 시 내부 state(isClick)가 true로 바뀌며 스타일이 활성화됩니다. 토글 방식은 아닙니다.!', |
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/design-system/src/components/badge/Badge.stories.tsx (1)
6-6: Storybook 타이틀 컨벤션 준수 확인타이틀을
Components/Badge로 통일한 점 좋습니다. 이전 코멘트가 잘 반영되었습니다.
🧹 Nitpick comments (4)
packages/design-system/src/components/badge/Badge.stories.tsx (4)
3-3: 불필요한 타입 import 제거로 lint 경고 해소
BadgeProps를 가져오지만 사용하지 않아 lint 경고가 발생합니다. 불필요한 import를 제거해 깨끗한 빌드 유지하세요.-import Badge, { type BadgeProps } from './Badge'; +import Badge from './Badge';
2-2: play 함수에 어설션 추가로 회귀 탐지 강화현재는 클릭만 수행합니다. 간단한 어설션을 추가해 상호작용의 결과가 실제로 반영되는지 검증하도록 보강하는 것을 권장합니다(속성/셀렉터는 컴포넌트 구현에 맞게 조정).
-import { within, userEvent } from '@storybook/test'; +import { within, userEvent, expect } from '@storybook/test'; @@ play: async ({ canvasElement, args }) => { const canvas = within(canvasElement); - await userEvent.click(await canvas.findByText(String(args.text))); + const label = await canvas.findByText(String(args.text)); + await userEvent.click(label); + // 예: 활성화 시 data-state="active"가 걸린다고 가정. 구현에 맞게 조정하세요. + await expect(label).toHaveAttribute('data-state', 'active'); },Also applies to: 45-48
35-41: 0 카운트 케이스 스토리 추가 제안
undefined와0의 UX가 다를 수 있습니다(예: 0이면 표시/비표시 정책).ZeroCount스토리를 추가해 의도를 명확히 하고 회귀를 예방하세요.export const NoCount: Story = { args: { text: '카운트 없음', countNum: undefined }, }; +export const ZeroCount: Story = { + args: { text: '카운트 0', countNum: 0 }, +}; + export const LargeCount: Story = { args: { text: '메시지', countNum: 12000 }, };
5-5: 타입 안전성 미세 개선:satisfies활용현재도 문제는 없지만,
satisfies를 사용하면meta객체가 과도하게 확장되는 것을 방지하면서 타입 체크가 더 엄격해집니다.-const meta: Meta<typeof Badge> = { +const meta = { @@ -}; +} satisfies Meta<typeof Badge>;Also applies to: 28-28
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/design-system/src/components/badge/Badge.stories.tsx(1 hunks)
🧰 Additional context used
🪛 GitHub Check: lint
packages/design-system/src/components/badge/Badge.stories.tsx
[warning] 3-3:
'BadgeProps' is defined but never used
🔇 Additional comments (1)
packages/design-system/src/components/badge/Badge.stories.tsx (1)
1-1:@storybook/react-vite경로가 올바릅니다.
- 모노레포 루트와
packages/design-system모두@storybook/react-vite@^9.1.3이 설치되어 있습니다.@storybook/react패키지는 어디에도 설치되어 있지 않습니다.- Vite 빌더 기반 Storybook 구성에서는
@storybook/react-vite에서 타입을 가져오는 것이 권장되는 방식이므로 변경이 필요 없습니다.Likely an incorrect or invalid review comment.
📌 Related Issues
✅ 체크 리스트
📄 Tasks
⭐ PR Point (To Reviewer)
📷 Screenshot
Summary by CodeRabbit