React + TypeScript + Vite 기반의 프론트엔드 프로젝트
| 분류 | 기술 |
|---|---|
| Framework | React 19, TypeScript |
| Build Tool | Vite |
| Styling | Tailwind CSS v4, CVA (class-variance-authority) |
| State Management | Zustand (클라이언트), React Query (서버) |
| Routing | React Router v7 |
| UI Components | Radix UI, Lucide Icons |
| HTTP Client | Axios |
| Mocking | MSW (Mock Service Worker) |
| Code Quality | ESLint, Prettier, Husky, lint-staged |
| Commit | Commitlint (Conventional Commits) |
- Node.js 18+
- pnpm
# 의존성 설치
pnpm install
# 개발 서버 실행
pnpm dev
# 프로덕션 빌드
pnpm build
# 빌드 결과물 미리보기
pnpm previewsrc/
├── api/ # API 관련 코드
│ └── api.ts
├── assets/ # 정적 자원
│ └── icons/ # SVG 아이콘
├── components/
│ ├── common/ # 공통 컴포넌트 (비즈니스 로직 포함)
│ │ ├── Dropdown.tsx
│ │ └── Toast.tsx
│ ├── layout/ # 레이아웃 컴포넌트
│ │ └── RootLayout.tsx
│ └── ui/ # 기본 UI 컴포넌트 (Headless)
│ ├── button.tsx
│ ├── input.tsx
│ ├── textarea.tsx
│ └── dropdown-menu.tsx
├── hooks/ # 커스텀 훅
├── lib/ # 유틸리티 라이브러리
│ └── utils.ts # cn() 등 헬퍼 함수
├── mocks/ # MSW 모킹 설정
│ ├── browser.ts
│ └── handlers.ts
├── pages/ # 페이지 컴포넌트
│ ├── HomePage.tsx
│ ├── TestPage.tsx
│ └── NotFoundPage.tsx
├── store/ # Zustand 스토어
├── test/ # UI 테스트용 컴포넌트
├── types/ # TypeScript 타입 정의
├── utils/ # 유틸리티 함수
├── App.tsx # 라우팅 설정
├── main.tsx # 앱 진입점
└── index.css # 글로벌 스타일 & 디자인 토큰
| 명령어 | 설명 |
|---|---|
pnpm dev |
개발 서버 실행 |
pnpm build |
TypeScript 컴파일 + Vite 빌드 |
pnpm preview |
빌드 결과물 미리보기 |
pnpm lint |
ESLint 검사 |
pnpm lint:fix |
ESLint 자동 수정 |
Conventional Commits 규칙을 따릅니다.
<type>: <subject> (#<issue-number>)
[optional body]
| 타입 | 설명 |
|---|---|
feat |
새로운 기능 추가 |
fix |
버그 수정 |
docs |
문서 수정 |
style |
코드 포맷팅 (기능 변경 없음) |
refactor |
코드 리팩토링 |
test |
테스트 코드 추가/수정 |
chore |
빌드, 설정 파일 수정 |
remove |
파일/코드 삭제 |
hotfix |
긴급 버그 수정 |
deprecated |
더 이상 사용하지 않는 기능 표시 |
design |
UI/UX 디자인 변경 |
feat: 로그인 폼 유효성 검사 추가 (#12)
fix: 드롭다운 닫힘 버그 수정 (#15)
docs: README 커밋 컨벤션 추가 (#20)| Hook | 동작 |
|---|---|
pre-commit |
lint-staged 실행 (ESLint + Prettier) |
commit-msg |
커밋 메시지 규칙 검사 |
pre-push |
빌드 검증 |
prefer-const: 재할당 없는 변수는 const 사용no-var: var 사용 금지@typescript-eslint/no-explicit-any: any 타입 경고no-console: console.log 경고react/jsx-boolean-value: boolean prop 축약 권장react/jsx-no-useless-fragment: 불필요한 Fragment 경고
{
"semi": false,
"trailingComma": "es5",
"singleQuote": true,
"tabWidth": 2,
"printWidth": 80,
"endOfLine": "lf"
}CVA(class-variance-authority)를 사용한 variant 기반 컴포넌트입니다.
import Button from '@/components/ui/button'
// Variants: fill, ghost, outline, sidebarTab
// Sizes: sm, md, full, sidebarTab
<Button variant="fill" size="md">버튼</Button>
<Button variant="outline" size="sm" disabled>비활성</Button>
<Button variant="sidebarTab" active>활성 탭</Button>import Input from '@/components/ui/input'
// Status: default, danger, success, disabled
<Input status="default" placeholder="입력하세요" />
<Input status="danger" helperText="필수 입력입니다" />
<Input type="password" /> // 비밀번호 토글 자동 지원import Textarea from '@/components/ui/textarea'
// Variants: default, feedback, answer
// Sizes: sm, md
;<Textarea variant="feedback" size="md" placeholder="내용을 입력하세요" />비즈니스 로직이 포함된 재사용 컴포넌트입니다.
src/index.css에 정의된 CSS 변수를 Tailwind에서 사용합니다.
primary-default: #6201E0 (메인 컬러)primary-100~primary-700: 밝기 변형
grey-1: #FFFFFF (배경)grey-6: #222222 (텍스트)grey-9: #BDBDBD (placeholder)
btn-fill-default: #6201E0btn-fill-hover: #4E01B3btn-fill-active: #3B0186
other-red: #EC0037 (에러)other-green: #5EB669 (성공)other-yellow: #F6A818 (경고)
<div className="bg-grey-1 text-grey-6">
<button className="bg-btn-fill-default hover:bg-btn-fill-hover">버튼</button>
</div>개발 환경에서 자동으로 MSW가 활성화됩니다.
// src/mocks/handlers.ts에 핸들러 추가
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('/api/users', () => {
return HttpResponse.json([{ id: 1, name: 'User' }])
}),
]@/를 사용하여 src/ 디렉토리를 참조할 수 있습니다.
// ✅ 권장
import Button from '@/components/ui/button'
import { cn } from '@/lib/utils'
// ❌ 비권장
import Button from '../../../components/ui/button'