Skip to content

Feat(design-system): 레벨 컴포넌트#36

Merged
jllee000 merged 9 commits intodevelopfrom
feat/#35/level-component
Aug 25, 2025
Merged

Feat(design-system): 레벨 컴포넌트#36
jllee000 merged 9 commits intodevelopfrom
feat/#35/level-component

Conversation

@jllee000
Copy link
Collaborator

@jllee000 jllee000 commented Aug 25, 2025

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

✅ 체크 리스트

  • PR 제목의 형식을 잘 작성했나요? e.g. Feat(client): PR 템플릿 작성
  • 빌드가 성공했나요? (pnpm build)

📄 Tasks

크게 복잡하지 않아서, 단순 레벨 숫자 받아서 구현했어요

⭐ PR Point (To Reviewer)

📷 Screenshot

image

Summary by CodeRabbit

  • 신기능
    • Storybook 환경 도입(React-Vite) 및 주요 컴포넌트 스토리 추가(Button/Header/Page, Level).
    • 디자인 시스템에 Level 배지 컴포넌트 추가 및 공개(export).
  • 문서
    • Storybook 안내/구성 MDX 페이지 추가로 사용 가이드 강화.
  • 테스트
    • Vitest + Playwright 기반의 Storybook 브라우저 테스트 설정 추가(프로젝트 어노테이션, 설정 파일 포함).
  • 작업(Chores)
    • Storybook 실행/빌드 스크립트 및 관련 의존성 추가.
  • 스타일
    • 앱 헤더 문구 라인 포맷 조정(표현 변화 없음).

@jllee000 jllee000 self-assigned this Aug 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 25, 2025

Warning

Rate limit exceeded

@jllee000 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 15 minutes and 38 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between cdf8494 and fe2a58b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • packages/design-system/src/components/index.ts (1 hunks)
  • packages/design-system/src/components/level/Level.stories.tsx (1 hunks)

Walkthrough

Storybook and Vitest testing infrastructure were added. A new Level component was introduced in the design system with associated Storybook stories and export. Example Storybook components and docs (Button, Header, Page, MDX) were included. package.json scripts and devDependencies were updated. Minor formatting change in apps/client/src/App.tsx.

Changes

Cohort / File(s) Summary
Storybook Config
\.storybook/main.ts, \.storybook/preview.ts
Added Storybook setup for React-Vite, addons (docs, a11y, vitest, Chromatic), and preview parameters.
Vitest + Storybook Test Runner
vitest.config.ts, \.storybook/vitest.setup.ts, vitest.shims.d.ts
Configured Vitest project with Storybook test plugin, Playwright browser provider, and a11y/project annotations.
Design System: Level Component
packages/design-system/src/components/level/Level.tsx, packages/design-system/src/components/index.ts
Implemented Level badge component and exported it via components index.
Level Stories
packages/design-system/src/components/level/Level.stories.tsx
Added Storybook stories for Level with numeric control and presets (1, 10, 99).
Example Storybook Components
stories/Button.tsx, stories/Button.stories.ts, stories/Header.tsx, stories/Header.stories.ts, stories/Page.tsx, stories/Page.stories.ts, stories/Configure.mdx
Added example components, stories, interaction test (play function), and MDX docs page.
Story Styles
stories/button.css, stories/header.css, stories/page.css
Added CSS for example Storybook components and pages.
Client App Minor Edit
apps/client/src/App.tsx
Formatting change: trailing backslash after h1 line.
Tooling Updates
package.json
Added Storybook/Vitest/Playwright scripts and devDependencies.

Sequence Diagram(s)

sequenceDiagram
  actor Dev as Developer
  participant SB as Storybook (React-Vite)
  participant DS as Level Component
  Dev->>SB: Run "pnpm storybook"
  SB->>SB: Load addons (docs, a11y, vitest)
  SB->>DS: Render Level with args (level)
  DS-->>SB: JSX (Lv.{level})
  SB-->>Dev: Interactive story UI
Loading
sequenceDiagram
  participant VT as Vitest
  participant SBTR as Storybook Test Runner
  participant PW as Playwright (Chromium)
  VT->>SBTR: Load project annotations (a11y + preview)
  SBTR->>PW: Launch headless browser
  PW->>SBTR: Execute story interactions (play)
  SBTR-->>VT: Report results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Progress bar 구현 (close #24) [#25] 프로그래스바 컴포넌트/스타일/스토리가 추가되지 않음.
Level 컴포넌트 제작 [#35]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Example Button component and stories added (stories/Button.tsx, stories/Button.stories.ts) 링크된 이슈(#25, #35)와 직접 관련 없음; 데모/예시 컴포넌트.
Example Header component and stories added (stories/Header.tsx, stories/Header.stories.ts) Level 또는 Progress 요구사항과 무관한 예시용 UI.
Example Page component and stories with play test (stories/Page.tsx, stories/Page.stories.ts) 연결된 기능 목표와 무관한 스토리북 예시/테스트.
Configure 문서 페이지 추가 (stories/Configure.mdx) 기능 구현 목표와 직접적 연관 없음; 문서/가이드.
apps/client/src/App.tsx 하위 포맷 변경 기능 요구와 무관한 사소한 포맷 변경.

Possibly related PRs

Suggested labels

🛠️ Feature, frontend, documentation, needs-review

Suggested reviewers

  • constantly-dev
  • jjangminii

Poem

새 뱃지 “Lv.” 반짝, 나 당근처럼 기뻐요! 🥕
스토리북 문 열고 톡톡, 이야길 펼쳐요.
비테스트 바람 타고 테스트도 숭숭—
버튼·헤더 친구들도 구경 와서 쿵짝!
오늘도 릴리즈 밭에 별이 하나 더.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#35/level-component

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the feat 기능 개발하라 개발 달려라 달려 label Aug 25, 2025
@github-actions github-actions bot requested a review from jjangminii August 25, 2025 15:43
@github-actions
Copy link

github-actions bot commented Aug 25, 2025

✅ Storybook chromatic 배포 확인:
🐿️ storybook

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Nitpick comments (23)
.storybook/preview.ts (2)

1-1: 타입 import 출처 점검: Preview 타입은 보통 '@storybook/react'에서 import

현재 Preview 타입을 @storybook/react-vite 에서 가져오고 있는데, 일반적으로는 @storybook/react에서 import 합니다. 프로젝트의 Storybook 버전 매트릭스에 따라 타입 출처가 달라질 수 있으니 타입 에러 여부를 확인해 주세요. 문제가 있다면 아래처럼 변경을 고려해 주세요.

- import type { Preview } from '@storybook/react-vite';
+ import type { Preview } from '@storybook/react';

참고: 학습 메모에 따라 패키지 간 메이저 버전이 섞일 수 있음(예: react-vite 9.x, blocks/test 8.x). 혼용 자체는 문제 아닙니다만, 타입 선언의 출처는 일관되어야 합니다.


12-17: a11y 설정의 사용자 정의 필드(test) 사용 확인

parameters.a11y.test = 'todo' 는 공식 a11y 애드온의 표준 키가 아닐 수 있습니다. 테스트 러너나 내부 규약에서 소비되는 커스텀 파라미터라면 좋습니다만, 타입 상으로 경고가 난다면 as any 캐스팅 또는 별도 프로젝트 타입 확장을 고려하세요. CI에서 접근성을 경고(=todo)로만 처리하려는 의도라면 팀 합의도 함께 문서화해 주세요.

packages/design-system/src/components/level/Level.tsx (2)

1-10: 접근성·확장성 개선: aria 라벨 기본값과 className 전달 지원

현재 시각적 표기 "Lv."는 스크린 리더에서 의미가 모호할 수 있습니다. 또한 외부에서 여백/색상 미세 조정이 필요할 수 있으니 className 전달을 허용하면 재사용성이 좋아집니다. 아래와 같이 개선을 제안합니다.

-interface LevelProps {
-  level: number;
-}
-const Level = ({ level }: LevelProps) => {
+import type { HTMLAttributes } from 'react';
+
+export interface LevelProps extends HTMLAttributes<HTMLSpanElement> {
+  level: number;
+  className?: string;
+}
+const Level = ({ level, className, ...rest }: LevelProps) => {
   return (
-    <span className="caption2-sb text-main600 bg-main100 rounded-[0.4rem] px-[0.8rem] py-[0.2rem]">
-      Lv.{level}
-    </span>
+    <span
+      className={`caption2-sb text-main600 bg-main100 rounded-[0.4rem] px-[0.8rem] py-[0.2rem] ${className ?? ''}`}
+      aria-label={rest['aria-label'] ?? `레벨 ${level}`}
+      {...rest}
+    >
+      Lv.{level}
+    </span>
   );
 };

효과:

  • 스크린 리더에 “레벨 N”으로 읽히도록 기본 aria-label 제공.
  • 소비자가 className·기타 span 속성(예: title, data-*)을 전달 가능.
  • LevelProps 공개(export)로 타입 재사용성 향상.

12-12: 타입 재노출 고려

디자인 시스템 소비자가 LevelProps 타입을 활용할 수 있도록 타입도 함께 export 하는 것을 권장합니다(아래 index.ts 코멘트 참고).

packages/design-system/src/components/index.ts (1)

4-4: 공개 API 확장 LGTM + 타입 재노출 제안

Level 컴포넌트 공개 추가는 적절합니다. 함께 LevelProps 타입도 재노출하면 소비자 입장에서 사용성이 향상됩니다.

추가 제안(이 변경은 해당 파일의 다른 라인에 적용되어야 합니다):

// Level.tsx에서 `export interface LevelProps ...` 형태로 공개한 뒤
export type { LevelProps } from './level/Level';
.storybook/main.ts (2)

9-11: 반환 타입을 구체화하세요.

getAbsolutePath의 반환 타입을 any 대신 string으로 명시하면 타입 안전성이 올라갑니다.

-function getAbsolutePath(value: string): any {
+function getAbsolutePath(value: string): string {
   return dirname(require.resolve(join(value, 'package.json')))
 }

1-11: .storybook/main.ts require 사용 환경 검증 완료 (선택적 리팩터링 제안)

루트 레포지토리의 package.json"type" 필드가 없으므로(기본 CJS 모드), 현재 .storybook/main.tsrequire 호출은 정상 작동합니다. run_scripts

다만, 프로젝트 전반 또는 하위 패키지들이 이미 ESM 모드("type": "module")로 설정된 상태이며, 추후 루트까지 ESM 전환 혹은 Storybook 설정 파일을 Vite가 ESM으로 로드하게 될 경우 require가 정의되지 않아 실행 오류가 발생할 가능성이 있습니다.
– 워크스페이스 내 다수 패키지에 "type": "module" 설정 확인됨 run_scripts

따라서 당장은 변경이 불필요하지만, 장기적인 안정성을 위해 아래 대안 패치를 선택적으로 적용하실 것을 권장드립니다:

+import { createRequire } from 'module';
 import type { StorybookConfig } from '@storybook/react-vite';
 import { join, dirname } from "path"

+// ESM 환경에서도 동작하는 안전한 require 생성
+const require: NodeRequire = typeof createRequire === 'function'
+  ? createRequire(import.meta.url)
+  : (eval('require') as NodeRequire);

 /**
  * 패키지의 절대 경로를 resolver 기반으로 계산합니다.
  * Yarn PnP 또는 모노레포 환경 지원을 위해 필요합니다.
  */
-function getAbsolutePath(value: string): any {
+function getAbsolutePath(value: string): string {
   return dirname(require.resolve(join(value, 'package.json')))
 }
stories/page.css (1)

1-69: 스토리북 전용 네임스페이스로 잘 격리되어 있습니다.

.storybook-page 스코프를 사용하여 스타일 누출을 방지하는 접근이 좋습니다. 추가로 색상/폰트 값을 디자인 토큰(CSS 변수)로 추출하면 향후 테마 변경이 쉬워집니다.

packages/design-system/src/components/level/Level.stories.tsx (1)

19-36: 샘플 스토리 구성이 적절합니다.

Default, Level10, Level99로 경계값에 가까운 값을 포함시킨 점이 유용합니다. 추후 접근성 테스트를 위해 play 함수(예: a11y 체크) 추가를 고려해도 좋겠습니다.

원하시면 @storybook/addon-a11y와 연동된 간단한 play 테스트 예시를 추가해 드릴게요.

stories/Configure.mdx (1)

18-33: RightArrow 스타일 객체에 유효하지 않은 키가 포함되어 있습니다.

'path fill'은 React 인라인 스타일에서 유효하지 않습니다. SVG 전체에 fill: currentColor를 적용하면 하위 path에도 상속됩니다. 불필요한 속성을 제거하세요.

 export const RightArrow = () => <svg 
     viewBox="0 0 14 14" 
     width="8px" 
     height="14px" 
     style={{ 
       marginLeft: '4px',
       display: 'inline-block',
       shapeRendering: 'inherit',
       verticalAlign: 'middle',
       fill: 'currentColor',
-      'path fill': 'currentColor'
     }}
 >
.storybook/vitest.setup.ts (1)

1-3: 문자열 따옴표 스타일 통일 제안

1~3라인에서 큰따옴표/작은따옴표가 혼재합니다. ESLint/Prettier 규칙에 맞춰 일관되게 유지하면 리뷰 소음이 줄어듭니다.

예: 모두 작은따옴표로 통일

-import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
+import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
vitest.config.ts (2)

22-22: 상대 경로를 절대 경로로 고정하여 작업 디렉터리 의존성 제거

테스트 실행 위치에 따라 '.storybook' 상대 경로가 해석 실패할 수 있습니다. 루트 기반 절대 경로로 지정하세요.

-          storybookTest({ configDir: path.join(dirname, '.storybook') }),
+          storybookTest({ configDir: path.resolve(dirname, '.storybook') }),
@@
-          setupFiles: ['.storybook/vitest.setup.ts'],
+          setupFiles: [path.resolve(dirname, '.storybook/vitest.setup.ts')],

Also applies to: 32-32


18-18: extends: true 사용 확인

Vitest 멀티 프로젝트에서 extends: true는 상위 test 설정을 상속할 때 사용합니다. 현재 상위에 상속할 옵션이 거의 없으므로 제거해도 동작은 동일할 가능성이 큽니다. 팀 규칙에 따라 유지/삭제를 결정해 주세요.

stories/Page.tsx (1)

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/Header.tsx (2)

42-45: 시맨틱 마크업: b 대신 strong 사용 권장

시각적 굵기 강조뿐 아니라 의미적 강조로 이 적합합니다.

-            <span className="welcome">
-              Welcome, <b>{user.name}</b>!
-            </span>
+            <span className="welcome">
+              Welcome, <strong>{user.name}</strong>!
+            </span>

10-15: 브랜드 텍스트 하드코딩 해제: 타이틀을 Prop으로 외부화

디자인 시스템 관점에서 "Acme"를 Prop으로 외부화하면 재사용성과 i18n 유연성이 높아집니다(기본값 유지).

 export interface HeaderProps {
   user?: User;
   onLogin?: () => void;
   onLogout?: () => void;
   onCreateAccount?: () => void;
+  /** 헤더 타이틀(브랜드명) */
+  title?: string;
 }
 
-export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (
+export const Header = ({ user, onLogin, onLogout, onCreateAccount, title = 'Acme' }: HeaderProps) => (
   <header>
     <div className="storybook-header">
       <div>
         <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
@@
-        <h1>Acme</h1>
+        <h1>{title}</h1>

Also applies to: 17-37

stories/Page.stories.ts (2)

23-33: 상호작용 테스트의 동기화 향상: findByRole/queryByRole 사용

리렌더 이후 같은 DOM 노드를 참조하는 패턴은 불안정할 수 있습니다. findByRole 로 대기하고 queryByRole 로 부재를 검증하도록 바꾸면 플레이 함수의 안정성이 올라갑니다.

-  play: async ({ canvasElement }) => {
+  play: async ({ canvasElement }) => {
     const canvas = within(canvasElement);
-    const loginButton = canvas.getByRole('button', { name: /Log in/i });
-    await expect(loginButton).toBeInTheDocument();
-    await userEvent.click(loginButton);
-    await expect(loginButton).not.toBeInTheDocument();
-
-    const logoutButton = canvas.getByRole('button', { name: /Log out/i });
-    await expect(logoutButton).toBeInTheDocument();
+    const loginButton = await canvas.findByRole('button', { name: /Log in/i });
+    await userEvent.click(loginButton);
+    await expect(canvas.queryByRole('button', { name: /Log in/i })).not.toBeInTheDocument();
+    const logoutButton = await canvas.findByRole('button', { name: /Log out/i });
+    await expect(logoutButton).toBeInTheDocument();
   },

1-1: 스토리북 타입 import 경로 일관성 유지 제안

검증 결과, stories 디렉터리 내 아래 3개 파일에서 모두

import type { Meta, StoryObj } from '@storybook/react-vite';

를 사용하고 있는 것을 확인했습니다.

  • stories/Page.stories.ts
  • stories/Header.stories.ts
  • stories/Button.stories.ts

타입(Meta, StoryObj)은 @storybook/react-vite 대신 공식 권장 경로인 @storybook/react에서 가져오는 것이 IDE 및 타입 경험 측면에서 보다 일관적입니다. 선택적 리팩터링으로 적용을 권장드립니다.

-import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { Meta, StoryObj } from '@storybook/react';
stories/button.css (1)

10-30: 비활성/호버 상태와 테마 확장성 개선 제안

  • 비활성 상태 스타일이 없어 UX 일관성이 떨어질 수 있습니다.
  • 색상 하드코딩(#555ab9 등) 대신 CSS 변수로 전환하면 테마 확장성이 좋아집니다.
 .storybook-button--primary {
-  background-color: #555ab9;
+  background-color: var(--sb-btn-primary-bg, #555ab9);
   color: white;
 }
 
 .storybook-button--secondary {
-  box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+  box-shadow: rgba(0, 0, 0, 0.15) 0 0 0 1px inset;
   background-color: transparent;
-  color: #333;
+  color: var(--sb-btn-fg, #333);
 }
+
+.storybook-button:hover {
+  filter: brightness(0.98);
+}
+
+.storybook-button:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+  filter: none;
+}
stories/Button.tsx (2)

5-16: 표준 버튼 속성 지원을 위해 HTMLButtonAttributes 상속

현재 인터페이스는 aria-*, data-*, disabled, type 등 표준 속성을 충분히 수용하지 못합니다. React.ButtonHTMLAttributes 를 상속해 사용성을 높이세요.

-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;
+}

18-37: forwardRef 및 className 병합으로 확장성 향상

  • ref 전달이 없어 외부에서 포커스 제어나 측정이 곤란합니다.
  • className 을 덮어쓰지 않고 병합하는 패턴이 일반적입니다.
  • 인라인 스타일도 외부 props.style 과 병합하세요.
-/** Primary UI component for user interaction */
-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}
-    >
-      {label}
-    </button>
-  );
-};
+/** Primary UI component for user interaction */
+export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
+  ({ primary = false, size = 'medium', backgroundColor, label, className, style, ...props }, ref) => {
+    const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
+    return (
+      <button
+        type="button"
+        ref={ref}
+        className={['storybook-button', `storybook-button--${size}`, mode, className]
+          .filter(Boolean)
+          .join(' ')}
+        style={{ backgroundColor, ...style }}
+        {...props}
+      >
+        {label}
+      </button>
+    );
+  }
+);
stories/header.css (2)

24-26: 공백 제어는 parent flex 컨테이너의 gap 사용 고려

버튼 사이 여백을 button + button 선택자로 처리하면 요소 추가 시 예측성이 떨어질 수 있습니다. 버튼을 감싸는 컨테이너에 display: flex; gap: 10px;을 주는 방식을 고려해 보세요. 현재 구조에서는 우측 래퍼 <div> 에만 적용하는 것이 안전합니다.

예시(컴포넌트 마크업 변경 없이 적용하려면 우측 컨테이너에 클래스 추가가 필요합니다):

- .storybook-header button + button {
-   margin-left: 10px;
- }
+ .storybook-header .actions {
+   display: flex;
+   gap: 10px;
+   align-items: center;
+ }

그리고 Header.tsx의 우측 컨테이너 div에 클래스명을 추가합니다:

-      <div>
+      <div className="actions">
         {user ? ( /* ... */ ) : ( /* ... */ )}
       </div>

1-8: px → rem 전환 제안(타이포그래피/스페이싱 스케일 대응)

디자인 시스템 확장성을 위해 패딩/폰트 크기 등을 rem 기반으로 운용하는 것이 유지보수에 유리합니다. 필수는 아니지만 장기적으로 이점이 큽니다.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 3cbf52e and cdf8494.

⛔ Files ignored due to path filters (16)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • stories/assets/accessibility.png is excluded by !**/*.png
  • stories/assets/accessibility.svg is excluded by !**/*.svg
  • stories/assets/addon-library.png is excluded by !**/*.png
  • stories/assets/assets.png is excluded by !**/*.png
  • stories/assets/context.png is excluded by !**/*.png
  • stories/assets/discord.svg is excluded by !**/*.svg
  • stories/assets/docs.png is excluded by !**/*.png
  • stories/assets/figma-plugin.png is excluded by !**/*.png
  • stories/assets/github.svg is excluded by !**/*.svg
  • stories/assets/share.png is excluded by !**/*.png
  • stories/assets/styling.png is excluded by !**/*.png
  • stories/assets/testing.png is excluded by !**/*.png
  • stories/assets/theming.png is excluded by !**/*.png
  • stories/assets/tutorials.svg is excluded by !**/*.svg
  • stories/assets/youtube.svg is excluded by !**/*.svg
📒 Files selected for processing (20)
  • .storybook/main.ts (1 hunks)
  • .storybook/preview.ts (1 hunks)
  • .storybook/vitest.setup.ts (1 hunks)
  • apps/client/src/App.tsx (1 hunks)
  • package.json (1 hunks)
  • packages/design-system/src/components/index.ts (1 hunks)
  • packages/design-system/src/components/level/Level.stories.tsx (1 hunks)
  • packages/design-system/src/components/level/Level.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 (2)
📓 Common 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.
📚 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.tsx (1)
stories/Button.tsx (1)
  • Button (19-37)
stories/Page.tsx (1)
stories/Header.tsx (1)
  • Header (17-56)
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/Button.stories.ts (1)
stories/Button.tsx (1)
  • Button (19-37)
🔇 Additional comments (7)
package.json (1)

24-31: 호환성 확인 필요: Vite 7.1.2 + Storybook 9.1.3
현재 스크립트가 node_modules 미설치로 인해 실행되지 않아 실제 설치 트리나 중복 버전 여부를 확인할 수 없습니다. 로컬에서 종속성을 설치한 후 아래 과정을 따라 한 번 더 점검해 주세요.

pnpm installnode_modules 설치
• 설치된 패키지 트리 및 중복 Vite 버전 확인

pnpm ls @storybook/react-vite vite --depth=2 || true

pnpm.overrides.vite 설정 여부 확인

jq -r '.pnpm.overrides.vite // "no override"' package.json

• Storybook 실행 버전 확인

pnpm storybook --version

– 가능하다면 CI 환경에서도 빌드/실행 드라이런을 통해 경고(warnings)나 에러(errors) 발생 여부를 함께 검증해 주세요.

packages/design-system/src/components/level/Level.stories.tsx (2)

4-14: argTypes 정의와 기본 메타 구성은 깔끔합니다.

level을 number 컨트롤로 제한하고 설명을 제공한 점이 좋습니다. 스토리북 탭에서 컨트롤 가이드가 명확해집니다.


2-2: 스토리 경로 인식 여부 확인 필요.

이 스토리가 로드되려면 .storybook/main.tsstories 글롭이 packages/**를 포함해야 합니다(현재 PR 기준 미포함). 상단 코멘트의 글롭 확장 패치를 함께 적용하세요.

stories/Page.tsx (1)

10-21: 구성은 깔끔합니다

상태 관리와 Header prop 연동이 단순·명확합니다. 스토리북 데모용으로 충분히 적절합니다.

stories/Header.stories.ts (2)

16-20: storybook/test 8.x 사용은 허용 범위

장기 학습 메모에 따르면 '@storybook/test'(또는 'storybook/test') 8.x와 '@storybook/react-vite' 9.x를 혼용하는 구성이 일반적입니다. 이 PR에서도 fn() 사용은 적절합니다.


1-1: Story 타입 import 경로 수정: '@storybook/react-vite' → '@storybook/react'

스토리 타입(Meta, StoryObj)은 프레임워크 패키지인 '@storybook/react'에서 제공됩니다. '@storybook/react-vite'는 프레임워크-빌더 지정용으로, 타입을 보장하지 않습니다. 현재 설정은 타입/IDE 지원 저하 또는 빌드 오류를 유발할 수 있습니다.

-import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { Meta, StoryObj } from '@storybook/react';
⛔ 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/Button.stories.ts (1)

8-24: 스토리 메타 정의 적절

컨트롤/액션/레이아웃 설정이 기본 예제로 적합하며, Autodocs 태그도 올바르게 선언되어 있습니다.

Comment on lines +13 to +16
"stories": [
"../stories/**/*.mdx",
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"
],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

스토리 탐색 글롭이 패키지 내 컴포넌트 스토리를 누락합니다 (Level.stories.tsx가 로드되지 않음).

현재 설정은 ../stories/**만 대상으로 하므로 packages/design-system/src/**/Level.stories.tsx가 스토리북에 나타나지 않습니다. 모노레포 전역에서 스토리를 탐색하도록 글롭을 확장해 주세요.

다음 패치를 제안합니다:

   "stories": [
     "../stories/**/*.mdx",
-    "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"
+    "../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
+    "../packages/**/*.mdx",
+    "../packages/**/*.stories.@(js|jsx|mjs|ts|tsx)"
   ],
📝 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
"stories": [
"../stories/**/*.mdx",
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)"
],
"stories": [
"../stories/**/*.mdx",
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
"../packages/**/*.mdx",
"../packages/**/*.stories.@(js|jsx|mjs|ts|tsx)"
],
🤖 Prompt for AI Agents
In .storybook/main.ts around lines 13 to 16, the stories glob only targets
../stories/** so package-level component stories like
packages/design-system/src/**/Level.stories.tsx are omitted; add an additional
glob that expands story discovery to the monorepo packages (e.g., include a
pattern matching packages/**/src/**/*stories.@(js|jsx|mjs|ts|tsx) or similar) so
Storybook loads stories from packages as well, and ensure the new pattern is
placed alongside the existing entries in the stories array.

@@ -0,0 +1,7 @@
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
import { setProjectAnnotations } from '@storybook/react-vite';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

setProjectAnnotations import 경로가 잘못되었습니다: '@storybook/react'로 교체 필요

portable stories + Vitest 문서 기준 setProjectAnnotations는 프레임워크 패키지(예: '@storybook/react')가 내보내며, '@storybook/react-vite'는 빌더 프레임워크 이름입니다. 현 상태에선 타입/런타임 오류 가능성이 높습니다.

다음 패치를 적용해 주세요.

-import { setProjectAnnotations } from '@storybook/react-vite';
+import { setProjectAnnotations } from '@storybook/react';
🤖 Prompt for AI Agents
In .storybook/vitest.setup.ts around line 2, the import of setProjectAnnotations
is coming from the wrong package ('@storybook/react-vite'); change the import to
come from the framework package '@storybook/react' instead. Replace the import
source string, update any related type imports if needed, and run
TypeScript/vitest to verify there are no remaining type or runtime import
errors.

</a>
</div>
<h1>Vite + React</h1>
<h1>Vite + React</h1>\
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

TSX 구문 오류: 닫는 태그 뒤 백슬래시 제거 필요

닫는 태그 뒤에 붙은 \ 는 TSX에서 유효하지 않아 빌드/타입체크가 실패할 확률이 높습니다. 단순 오타로 보이니 제거해 주세요.

수정안:

-      <h1>Vite + React</h1>\
+      <h1>Vite + React</h1>
📝 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
<h1>Vite + React</h1>\
<h1>Vite + React</h1>
🤖 Prompt for AI Agents
In apps/client/src/App.tsx around line 19, there's an extraneous backslash
immediately after the closing <h1> tag which is invalid TSX and will break
compilation; remove the trailing "\" so the line reads a normal closing tag
without any escape characters, and ensure there are no other stray backslashes
or malformed JSX on that line.

Comment on lines 14 to +31
"devDependencies": {
"@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",
"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"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

MDX 블록 컴포넌트를 사용한다면 '@storybook/blocks' 의존성을 추가하세요.

stories/Configure.mdx에서 문서 블록을 사용 중이며, 최신 스토리북에서는 @storybook/addon-docs/blocks 대신 @storybook/blocks 사용이 권장됩니다. 모노레포 혼합 메이저 버전 사용은 팀 규칙(학습 사항)상 허용되므로, @storybook/blocks@^8.x 추가를 권장합니다.

   "devDependencies": {
     "@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",
     "prettier": "^3.6.2",
     "prettier-plugin-tailwindcss": "^0.6.14",
+    "@storybook/blocks": "^8.6.14",
     "storybook": "^9.1.3",
     "turbo": "^2.5.6",
     "typescript": "5.9.2",
     "vite": "7.1.2",
     "vitest": "^3.2.4",
     "@vitest/browser": "^3.2.4",
     "playwright": "^1.55.0",
     "@vitest/coverage-v8": "^3.2.4"
   },

참고: 장기 학습에 따라 @storybook/blocks(8.x)와 @storybook/react-vite(9.x)를 혼합해 사용하는 패턴이 유효합니다.

📝 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
"devDependencies": {
"@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",
"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"
"devDependencies": {
"@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",
"prettier": "^3.6.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"@storybook/blocks": "^8.6.14",
"storybook": "^9.1.3",
"turbo": "^2.5.6",
"typescript": "5.9.2",
"vite": "7.1.2",
"vitest": "^3.2.4",
"@vitest/browser": "^3.2.4",
"playwright": "^1.55.0",
"@vitest/coverage-v8": "^3.2.4"
},
🤖 Prompt for AI Agents
In package.json around lines 14 to 31, the project uses MDX document blocks
(stories/Configure.mdx) but lacks the @storybook/blocks dependency; add
"@storybook/blocks": "^8.x" to devDependencies (matching 8.x per guidance), run
your package manager install to update lockfiles, and ensure imports in MDX are
updated from @storybook/addon-docs/blocks to @storybook/blocks if present.

@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Story 타입 import 대상이 잘못되었습니다.

스토리 파일에서는 타입을 @storybook/react에서 가져와야 합니다. 현재 @storybook/react-vite에서 가져오고 있어 TS 타입 확인/자동 문서화에 문제가 생길 수 있습니다.

-import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { Meta, StoryObj } from '@storybook/react';
📝 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
import type { Meta, StoryObj } from '@storybook/react-vite';
import type { Meta, StoryObj } from '@storybook/react';
🤖 Prompt for AI Agents
In packages/design-system/src/components/level/Level.stories.tsx around line 1,
the Story types are imported from the wrong package; replace the type import
from '@storybook/react-vite' with types from '@storybook/react' (i.e. import
Meta and StoryObj from '@storybook/react' instead of '@storybook/react-vite') so
TypeScript type checking and autogenerated docs work correctly; update the
import statement accordingly and run type checks to confirm.

Comment on lines 51 to 54
href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
target="_blank"
>Learn more<RightArrow /></a>
</div>
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

target="_blank" 사용 시 보안 속성(rel="noopener noreferrer")이 누락되었습니다.

탭 납치(reverse tabnabbing) 방지를 위해 외부 링크엔 rel="noopener noreferrer"를 반드시 추가하세요.

예시 패치(일부 링크에 적용, 동일 패턴으로 전체에 적용):

-      ><RightArrow /></a>
+      rel="noopener noreferrer"><RightArrow /></a>

일괄 적용을 위한 스크립트:

Also applies to: 63-66, 75-78, 97-100, 106-109, 116-119, 126-129, 135-138, 144-147, 156-159, 171-174, 181-184, 192-195, 202-205


🏁 Script executed:

#!/bin/bash
# stories/Configure.mdx 에서 target="_blank" 이지만 rel 속성이 없는 <a> 태그에 rel 추가
tmpfile=$(mktemp)
awk '{
  if ($0 ~ /<a[^>]*target="_blank"[^>]*>/ && $0 !~ /rel="/) {
    sub(/target="_blank"/, "target=\"_blank\" rel=\"noopener noreferrer\"")
  }
  print
}' stories/Configure.mdx > "$tmpfile" && mv "$tmpfile" stories/Configure.mdx

# 확인
rg -n '<a[^>]*target="_blank"(?![^>]*rel=)' stories/Configure.mdx || echo "모든 target=\"_blank\" 링크에 rel 추가됨"

Length of output: 670


rel 속성 적용 여부를 확인하기 위해 아래 스크립트를 실행해주세요.
실행 결과로 아무 출력이 없으면 모든 target="_blank" 링크에 rel="noopener noreferrer"가 적용된 것입니다.


🏁 Script executed:

#!/bin/bash
# stories/Configure.mdx에서 target="_blank" 링크 중 rel 속성이 없는 항목 탐지
rg 'target="_blank"' stories/Configure.mdx | grep -v 'rel=' || echo "모든 target=\"_blank\" 링크에 rel=\"noopener noreferrer\"가 적용되었습니다."

Length of output: 415


모든 target="_blank" 링크의 문맥을 확인해보겠습니다. 아래 스크립트를 실행해주시고, 출력 결과를 공유해주세요.


🏁 Script executed:

#!/bin/bash
# stories/Configure.mdx에서 target="_blank"가 사용된 <a> 태그 주변 문맥 확인
rg -n 'target="_blank"' -C2 stories/Configure.mdx

Length of output: 2872


보안 속성 누락: target="_blank" 링크에 rel="noopener noreferrer" 추가 필요

stories/Configure.mdx 파일 내 모든 외부 링크(<a target="_blank">)에 보안 속성 rel="noopener noreferrer"가 빠져 있습니다. reverse tabnabbing 공격을 방지하기 위해 아래 위치에 반드시 추가해주세요.

  • 적용 대상 위치

    • Lines 51–54, 63–66, 75–78, 97–100, 106–109, 116–119, 126–129, 135–138, 144–147, 156–159, 171–174, 181–184, 192–195, 202–205
  • 예시 패치

- <a href="https://storybook.js.org/..." target="_blank">Learn more<RightArrow /></a>
+ <a href="https://storybook.js.org/..." target="_blank" rel="noopener noreferrer">Learn more<RightArrow /></a>

위 변경을 적용하면 reverse tabnabbing 취약점을 효과적으로 방지할 수 있습니다.

📝 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
href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div>
<a
href="https://storybook.js.org/docs/configure/styling-and-css/?renderer=react&ref=configure"
target="_blank"
rel="noopener noreferrer"
>
Learn more<RightArrow />
</a>
</div>
🤖 Prompt for AI Agents
In stories/Configure.mdx around lines 51–54, 63–66, 75–78, 97–100, 106–109,
116–119, 126–129, 135–138, 144–147, 156–159, 171–174, 181–184, 192–195, and
202–205, any anchor elements using target="_blank" are missing the security
attributes; update each <a target="_blank"> to include rel="noopener noreferrer"
to prevent reverse tabnabbing (i.e., add rel="noopener noreferrer" alongside
target="_blank" on every external link at those locations).

Comment on lines 21 to 36
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
fill="#FFF"
/>
<path
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
fill="#555AB9"
/>
<path
d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z"
fill="#91BAF8"
/>
</g>
</svg>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

SVG는 장식용이므로 스크린리더에서 숨기고 포커서블 해제하세요

헤더 로고 SVG가 정보 전달 목적이 아니라면 접근성 관점에서 aria-hiddenfocusable="false"를 추가하는 것이 안전합니다.

-        <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
+        <svg
+          width="32"
+          height="32"
+          viewBox="0 0 32 32"
+          xmlns="http://www.w3.org/2000/svg"
+          aria-hidden="true"
+          focusable="false"
+        >
🤖 Prompt for AI Agents
In stories/Header.tsx around lines 21 to 36 the logo SVG is decorative and
should be hidden from assistive tech and removed from the tab order; add
aria-hidden="true" and focusable="false" attributes to the <svg> element (JSX
attributes) so screen readers ignore it and it cannot receive keyboard focus,
keeping visual behavior unchanged.

@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from '@storybook/react-vite';

import { expect, userEvent, within } from 'storybook/test';
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

잘못된 import 경로: 'storybook/test' → '@storybook/test'

현재 경로는 패키지를 찾지 못해 빌드/테스트가 실패합니다. Storybook 8/9에서는 @storybook/test 가 맞습니다.

-import { expect, userEvent, within } from 'storybook/test';
+import { expect, userEvent, within } from '@storybook/test';

다음 스크립트로 저장소 전역의 오타를 점검해 주세요.


🏁 Script executed:

#!/bin/bash
# 'storybook/test' 오타 검색
rg -nP "from\\s+['\"]storybook/test['\"]" -g '!**/node_modules/**'

Length of output: 287


스토리북 테스트 임포트 경로 수정 필요
다음 3개 파일에서 잘못된 경로(storybook/test)가 확인되었습니다. 모두 @storybook/test로 변경해 주세요.

• stories/Header.stories.ts (line 3)
• stories/Button.stories.ts (line 3)
• stories/Page.stories.ts (line 3)

-import { fn } from 'storybook/test';
+import { fn } from '@storybook/test';

(위와 동일하게 expect, userEvent, within 등 모든 storybook/test import를 @storybook/test로 변경합니다.)

🤖 Prompt for AI Agents
In stories/Page.stories.ts around line 3, the test utility import uses the wrong
module path: change the import source from 'storybook/test' to '@storybook/test'
and ensure all imported symbols (expect, userEvent, within, etc.) are imported
from '@storybook/test' instead; apply the same replacement for any other
occurrences in this file.

Comment on lines +26 to +31
browser: {
enabled: true,
headless: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Playwright 런타임/브라우저 설치 확인 필요

provider가 'playwright'이므로 devDependencies에 playwright-core 또는 @playwright/test가 설치되어야 하며, 브라우저 바이너리(chromium)도 사전 설치가 필요합니다. 패키지 누락/브라우저 미설치 시 CI에서 바로 실패합니다.

다음 스크립트로 의존성 여부를 점검해 주세요(리포지토리 루트에서 실행).


🏁 Script executed:

#!/bin/bash
# devDependencies에 playwright 관련 패키지 확인
rg -n '"devDependencies"\s*:\s*{[^}]*}' -N package.json | sed -n '1p'
rg -nP '"(@?playwright/(test|.*)|playwright(-core)?)"\s*:\s*"[^\"]+"' package.json -n -C1

# 스토리북/비텟 플러그인 존재 확인
rg -n "@storybook/addon-vitest|vitest-plugin" package.json -n -C1

# (참고) 로컬에서는 아래로 브라우저 설치
echo "Install browsers: npx playwright install chromium"

Length of output: 712


Playwright 런타임 및 브라우저 바이너리 설치 확인 필요

  • package.json(devDependencies)에 이미 playwright 패키지가 등록되어 있어 런타임은 확보된 상태입니다.
  • CI 환경에서 Chromium 바이너리가 설치되지 않으면 브라우저 테스트가 바로 실패하니, CI 워크플로나 설치 스크립트에 아래 명령을 반드시 포함해 주세요.
    npx playwright install chromium
    
  • 또한 스토리북용 Vitest 통합 플러그인(@storybook/addon-vitest)도 이미 설치되어 있습니다.
🤖 Prompt for AI Agents
In vitest.config.ts around lines 26 to 31 the Playwright browser is enabled for
headless Chromium but CI may not have the Chromium binary installed; update
CI/workflow or project install scripts to run the Playwright browser installer
(e.g. run "npx playwright install chromium") before tests execute and verify
playwright is present in devDependencies (and that any Storybook Vitest plugin
is installed) so tests won't fail due to missing browser binaries.

@@ -0,0 +1 @@
/// <reference types="@vitest/browser/providers/playwright" /> No newline at end of file
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

타입 참조 경로 유효성 및 의존성 확인

삼중 슬래시 타입 참조는 간단하지만, 해당 타입 패키지들이 실제 의존성에 존재해야 합니다. 루트 혹은 워크스페이스 패키지에 @vitest/browserplaywright(또는 @playwright/test)가 devDependency로 포함되어 있는지 확인해 주세요. 또한 tsconfig.jsoninclude가 이 파일을 포함하는지도 점검이 필요합니다.

다음 스크립트로 모든 패키지에서 의존성 존재 여부를 점검할 수 있습니다:


🏁 Script executed:

#!/bin/bash
# 모든 package.json에서 관련 의존성 확인
set -euo pipefail
fd -H -E "node_modules" "package.json" | while read -r pkg; do
  echo "==> $pkg"
  jq -r '
    {
      deps: (.dependencies // {}) + (.devDependencies // {}),
      name: .name
    } as $p
    | $p.name,
      (["  vitest: ",   ( ($p.deps.vitest // "MISSING")       | tostring )] | join("")),
      (["  @vitest/browser: ", ( ($p.deps["@vitest/browser"] // "MISSING") | tostring )] | join("")),
      (["  playwright or @playwright/test: ", ( ($p.deps.playwright // ($p.deps["@playwright/test"] // "MISSING")) | tostring )] | join(""))
  ' "$pkg"
done

echo
echo "tsconfig include에 vitest.shims.d.ts가 포함되는지 확인:"
fd -H -E "node_modules" "tsconfig*.json" | while read -r ts; do
  echo "==> $ts"
  jq '.include // []' "$ts"
done

Length of output: 845


jq 문법 오류로 인해 의존성 확인 스크립트가 실패했습니다. 아래 스크립트로 다시 실행하여 @vitest/browserplaywright(또는 @playwright/test)가 devDependency에 포함되어 있는지, 그리고 tsconfig.jsonincludevitest.shims.d.ts가 포함되는지 확인해 주세요.


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# 모든 package.json에서 관련 의존성 확인
fd -H -E "node_modules" "package.json" | while read -r pkg; do
  echo "==> $pkg"
  vitest_ver=$(jq -r '.dependencies.vitest // .devDependencies.vitest // "MISSING"' "$pkg")
  browser_ver=$(jq -r '.dependencies["@vitest/browser"] // .devDependencies["@vitest/browser"] // "MISSING"' "$pkg")
  play_ver=$(jq -r '.dependencies.playwright // .dependencies["@playwright/test"] // .devDependencies.playwright // .devDependencies["@playwright/test"] // "MISSING"' "$pkg")
  echo "  vitest: $vitest_ver"
  echo "  @vitest/browser: $browser_ver"
  echo "  playwright or @playwright/test: $play_ver"
done

echo
# tsconfig include에 vitest.shims.d.ts가 포함되는지 확인
fd -H -E "node_modules" "tsconfig*.json" | while read -r ts; do
  echo "==> $ts"
  jq -r '.include // []' "$ts"
done

Length of output: 7088


devDependencies 및 tsconfig 설정 보강 필요

다음 두 가지 항목을 점검하고 부족한 부분을 보강해 주세요:

• devDependencies 확인
– 루트(package.json)와 packages/design-system에는 이미
• vitest: ^3.2.4
• @vitest/browser: ^3.2.4
• playwright (또는 @playwright/test): ^1.55.0
이 포함되어 있습니다.
– 하지만 아래 패키지들에서는 모두 MISSING으로 나타났습니다.
• apps/client
• apps/extension
• apps/landing
• packages/eslint-config
• packages/tailwind-config
• packages/typescript-config
– 각 패키지에서 브라우저용 Vitest(type provider)와 Playwright 타입을 사용하려면, 해당 패키지의 devDependencies에 위 세 가지를 추가하거나, 패키지 매니저(예: pnpm-workspace) 설정을 통해 루트 의존성을 올바르게 호이스트하는지 확인해야 합니다.

• tsconfig.json의 include 설정
vitest.shims.d.ts 파일을 실제로 타입 검사에 포함시키려면, 각 tsconfig.json 또는 공통 상위 tsconfig.base.json의 include 배열에 아래처럼 추가해야 합니다.
diff { "include": [ "src", "vite.config.ts", + "vitest.shims.d.ts" ] }
– 현재 apps/client, apps/extension, apps/landing, packages/design-system 등 모든 하위 tsconfig.json에서 해당 파일이 빠져 있으므로, 테스트 환경에서 타입이 인식되지 않을 수 있습니다.

위 두 항목을 반영한 뒤, 테스트가 정상 실행되는지 확인해 주세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In vitest.shims.d.ts around lines 1 to 1, the repo is missing browser Vitest and
Playwright types in several package devDependencies and the shims file is not
included in tsconfig includes; update each affected package (apps/client,
apps/extension, apps/landing, packages/eslint-config, packages/tailwind-config,
packages/typescript-config) to either add devDependencies for vitest,
@vitest/browser and playwright (or @playwright/test) or ensure your
workspace/pnpm hoisting exposes the root versions to those packages, and add the
path to vitest.shims.d.ts into the include array of each package tsconfig.json
(or the shared tsconfig.base.json) so the type provider is picked up; after
making these changes, run the test suite to confirm types and tests load
correctly.

Copy link
Member

@constantly-dev constantly-dev left a comment

Choose a reason for hiding this comment

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

수고하셨습니다 👍

Copy link
Member

Choose a reason for hiding this comment

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

parameterslayout: 'centered'추가해서 중앙 정렬해서 통일하면 좋을 것 같아요 👍

Comment on lines 32 to 36
export const Level99: Story = {
args: {
level: 99,
},
};
Copy link
Member

Choose a reason for hiding this comment

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

단순 궁금증인데 저희 서비스에서도 99정도 나올 경우가 있나요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

아녓..사실 원래 99999로 숫자 길게 됐을 때 레이아웃 확인할려고 썼는데 생각해보니, 그정도 자릿수의 숫자는 어차피 안 쓸 것 같네용.!

Copy link
Member

Choose a reason for hiding this comment

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

아하! 테스트 용이라면 좋습니다! 그리고 서비스 확장성을 고려하신 거라면 충분한 이유가 된다고 생각합니다.
다만 ts자동완성이나 안정성 측면에서 levelunion/정적으로 입력할 수도 있겠다 싶어서 질문드렸어요!! 지금도 굿굿~

@jllee000 jllee000 merged commit 361c9a4 into develop Aug 25, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발하라 개발 달려라 달려

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] Level 컴포넌트 제작

2 participants