Skip to content

feat: 버튼 컴포넌트 구현 및 디자인 토큰 생성 및 크로마틱 워크플로우 생성 (#49) #51

Merged
wkdtnqls0506 merged 14 commits intomainfrom
feature/PRODUCT-96
Jun 25, 2025
Merged

feat: 버튼 컴포넌트 구현 및 디자인 토큰 생성 및 크로마틱 워크플로우 생성 (#49) #51
wkdtnqls0506 merged 14 commits intomainfrom
feature/PRODUCT-96

Conversation

@wkdtnqls0506
Copy link
Contributor

@wkdtnqls0506 wkdtnqls0506 commented Jun 24, 2025

✅ 이슈 번호

close #49


🪄 작업 내용 (변경 사항)

  • 디자인 토큰 생성 및 글로벌 스타일 생성
    • src/styles/colors.css.ts
    • src/styles/radius.css.ts
    • src/styles/reset.css.ts
  • 버튼 컴포넌트 구현
    • src/components/ui/Button
  • 크로마틱 워크플로우 생성
    • .github/workflows/chromatic.yml

📸 스크린샷

  • Storybook Button 컴포넌트 생성
스크린샷 2025-06-24 오후 3 39 39

💡 설명

1. 디자인 토큰(color, radius) 및 글로벌 스타일 생성

버튼 컴포넌트 구현에 필요한 color, radius 값을 디자인 토큰으로 생성하였습니다.
또한 reset.css.ts를 생성하여 브라우저 기본 스타일을 초기화하였습니다.


2. Button 컴포넌트 생성

src/components/ui/Button 경로에 Button 컴포넌트를 구현하였습니다.

버튼 컴포넌트가 필요한 곳에 다음과 같이 사용하시면 됩니다.

<Button variant='dark' size='large'>
    텍스트
</Button>
  • 속성을 지정해두지 않으면 기본적으로 variant='primary' size='medium' 값이 적용됩니다.

  • vanilla-extract의 recipe를 사용하여 Button.css.ts를 구현하였습니다!

    • variantsize에 따라 자동으로 스타일이 조합되도록 recipe를 사용해 CSS 클래스를 구성했습니다.

스크린샷 2025-06-24 오후 3 55 03
  • variant="dark"size="large" 조합 시, recipe에 의해 생성된 클래스입니다.
  • variantsize에 따라 클래스가 조합되며, 디자인 토큰이 CSS 변수로 적용된 것을 확인할 수 있습니다.

3. Chromatic Workflow 생성

Storybook을 배포하기 위해 Chromatic Workflow를 생성했습니다.

  • Storybook을 PR 생성 시점에 배포합니다.

🗣️ 리뷰어에게 전달 사항

  • 수정해야 하거나 궁금한 점이 있으면 리뷰 남겨주세요 🍀

📍 트러블 슈팅

Storybook에서 vanilla-extract 스타일 적용 오류 해결

Storybook 실행 시 아래와 같은 오류가 발생하였습니다.
스크린샷 2025-06-23 오후 5 06 16

- You may have created styles outside of a '.css.ts' context
- You may have incorrect configuration. See https://vanilla-extract.style/documentation/getting-started

이는 vanilla-extract 스타일이 Storybook 내에서 올바르게 처리되지 않아 CSS 변수가 생성되지 못한 것이 원인이었습니다.
vanilla-extract는 빌드 타임에 CSS를 추출하는 방식인데, Storybook은 기본 설정만으로는 .css.ts 파일을 인식하거나 처리하지 못합니다.

따라서 Storybook 공식 문서 를 참고하여 @vanilla-extract/vite-plugin 을 설치하고, .storybook/main.ts 에 Vite 설정을 추가하여 해결했습니다.

// .storybook/main.ts
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';

const config: StorybookConfig = {
  ...
  viteFinal: async (config) => {
    config.plugins = config.plugins || [];
    config.plugins.push(vanillaExtractPlugin());
    return config;
  },
};
  • vanillaExtractPlugin() 은 Vite가 .css.ts 파일을 읽고 실제 CSS로 추출할 수 있도록 도와줍니다.
  • 결과적으로 Storybook에서도 vanilla-extract 스타일이 정상적으로 적용됩니다!

Summary by CodeRabbit

  • New Features

    • 새로운 Button 컴포넌트가 추가되어 다양한 스타일(variant, size)과 함께 사용할 수 있습니다.
    • Button 컴포넌트의 Storybook 스토리가 추가되어 다양한 상태와 크기를 미리 볼 수 있습니다.
    • 글로벌 색상, 반경(radii), 레이어, CSS reset 등 공통 스타일 시스템이 도입되었습니다.
  • Style

    • CSS-in-JS 기반의 글로벌 스타일 및 CSS reset이 적용되어 일관된 디자인이 제공됩니다.
  • Documentation

    • Storybook에 Button 컴포넌트 예제가 추가되어 사용법을 쉽게 확인할 수 있습니다.
  • Chores

    • Chromatic을 통한 Storybook 배포 및 프리뷰 확인을 위한 CI 워크플로우가 추가되었습니다.
    • 관련 라이브러리 및 개발 의존성이 업데이트되었습니다.
  • Refactor

    • 앱 전체에 CSS reset이 적용되어 UI 일관성이 향상되었습니다.
    • 앱 타이틀이 "Timeat"에서 "Eat-da"로 변경되었습니다.

@wkdtnqls0506 wkdtnqls0506 added the ✨ feature 새로운 기능 추가 label Jun 24, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 24, 2025

"""

Walkthrough

이 변경사항은 Button 컴포넌트와 관련 스타일, 스토리북 스토리, 디자인 토큰(color, radius), 글로벌 CSS 리셋을 새로 도입하고, Storybook Chromatic 배포를 위한 GitHub Actions 워크플로우를 추가합니다. 또한, Storybook과 Vite 설정을 Vanilla Extract와 TypeScript 경로 플러그인으로 확장합니다.

Changes

파일/경로 그룹 변경 요약
src/components/ui/Button/Button.tsx, Button.css.ts, Button.stories.tsx, index.ts Button 컴포넌트, 스타일(recipe), 스토리북 스토리, index 재수출 추가
src/styles/colors.css.ts, radius.css.ts, layers.css.ts, reset.css.ts, index.ts 색상, radius 디자인 토큰, CSS layer, 글로벌 리셋 스타일, 스타일 index 추가
.storybook/main.ts, .storybook/preview.ts Storybook에 Vite 플러그인 및 CSS 리셋 적용
src/app/layout.tsx CSS 리셋 적용 및 타이틀 변경
package.json Vanilla Extract, chromatic 등 패키지 추가
.github/workflows/chromatic.yml Storybook Chromatic 배포용 워크플로우 추가

Sequence Diagram(s)

sequenceDiagram
    participant Developer
    participant GitHub Actions
    participant Chromatic
    participant Storybook

    Developer->>GitHub Actions: PR 생성(main 브랜치 대상)
    GitHub Actions->>Repository: 코드 체크아웃
    GitHub Actions->>GitHub Actions: pnpm, Node.js, 의존성 설치
    GitHub Actions->>Chromatic: Storybook 빌드 및 배포
    Chromatic->>GitHub Actions: 빌드 결과 및 URL 반환
    GitHub Actions->>GitHub PR: Storybook/Chromatic 링크 코멘트 작성
Loading

Assessment against linked issues

Objective (이슈 번호) Addressed Explanation
Button Component 생성: Large, Medium, Small (#49)
디자인 토큰 생성: color, radius (#49)
Storybook Chromatic 워크플로우 생성 (#49)

Assessment against linked issues: Out-of-scope changes

해당 이슈(#49)의 요구사항과 무관한 변경사항은 발견되지 않았습니다.

Poem

🐇
버튼이 생겼어요, 색깔도 반짝!
크기도 세 가지, 토큰은 듬뿍.
리셋 스타일로 깔끔하게,
스토리북에 예쁘게 등장해요.
Chromatic도 신나서 춤을 춰요!

─ 코딩토끼 드림 🥕
"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/components/ui/Button/Button.css.ts

Oops! Something went wrong! :(

ESLint: 9.27.0

ESLint couldn't find the plugin "eslint-plugin-react-hooks".

(The package "eslint-plugin-react-hooks" was not found when loaded as a Node module from the directory "".)

It's likely that the plugin isn't installed correctly. Try reinstalling by running the following:

npm install eslint-plugin-react-hooks@latest --save-dev

The plugin "eslint-plugin-react-hooks" was referenced from the config file in " » eslint-config-next/core-web-vitals » /node_modules/.pnpm/eslint-config-next@15.3.2_eslint@9.27.0_jiti@2.4.2__typescript@5.8.3/node_modules/eslint-config-next/index.js".

If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fdb5480 and cfc7537.

📒 Files selected for processing (1)
  • src/components/ui/Button/Button.css.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/ui/Button/Button.css.ts
✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

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

@github-actions
Copy link

github-actions bot commented Jun 24, 2025

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

🧹 Nitpick comments (8)
src/styles/layers.css.ts (1)

1-3: reset 레이어 네이밍이 모호할 수 있습니다
향후 다른 전역 레이어가 추가될 경우 reset 만으로는 역할이 불분명해질 수 있습니다. globalReset 또는 baseReset 처럼 의도를 좀 더 명확히 드러내는 이름을 고려해 보세요.

package.json (1)

19-26: 런타임 번들 크기 최소화를 위해 @vanilla-extract/recipes 위치 재검토 필요
@vanilla-extract/recipes 는 빌드-타임(compile-time) 전용 의존성입니다. 런타임에 포함될 이유가 없으므로 "devDependencies" 로 이동하면 번들 크기와 설치 시간이 줄어듭니다.

-    "@vanilla-extract/recipes": "^0.5.7",
+// devDependencies 블록으로 이동
src/styles/colors.css.ts (1)

3-7: common 팔레트의 스케일 키 일관성
다른 팔레트는 100, 95 … 식으로 감소하는데 common1000 두 개만 존재합니다. 필요하다면 0 대신 0 or 5 스텝을 유지하거나 black/white 상수로 분리하여 스케일 규칙을 일관성 있게 유지해 주세요.

src/app/layout.tsx (1)

1-2: 전역 reset 경로 확정 필요 (.css.ts vs .css)
reset.css.ts 파일을 side-effect import 하려면 일반적으로 import "@/styles/reset.css.ts"; 로 작성하고, vanilla-extract 플러그인이 빌드시 .css 로 치환합니다.
지금처럼 .css 확장자를 직접 지정하면 IDE 경로 인식이 안 되거나 Jest 모킹 시 경로 resolve 실패가 발생할 수 있습니다.

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

38-58: 고정 너비 값보다 최소 너비 사용을 고려하세요.

버튼 크기에 고정 너비(width)를 사용하면 텍스트 길이에 따라 내용이 잘릴 수 있습니다. minWidth를 사용하면 더 유연한 레이아웃을 제공할 수 있습니다.

       small: {
-        width: "63px",
+        minWidth: "63px",
         height: "32px",
         padding: "9px 20px",
         fontSize: "13px",
         borderRadius: radius[80],
       },
       medium: {
-        width: "80px",
+        minWidth: "80px",
         height: "40px",
         padding: "9px 20px",
         fontSize: "15px",
         borderRadius: radius[100],
       },
       large: {
-        width: "98px",
+        minWidth: "98px",
         height: "48px",
         padding: "12px 28px",
         fontSize: "16px",
         borderRadius: radius[120],
       },

31-36: disabled 변형에 호버 상태 비활성화 추가를 고려하세요.

disabled 변형에서 마우스 호버 때 스타일 변화를 방지하기 위해 명시적으로 호버 상태를 비활성화하는 것이 좋습니다.

       disabled: {
         backgroundColor: colors.coolNeutral[96],
         color: colors.neutral[70],
         cursor: "not-allowed",
+        ":hover": {
+          backgroundColor: colors.coolNeutral[96],
+        },
       },
src/components/ui/Button/Button.tsx (1)

14-27: disabled 변형 사용 시 자동으로 disabled 속성 설정을 고려하세요.

현재 variant="disabled"를 사용해도 버튼이 실제로 비활성화되지 않습니다. 사용자 경험을 위해 disabled 변형일 때 자동으로 disabled 속성을 설정하는 것을 고려해보세요.

 export const Button = ({
   children,
   variant = "primary",
   size = "medium",
   ...props
 }: ButtonProps) => {
   const variantClass = button({ variant, size });

   return (
-    <button className={variantClass} {...props}>
+    <button 
+      className={variantClass} 
+      disabled={variant === "disabled" || props.disabled}
+      {...props}
+    >
       {children}
     </button>
   );
 };
src/styles/reset.css.ts (1)

47-58: 전역 버튼 커서 설정이 disabled 버튼과 충돌할 수 있습니다.

모든 버튼에 cursor: pointer를 설정하면 disabled 버튼의 cursor: not-allowed 스타일과 충돌할 수 있습니다. 좀 더 구체적인 선택자 사용을 고려해보세요.

 globalStyle("button", {
   "@layer": {
     [reset]: {
       padding: 0,
       border: "none",
       background: "none",
       outline: "none",
       boxShadow: "none",
-      cursor: "pointer",
     },
   },
 });

+globalStyle("button:not(:disabled)", {
+  "@layer": {
+    [reset]: {
+      cursor: "pointer",
+    },
+  },
+});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f1e8295 and 04dfacd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • .github/workflows/chromatic.yml (1 hunks)
  • .storybook/main.ts (2 hunks)
  • .storybook/preview.ts (1 hunks)
  • package.json (2 hunks)
  • src/app/layout.tsx (1 hunks)
  • src/components/ui/Button/Button.css.ts (1 hunks)
  • src/components/ui/Button/Button.stories.tsx (1 hunks)
  • src/components/ui/Button/Button.tsx (1 hunks)
  • src/components/ui/Button/index.ts (1 hunks)
  • src/styles/colors.css.ts (1 hunks)
  • src/styles/index.ts (1 hunks)
  • src/styles/layers.css.ts (1 hunks)
  • src/styles/radius.css.ts (1 hunks)
  • src/styles/reset.css.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`src/styles/*.{ts,tsx}`: 전역 스타일 파일은 src/styles 폴더에 배치한다.

src/styles/*.{ts,tsx}: 전역 스타일 파일은 src/styles 폴더에 배치한다.

  • src/styles/index.ts
  • src/styles/layers.css.ts
  • src/styles/colors.css.ts
  • src/styles/reset.css.ts
  • src/styles/radius.css.ts
`**/*.css.ts`: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치한다.

**/*.css.ts: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치한다.

  • src/styles/layers.css.ts
  • src/styles/colors.css.ts
  • src/components/ui/Button/Button.css.ts
  • src/styles/reset.css.ts
  • src/styles/radius.css.ts
🧬 Code Graph Analysis (2)
src/styles/reset.css.ts (1)
src/styles/layers.css.ts (1)
  • reset (3-3)
src/components/ui/Button/Button.tsx (1)
src/components/ui/Button/Button.css.ts (1)
  • button (5-72)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (16)
package.json (2)

46-48: Vite 플러그인 버전 호환성 확인
@vanilla-extract/vite-plugin@5.0.7 과 Vite, Next-js-vite 설정(Storybook 포함)이 모두 5.x 호환인지 확인해 주세요. 최근 Next 15 & Vite 플러그인 조합에서 5.0.8 이상이 요구되는 이슈가 있었습니다.


51-52: Chromatic 라이선스/토큰 설정 누락 여부 점검
chromatic 패키지만 추가되고 환경 변수(CHROMATIC_PROJECT_TOKEN) 설정이나 npm script 호출이 보이지 않습니다. 워크플로 정의 파일과 함께 토큰이 올바르게 주입되는지 확인해 주세요.

src/app/layout.tsx (1)

8-11: 타이틀 변경에 따른 SEO 영향 확인
TimeatEat-da 변경으로 기존 검색 색인에 영향이 있을 수 있습니다. 릴리스 노트에 기록하고 메타 태그(og:title 등)도 함께 업데이트했는지 확인 바랍니다.

src/styles/index.ts (1)

1-2: LGTM! 디자인 토큰 중앙화가 잘 구현되었습니다.

디자인 토큰을 중앙화하는 배럴 파일 패턴이 적절하게 적용되어 있으며, 코딩 가이드라인에 따라 src/styles 폴더에 배치되었습니다. 이는 컴포넌트 전반에서 일관된 스타일 사용을 가능하게 합니다.

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

1-2: LGTM! 컴포넌트 구조가 잘 설계되었습니다.

Button 컴포넌트와 스타일을 명확하게 분리하여 내보내는 배럴 파일 패턴이 적절하게 적용되어 있습니다. 이는 컴포넌트의 public API를 명확하게 정의하고 사용하기 쉽게 만듭니다.

.storybook/preview.ts (1)

1-1: LGTM! CSS 리셋이 적절하게 적용되었습니다.

CSS 리셋을 Storybook에 import하여 모든 스토리에 일관된 기본 스타일을 적용하는 것이 좋은 접근입니다. 이는 컴포넌트들이 예측 가능한 스타일 환경에서 렌더링되도록 보장합니다.

.storybook/main.ts (2)

2-3: LGTM! 필요한 Vite 플러그인들이 올바르게 import되었습니다.

Vanilla Extract와 TypeScript 경로 해석을 위한 플러그인들이 적절하게 import되어 있습니다.


13-20: LGTM! Vite 설정이 올바르게 확장되었습니다.

viteFinal 함수를 통해 Storybook의 Vite 설정을 확장하는 방식이 적절합니다. 기존 플러그인들을 유지하면서 새로운 플러그인들을 추가하는 방식이 안전합니다.

.github/workflows/chromatic.yml (2)

33-34: 설정 확인 요청: 자동 승인 옵션 검토

onlyChanged: trueautoAcceptChanges: true 설정이 의도된 것인지 확인이 필요합니다.

  • onlyChanged: true: 변경된 스토리만 배포 (성능상 좋음)
  • autoAcceptChanges: true: 모든 변경사항을 자동으로 승인 (검토 프로세스 생략)

autoAcceptChanges: true는 시각적 회귀 테스트의 목적을 무효화할 수 있습니다. 이 설정이 의도된 것인지 확인해 주세요.


36-43: LGTM! PR 댓글 기능이 잘 구현되었습니다.

if: always() 조건과 함께 PR에 Storybook 및 Chromatic 링크를 댓글로 추가하는 기능이 잘 구현되어 있습니다. 고유한 태그를 사용하여 PR별로 댓글을 관리하는 것도 좋은 접근입니다.

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

1-72: 전체적으로 잘 구현된 버튼 스타일 레시피입니다.

vanilla-extract recipe를 활용한 구조화된 스타일링이 잘 되어 있고, 디자인 토큰을 적절히 활용하고 있습니다. 다양한 변형과 크기 옵션도 잘 정의되어 있습니다.

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

1-27: 깔끔하고 타입 안전한 Button 컴포넌트 구현입니다.

TypeScript 타입 정의가 잘 되어 있고, props 전달 방식도 적절합니다. vanilla-extract 스타일링과의 연동도 잘 구현되어 있습니다.

src/components/ui/Button/Button.stories.tsx (2)

5-26: 포괄적이고 잘 구성된 Storybook 메타 설정입니다.

모든 주요 props에 대한 컨트롤이 적절히 설정되어 있고, argTypes 정의도 명확합니다. onClick 액션 로깅도 잘 추가되어 있습니다.


61-70: 다양한 크기를 한 번에 비교할 수 있는 유용한 스토리입니다.

Sizes 스토리는 모든 크기 변형을 시각적으로 비교할 수 있어 디자인 검토에 매우 유용합니다. flexWrap 속성으로 반응형 레이아웃도 고려되어 있습니다.

src/styles/reset.css.ts (2)

1-126: 포괄적이고 잘 구성된 CSS 리셋입니다.

CSS 레이어를 활용한 체계적인 구성과 다양한 요소들에 대한 일관된 스타일 초기화가 잘 되어 있습니다. 브라우저 간 일관성을 보장하는 데 도움이 될 것입니다.


103-103: CSS 사용자 정의 속성이 정의되어 있는지 확인하세요.

var(--font-family)를 사용하고 있지만 이 변수가 정의되어 있는지 확인이 필요합니다. 정의되지 않은 경우 브라우저 기본 글꼴이 사용됩니다.

다음 스크립트로 --font-family 변수가 정의된 위치를 확인해보세요:

#!/bin/bash
# Description: --font-family 변수가 정의된 위치를 찾습니다.

# CSS 파일에서 --font-family 정의 검색
rg -A 3 -B 3 "--font-family"

# TypeScript/JavaScript 파일에서 font-family 관련 코드 검색
rg -A 3 -B 3 "font-family"

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

🧹 Nitpick comments (1)
src/components/ui/Button/Button.css.ts (1)

43-72: size variant의 수치 설정에 개선 여지가 있습니다.

각 size별로 width가 고정되어 있어 텍스트 길이에 따른 유연성이 부족할 수 있습니다. 특히 다국어 지원 시 문제가 될 수 있습니다.

다음과 같이 개선을 고려해보세요:

small: {
-  width: "63px",
+  minWidth: "63px",
   height: "32px",
   padding: "9px 20px",
   fontSize: "13px",
   borderRadius: radius[80],
},
medium: {
-  width: "80px", 
+  minWidth: "80px",
   height: "40px",
   padding: "9px 20px",
   fontSize: "15px",
   borderRadius: radius[100],
},
large: {
-  width: "98px",
+  minWidth: "98px",
   height: "48px",
   padding: "12px 28px",
   fontSize: "16px",
   borderRadius: radius[120],
},
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5dde933 and 86bb305.

📒 Files selected for processing (3)
  • src/components/ui/Button/Button.css.ts (1 hunks)
  • src/components/ui/Button/Button.stories.tsx (1 hunks)
  • src/components/ui/Button/Button.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/ui/Button/Button.stories.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.css.ts`: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치한다.

**/*.css.ts: vanilla-extract 스타일 파일은 해당 컴포넌트와 같은 폴더에 배치한다.

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

List of files the instruction was applied to:

  • src/components/ui/Button/Button.css.ts
🧬 Code Graph Analysis (1)
src/components/ui/Button/Button.tsx (1)
src/components/ui/Button/Button.css.ts (1)
  • button (5-78)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (7)
src/components/ui/Button/Button.tsx (3)

1-3: 타입 전용 import 사용으로 번들 크기 최적화가 잘 되었습니다.

ComponentPropsWithoutRef를 타입 전용으로 import하여 런타임에 불필요한 코드가 포함되지 않도록 최적화했습니다.


5-12: 타입 정의가 명확하고 확장성이 뛰어납니다.

ButtonVariant와 ButtonSize 타입을 별도로 정의하고, ComponentPropsWithoutRef를 통해 네이티브 button 속성을 모두 상속받도록 구현했습니다. 타입 안전성과 재사용성이 우수합니다.


14-27: 컴포넌트 구현이 깔끔하고 props 처리가 적절합니다.

기본값 설정과 props spreading을 통해 유연한 컴포넌트를 구현했습니다. vanilla-extract의 recipe 함수를 활용한 동적 클래스 생성도 잘 구현되었습니다.

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

1-3: 디자인 토큰 import가 올바르게 구성되었습니다.

vanilla-extract recipes와 디자인 토큰을 적절히 import하여 일관된 디자인 시스템을 구축할 수 있는 기반이 마련되었습니다.


5-24: base 스타일과 disabled 상태 처리가 우수합니다.

flexbox를 활용한 레이아웃, 부드러운 transition 효과, 접근성을 고려한 disabled 상태 스타일링이 잘 구현되었습니다. cursor: "not-allowed"로 사용자 경험도 향상시켰습니다.


25-42: variant별 색상 조합이 명확하고 hover 상태 처리가 적절합니다.

primary, dark, assistive 각 variant의 색상 조합이 디자인 토큰을 활용하여 일관성 있게 정의되었고, hover 상태의 색상 변화도 자연스럽습니다.


74-78: 기본값 설정이 적절합니다.

primary variant와 medium size를 기본값으로 설정한 것이 일반적인 사용 패턴에 적합합니다.

Seojunhwan
Seojunhwan previously approved these changes Jun 25, 2025
Copy link
Member

@Seojunhwan Seojunhwan left a comment

Choose a reason for hiding this comment

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

대대대,, approve 눌러버렸는데 코멘트들 확인 부탁드립니다요!

수빈님 하나 궁금한 것이 있는데요, 커스텀이 필요할 때 어떻게 하는지 궁금해요! emotion, tailwind는 아래처럼 했었는데 흠흠,,

<Button
	css={customCss} // emotion
	className="p-4" // tailwind (내부에서 merge 필요)
>
  커스텀이 필요해~
</Button>

@wkdtnqls0506
Copy link
Contributor Author

대대대,, approve 눌러버렸는데 코멘트들 확인 부탁드립니다요!

수빈님 하나 궁금한 것이 있는데요, 커스텀이 필요할 때 어떻게 하는지 궁금해요! emotion, tailwind는 아래처럼 했었는데 흠흠,,

<Button
	css={customCss} // emotion
	className="p-4" // tailwind (내부에서 merge 필요)
>
  커스텀이 필요해~
</Button>

Button.tsx 내부에서 className props로 받아서

<button className={`${variantClass} ${className ?? ""}`}>{children}</button>

사용할 땐

<Button variant="primary" className={myCustomStyle}>
  커스텀~~~~~~~~~~~~~
</Button>

이런 식으로 사용해주면 되는거 같아요!!

@Seojunhwan
Copy link
Member

@wkdtnqls0506 오오옹 고렇게 쓰는군요,,! 감사합니다!

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

♻️ Duplicate comments (1)
src/components/ui/Button/Button.tsx (1)

14-20: children을 명시적으로 destructuring할 필요가 없습니다.

ComponentProps<"button">에 이미 children이 포함되어 있으므로, 별도로 children을 destructuring하지 않아도 됩니다. 이는 이전 리뷰에서도 지적된 사항입니다.

다음과 같이 수정할 수 있습니다:

export const Button = ({
-  children,
  variant = "primary",
  size = "medium",
  className,
  ...props
}: ButtonProps) => {
🧹 Nitpick comments (1)
src/components/ui/Button/Button.tsx (1)

21-28: 구현이 깔끔하지만 className 합성에서 작은 개선 여지가 있습니다.

전반적으로 잘 구현되었습니다. 다만 className 합성에서 불필요한 공백을 방지하기 위해 다음과 같이 개선할 수 있습니다:

-    <button className={`${variantClass} ${className ?? ""}`} {...props}>
+    <button className={[variantClass, className].filter(Boolean).join(" ")} {...props}>

또는 더 간단하게:

-    <button className={`${variantClass} ${className ?? ""}`} {...props}>
+    <button className={`${variantClass}${className ? ` ${className}` : ""}`} {...props}>

하지만 현재 구현도 충분히 동작하므로 선택사항입니다.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cce0a57 and fdb5480.

📒 Files selected for processing (1)
  • src/components/ui/Button/Button.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/components/ui/Button/Button.tsx (1)
src/components/ui/Button/Button.css.ts (1)
  • button (5-78)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test
🔇 Additional comments (2)
src/components/ui/Button/Button.tsx (2)

1-3: 깔끔한 import 구조입니다.

React의 ComponentProps를 사용하여 타입 안전성을 확보하고, 스타일링을 위한 button recipe를 적절히 import하고 있습니다.


5-12: 타입 정의가 잘 구성되어 있습니다.

ButtonVariant와 ButtonSize의 리터럴 타입이 CSS recipe와 일치하며, ButtonProps가 적절히 export되어 있어 외부에서 사용할 수 있습니다. ComponentProps<"button">을 확장하여 모든 네이티브 button 속성을 포함한 것도 좋습니다.

Copy link
Member

@Seojunhwan Seojunhwan left a comment

Choose a reason for hiding this comment

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

LGTM~! 고생하셨어요!

@wkdtnqls0506 wkdtnqls0506 merged commit c808dee into main Jun 25, 2025
6 checks passed
@wkdtnqls0506 wkdtnqls0506 deleted the feature/PRODUCT-96 branch June 25, 2025 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feature 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PRODUCT-96] Button Component 생성

2 participants