diff --git a/.claude/agents/feature-implementer.md b/.claude/agents/feature-implementer.md new file mode 100644 index 0000000..88f6985 --- /dev/null +++ b/.claude/agents/feature-implementer.md @@ -0,0 +1,114 @@ +--- +name: feature-implementer +description: 스펙 문서를 읽고 컴포넌트·테스트·Storybook 스토리를 순서대로 구현한다. /feature 커맨드의 Phase 2에서 호출된다. 스펙 파일 경로를 인자로 받는다. +tools: Read, Write, Edit, Bash, Grep, Glob +--- + +당신은 자기소개서 도우미 웹앱의 기능 구현 전문가입니다. + +## 역할 + +`docs/specs/{feature}-spec.md`를 읽고 스펙의 "구현 순서"에 따라 파일을 생성합니다. + +## 작업 전 필수 확인 + +1. 스펙 파일을 처음부터 끝까지 완전히 읽는다. +2. `src/` 구조를 파악하여 기존 컴포넌트·유틸을 최대한 재사용한다. +3. `AGENTS.md`의 Next.js 버전 주의사항을 확인한다. + +## 구현 규칙 + +### 컴포넌트 코드 + +```typescript +// 파일 구조 예시 +'use client'; // 인터랙션(useState, useEffect, 이벤트 핸들러)이 있을 때만 + +interface ExampleProps { + // 스펙의 Props 인터페이스를 그대로 사용 +} + +export default function Example({ ... }: ExampleProps) { + return ( + // Tailwind CSS만 사용. 인라인 스타일 금지. + ); +} +``` + +- 파일당 컴포넌트 하나 +- Props 인터페이스는 파일 상단에 정의 +- 'use client'는 필요한 경우에만 (서버 컴포넌트가 기본) +- Tailwind CSS만 사용, 인라인 스타일 금지 +- 함수형 컴포넌트만 사용 + +### 테스트 코드 (Vitest + @testing-library/react) + +테스트 파일이 없어도 코드를 작성해 둔다. 테스트 러너(Vitest)는 추후 설치 예정. + +```typescript +// {ComponentName}.test.tsx +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, expect, vi } from 'vitest'; +import {ComponentName} from './{ComponentName}'; + +describe('{ComponentName}', () => { + it('{스펙의 테스트명}', () => { + // Arrange + const props = { ... }; + + // Act + render(<{ComponentName} {...props} />); + + // Assert + expect(screen.getByRole(...)).toBeInTheDocument(); + }); +}); +``` + +- 스펙의 모든 테스트 케이스를 구현 +- AAA 패턴 (Arrange - Act - Assert) 준수 +- Mock API는 `vi.mock`으로 처리 +- `data-testid` 대신 접근성 역할(role), 레이블로 쿼리 + +### Storybook 스토리 (CSF3 형식) + +스토리 파일이 없어도 코드를 작성해 둔다. Storybook은 추후 설치 예정. + +```typescript +// {ComponentName}.stories.tsx +import type { Meta, StoryObj } from '@storybook/react'; +import {ComponentName} from './{ComponentName}'; + +const meta: Meta = { + title: '{Domain}/{ComponentName}', + component: {ComponentName}, + parameters: { layout: 'centered' }, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + // 스펙의 Default 스토리 args + }, +}; +``` + +## 구현 순서 + +스펙의 "구현 순서" 섹션을 그대로 따릅니다. 각 파일을 생성할 때: + +1. 파일 생성 +2. 다음 파일로 이동 + +중간에 멈추지 않고 모든 파일을 완성합니다. + +## 출력 + +완료 후 다음을 반환합니다: + +- 생성된 파일 목록 (경로) +- 각 파일의 라인 수 +- 미구현 항목이 있으면 이유와 함께 명시 diff --git a/.claude/agents/feature-planner.md b/.claude/agents/feature-planner.md new file mode 100644 index 0000000..bb7d71c --- /dev/null +++ b/.claude/agents/feature-planner.md @@ -0,0 +1,106 @@ +--- +name: feature-planner +description: 새 기능 구현 전 스펙 문서를 생성한다. 컴포넌트 계층, Props 인터페이스, 테스트 케이스, Storybook 스토리 목록을 docs/specs/ 에 저장한다. /feature 커맨드의 Phase 1에서 호출된다. +tools: Read, Write, Grep, Glob +--- + +당신은 자기소개서 도우미 웹앱(Next.js 16 + TypeScript + Tailwind CSS)의 기능 스펙 문서 작성 전문가입니다. + +## 역할 + +입력받은 기능 설명을 분석하여 `docs/specs/{feature-kebab-case}-spec.md`를 생성합니다. +구현 전에 무엇을 만들지 명확히 정의하는 것이 목적입니다. + +## 작업 전 필수 확인 + +1. `src/` 디렉토리 구조를 파악하여 기존 컴포넌트·유틸과 중복이 없는지 확인한다. +2. `AGENTS.md`와 `CLAUDE.md`를 읽어 프로젝트 규칙을 파악한다. +3. `docs/specs/`에 유사한 스펙이 이미 있는지 확인한다. + +## 스펙 문서 형식 + +생성하는 파일은 반드시 아래 구조를 따릅니다: + +```markdown +# {기능명} 스펙 + +## 개요 + +{기능의 목적과 사용자 시나리오를 2-3문장으로 설명} + +## 컴포넌트 계층 + +\`\`\` +src/components/{domain}/ +├── {ParentComponent}.tsx ← 컨테이너 +│ ├── {ChildA}.tsx +│ └── {ChildB}.tsx +└── index.ts ← barrel export +\`\`\` + +## 컴포넌트 상세 + +### {ComponentName} + +- **파일**: `src/components/{domain}/{ComponentName}.tsx` +- **'use client'**: 필요 / 불필요 +- **Props**: + \`\`\`typescript + interface {ComponentName}Props { + // 필드 목록 + } + \`\`\` +- **로컬 State**: {없음 / useState로 관리할 항목} +- **역할**: {한 줄 설명} + +## Mock 데이터 타입 + +백엔드 연동 전 사용할 타입 및 예시 데이터: + +\`\`\`typescript +// src/types/{domain}.ts 에 추가 +export interface {TypeName} { +// 필드 +} + +// Mock 예시 +export const mock{TypeName}: {TypeName} = { ... }; +\`\`\` + +## 테스트 케이스 (Vitest + React Testing Library) + +| # | 테스트명 | Given | When | Then | +| --- | -------- | ----------- | ------------- | ----------- | +| 1 | {설명} | {초기 상태} | {사용자 행동} | {예상 결과} | + +## Storybook 스토리 (CSF3) + +| 스토리명 | args 핵심값 | 설명 | +| ----------- | ----------- | --------- | +| Default | {기본값} | 기본 상태 | +| {다른 상태} | {값} | {설명} | + +## 구현 순서 + +1. `src/types/{domain}.ts` — 타입 정의 추가 +2. `src/components/{domain}/{ComponentName}.tsx` — 컴포넌트 구현 +3. `src/components/{domain}/{ComponentName}.test.tsx` — 테스트 작성 +4. `src/components/{domain}/{ComponentName}.stories.tsx` — 스토리 작성 +5. `src/components/{domain}/index.ts` — barrel export 추가 + +## 완료 기준 + +- [ ] 모든 Props가 TypeScript로 정의됨 +- [ ] 테스트 케이스가 사용자 시나리오를 커버함 +- [ ] Storybook에서 모든 상태를 확인 가능함 +- [ ] 백엔드 없이 Mock으로 동작함 +``` + +## 출력 + +스펙 파일 저장 후 다음을 반환합니다: + +- 저장 경로 +- 컴포넌트 목록 (파일 경로만) +- 테스트 케이스 수 +- 예상 구현 시간 diff --git a/.claude/agents/feature-verifier.md b/.claude/agents/feature-verifier.md new file mode 100644 index 0000000..48c92e5 --- /dev/null +++ b/.claude/agents/feature-verifier.md @@ -0,0 +1,104 @@ +--- +name: feature-verifier +description: 구현된 기능을 TypeScript 타입 검사·ESLint·테스트(설치된 경우) 순으로 검증하고 결과 리포트를 반환한다. /feature 커맨드의 Phase 3에서 호출된다. +tools: Read, Bash, Grep, Glob +--- + +당신은 코드 품질 검증 전문가입니다. + +## 역할 + +구현된 파일 목록을 받아 자동 검증과 체크리스트 점검을 수행합니다. +발견된 문제는 심각도와 함께 명확하게 보고합니다. + +## 검증 단계 + +### 1. TypeScript 타입 검사 + +```bash +pnpm type-check +``` + +실패하면 에러 목록을 수집합니다. 계속 진행합니다. + +### 2. ESLint 검사 + +```bash +pnpm lint +``` + +실패하면 에러 목록을 수집합니다. 계속 진행합니다. + +### 3. 테스트 실행 (Vitest 설치된 경우만) + +`package.json`에 `"test"` 스크립트가 있으면 실행합니다: + +```bash +pnpm test --run +``` + +없으면 "SKIPPED (Vitest 미설치)"로 기록하고 넘어갑니다. + +### 4. 코드 리뷰 체크리스트 (파일 직접 읽기) + +구현된 각 파일을 읽고 아래 항목을 점검합니다: + +**컴포넌트** + +- [ ] 'use client'가 필요한 경우에만 사용되었는가 +- [ ] Props 인터페이스가 명확히 정의되어 있는가 +- [ ] 하드코딩된 문자열·숫자가 없는가 +- [ ] 함수 하나가 50줄 이하인가 +- [ ] Tailwind 클래스만 사용되었는가 (인라인 스타일 없음) + +**테스트** + +- [ ] 스펙의 모든 테스트 케이스가 구현되었는가 +- [ ] 각 테스트가 AAA 패턴을 따르는가 +- [ ] `data-testid` 대신 role/label로 쿼리하는가 + +**Storybook** + +- [ ] 모든 스토리가 CSF3 형식인가 +- [ ] `meta.tags: ['autodocs']`가 있는가 +- [ ] 각 스토리에 `args`가 정의되어 있는가 + +## 심각도 기준 + +| 심각도 | 의미 | 처리 | +| -------- | ------------------------ | --------------------- | +| CRITICAL | 타입 에러, 빌드 실패 | 반드시 수정 후 재검증 | +| HIGH | 테스트 실패, ESLint 에러 | 수정 권장 | +| MEDIUM | 체크리스트 미통과 | 사용자에게 알림 | +| LOW | 스타일, 마이너 제안 | 참고 사항 | + +## 리포트 형식 + +``` +## 검증 결과: {기능명} + +### TypeScript: ✅ PASS / ❌ FAIL +{에러 목록 — FAIL인 경우} + +### ESLint: ✅ PASS / ❌ FAIL +{에러 목록 — FAIL인 경우} + +### 테스트: ✅ {n}개 통과 / ❌ FAIL / ⏭️ SKIPPED +{실패 테스트 목록 — FAIL인 경우} + +### 코드 리뷰: {통과 수}/{전체 수} +{미통과 항목 목록} + +--- +### 종합 판정 + +✅ 통과 — 구현 완료 +또는 +⚠️ 수정 필요 — CRITICAL/HIGH 이슈 {n}건 + {이슈 목록} +``` + +## 출력 + +리포트 전문을 반환합니다. +종합 판정이 "수정 필요"이면 수정 대상 파일과 구체적인 수정 방법을 함께 반환합니다. diff --git a/.claude/commands/feature.md b/.claude/commands/feature.md new file mode 100644 index 0000000..364f13a --- /dev/null +++ b/.claude/commands/feature.md @@ -0,0 +1,97 @@ +# /feature — 기능 구현 오케스트레이터 + +Plan → Implement → Verify 3단계로 새 기능을 구현합니다. + +## 사용법 + +``` +/feature [기능명 또는 설명] +``` + +**예시:** + +``` +/feature 경험 입력 폼 +/feature 채용공고 분석 결과 카드 +/feature 자기소개서 문항 편집기 +/feature +``` + +인자 없이 실행하면 현재 대화 컨텍스트에서 기능을 추론합니다. + +--- + +## Phase 1 — Plan + +`feature-planner` 에이전트를 실행하여 스펙 문서를 생성합니다. + +``` +입력: $ARGUMENTS (기능 설명) +출력: docs/specs/{feature}-spec.md +``` + +스펙 문서가 생성되면: + +1. 생성된 스펙 내용을 사용자에게 보여줍니다. +2. **사용자 승인을 기다립니다.** + - 수정 요청 → 스펙 파일을 수정하고 다시 보여줍니다. + - 승인 → Phase 2로 진행합니다. + - 취소 → 중단합니다. + +--- + +## Phase 2 — Implement + +`feature-implementer` 에이전트를 실행합니다. + +``` +입력: 승인된 스펙 파일 경로 +출력: 컴포넌트 + 테스트 + Storybook 스토리 파일들 +``` + +구현 완료 후 생성된 파일 목록을 사용자에게 보여줍니다. + +--- + +## Phase 3 — Verify + +`feature-verifier` 에이전트를 실행합니다. + +``` +입력: 구현된 파일 목록 +출력: 검증 리포트 +``` + +검증 결과에 따라: + +- **CRITICAL/HIGH 이슈 있음** → `feature-implementer`로 수정 후 재검증 (최대 2회) +- **MEDIUM/LOW만 있음** → 사용자에게 알리고 완료 처리 +- **모두 통과** → 완료 + +--- + +## 완료 출력 + +``` +✅ /feature 완료: {기능명} + +📄 스펙: docs/specs/{feature}-spec.md +📁 구현: + - src/components/{domain}/{Component}.tsx + - src/components/{domain}/{Component}.test.tsx + - src/components/{domain}/{Component}.stories.tsx + +🔍 검증: TypeScript ✅ | ESLint ✅ | 테스트 ⏭️ (Vitest 미설치) + +다음 단계: + /commit 으로 커밋하거나 + /issue 로 이슈를 연결하세요. +``` + +--- + +## 주의사항 + +- 테스트 파일은 Vitest 설치 전에도 코드를 작성해 둡니다. (`pnpm add -D vitest @testing-library/react` 후 실행 가능) +- Storybook 파일은 Storybook 설치 전에도 작성해 둡니다. (`pnpm dlx storybook@latest init` 후 확인 가능) +- 백엔드 없이 Mock 데이터로 동작하도록 구현합니다. API 연동은 추후 별도 작업으로 진행합니다. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..ba54b96 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,11 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Write|Edit", + "command": "pnpm exec prettier --write \"$FILE_PATH\" --ignore-unknown", + "description": "파일 저장 후 자동 Prettier 포맷" + } + ] + } +} diff --git a/AGENTS.md b/AGENTS.md index c153a9b..6a7f3dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,3 +5,145 @@ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + +--- + +# 자기소개서 도우미 — 프로젝트 컨텍스트 + +## 앱 개요 + +취업 준비생이 경험을 구조화하고, 채용공고를 분석하여, 자기소개서를 AI와 함께 작성하는 웹앱. + +**핵심 플로우:** + +1. **경험 등록** — 프로젝트·인턴·활동 등을 STAR 형식으로 입력 +2. **채용공고 분석** — 공고 텍스트 입력 → 직무 키워드·역량 추출 +3. **경험 매칭** — 문항별로 적합한 경험을 AI가 추천 +4. **자소서 편집** — AI 초안 생성 → 사용자 편집 → 저장 + +## 기술 스택 + +| 항목 | 버전 | 비고 | +| ------------ | ------ | -------------------------------- | +| Next.js | 16.2.6 | App Router (⚠️ 위 주의사항 필독) | +| React | 19.2.4 | | +| TypeScript | 5.x | strict 모드 | +| Tailwind CSS | 4.x | | +| pnpm | 10.x | 패키지 매니저 | +| Prettier | 3.x | prettier-plugin-tailwindcss 포함 | +| Husky | 9.x | pre-commit: lint-staged | + +## 디렉토리 구조 (목표) + +``` +src/ +├── app/ ← Next.js App Router 페이지 +│ ├── experiences/ +│ │ ├── page.tsx ← 경험 목록 +│ │ ├── new/page.tsx ← 경험 입력 폼 +│ │ └── [id]/edit/page.tsx ← 경험 수정 +│ ├── jobs/ +│ │ └── new/page.tsx ← 채용공고 입력 +│ ├── cover-letters/ +│ │ ├── page.tsx ← 저장된 자소서 목록 +│ │ ├── new/page.tsx ← 자소서 작성 시작 +│ │ └── [id]/page.tsx ← 자소서 편집기 +│ ├── layout.tsx +│ └── page.tsx ← 랜딩/대시보드 +├── components/ +│ ├── experience/ ← 경험 관련 컴포넌트 +│ ├── job/ ← 채용공고 관련 컴포넌트 +│ ├── cover-letter/ ← 자소서 관련 컴포넌트 +│ └── ui/ ← 공통 UI (Button, Card 등) +├── types/ +│ ├── experience.ts +│ ├── job.ts +│ └── cover-letter.ts +├── lib/ +│ ├── mock/ ← Mock 데이터 (백엔드 연동 전) +│ └── utils.ts +└── hooks/ ← 공통 커스텀 훅 +``` + +## 핵심 데이터 타입 + +```typescript +// src/types/experience.ts +export type ExperienceType = + | 'PROJECT' + | 'INTERNSHIP' + | 'ACTIVITY' + | 'AWARD' + | 'OTHER'; + +export interface Experience { + id: string; + experienceType: ExperienceType; + title: string; + role: string; + problem: string; // S: Situation + action: string; // A: Action + result: string; // R: Result + skills: string[]; + competencies: string[]; + startDate?: string; // 'YYYY-MM' + endDate?: string; +} + +// src/types/job.ts +export interface JobPosting { + id: string; + company: string; + jobTitle: string; + requiredSkills: string[]; + preferredSkills: string[]; + competencies: string[]; + rawText?: string; +} + +// src/types/cover-letter.ts +export interface CoverLetter { + id: string; + jobPostingId: string; + company: string; + jobTitle: string; + questions: CoverLetterQuestion[]; + createdAt: string; // ISO 8601 + updatedAt: string; +} + +export interface CoverLetterQuestion { + id: string; + question: string; + answer: string; + recommendedExperienceIds: string[]; + maxLength?: number; +} +``` + +## 백엔드 연동 방침 + +- **현재**: `src/lib/mock/` 폴더에 Mock 데이터·Mock 함수로 동작 +- **추후**: `src/lib/api/`에 API 클라이언트 추가 후 Mock을 실제 호출로 교체 +- 컴포넌트는 Mock/실제 API를 구분하지 않는다 (인터페이스 동일하게 유지) + +## 개발 워크플로우 + +| 커맨드 | 역할 | +| ---------------------- | -------------------------------- | +| `/feature [기능명]` | Plan → Implement → Verify 자동화 | +| `/issue [타입] [제목]` | GitHub 이슈 생성 + 브랜치 생성 | +| `/commit` | 변경사항 분석 → 기능별 커밋 분리 | +| `/pr` | Pull Request 메시지 자동 생성 | + +## 스크립트 + +```bash +pnpm dev # 개발 서버 +pnpm build # 프로덕션 빌드 +pnpm type-check # TypeScript 타입 검사 +pnpm lint # ESLint +pnpm lint:fix # ESLint 자동 수정 +pnpm format # Prettier 전체 포맷 +pnpm format:check # Prettier 검사 (CI용) +``` diff --git a/CLAUDE.md b/CLAUDE.md index 43c994c..59a7151 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1,2 @@ @AGENTS.md +@CONVENTIONS.md diff --git a/CONVENTIONS.md b/CONVENTIONS.md new file mode 100644 index 0000000..6184cec --- /dev/null +++ b/CONVENTIONS.md @@ -0,0 +1,231 @@ +# 프로젝트 컨벤션 + +이 파일은 코드를 작성할 때 반드시 따라야 하는 프로젝트 전용 규칙입니다. +전역 규칙(`~/.claude/rules/`)과 충돌하면 이 파일이 우선합니다. + +--- + +## 1. 컴포넌트 규칙 + +### 파일 구조 + +``` +src/components/{domain}/ +├── {ComponentName}.tsx ← 컴포넌트 (기본 export) +├── {ComponentName}.test.tsx ← 테스트 (같은 폴더) +├── {ComponentName}.stories.tsx ← Storybook 스토리 (같은 폴더) +└── index.ts ← barrel export +``` + +### 컴포넌트 파일 템플릿 + +```typescript +// 1. 'use client'는 인터랙션이 있을 때만 (useState, useEffect, 이벤트 핸들러) +'use client'; + +// 2. Props 인터페이스는 파일 상단에 +interface ExperienceCardProps { + experience: Experience; + onEdit?: (id: string) => void; +} + +// 3. 단일 named export, 화살표 함수 +export const ExperienceCard = ({ experience, onEdit }: ExperienceCardProps) => { + return ( + // 4. Tailwind CSS만 사용. 인라인 스타일 금지. +
+ ... +
+ ); +} +``` + +### 'use client' 판단 기준 + +| 사용 | 미사용 | +| ----------------------------------- | -------------------- | +| `useState`, `useEffect` | 정적 UI | +| 이벤트 핸들러 (`onClick` 등) | 데이터만 표시 | +| `useRouter`, `useSearchParams` | 서버에서 데이터 패칭 | +| 브라우저 API (`window`, `document`) | | + +서버 컴포넌트가 기본. 필요한 최소 범위에만 적용. + +--- + +## 2. 네이밍 규칙 + +| 대상 | 형식 | 예시 | +| --------------- | ------------------------- | -------------------------- | +| 컴포넌트 파일 | `PascalCase.tsx` | `ExperienceCard.tsx` | +| 페이지 파일 | Next.js 규칙 (`page.tsx`) | `app/experiences/page.tsx` | +| 훅 파일 | `camelCase.ts` | `useExperiences.ts` | +| 타입 파일 | `camelCase.ts` | `experience.ts` | +| Mock 파일 | `{도메인}.mock.ts` | `experience.mock.ts` | +| 유틸 파일 | `camelCase.ts` | `formatDate.ts` | +| 컴포넌트 함수 | `PascalCase` | `ExperienceCard` | +| 훅 함수 | `use` + PascalCase | `useExperiences` | +| 타입/인터페이스 | `PascalCase` | `Experience`, `JobPosting` | +| 상수 | `UPPER_SNAKE_CASE` | `MAX_QUESTION_LENGTH` | +| boolean 변수 | `is/has/can` 접두사 | `isLoading`, `hasError` | + +--- + +## 3. 타입 정의 규칙 + +- 모든 도메인 타입은 `src/types/` 에 정의한다. +- 컴포넌트 내부에서만 쓰이는 Props 타입은 해당 컴포넌트 파일에 둔다. +- `any` 사용 금지. 불가피하면 `unknown` + 타입 가드 사용. + +```typescript +// src/types/experience.ts — 도메인 타입 (API 스키마 기준) +export interface Experience { ... } + +// 컴포넌트 내부 — UI 전용 타입 (export 불필요) +interface ExperienceCardProps { + experience: Experience; + variant?: 'compact' | 'full'; +} +``` + +--- + +## 4. Mock 데이터 패턴 + +백엔드 연동 전 모든 데이터는 Mock으로 처리한다. + +``` +src/lib/mock/ +├── experience.mock.ts +├── job.mock.ts +└── cover-letter.mock.ts +``` + +### Mock 파일 구조 + +```typescript +// src/lib/mock/experience.mock.ts +import type {Experience} from '@/types/experience'; + +export const MOCK_EXPERIENCES: Experience[] = [ + { + id: 'exp-1', + experienceType: 'PROJECT', + title: '졸업 프로젝트', + // ... + }, +]; +``` + +컴포넌트는 Mock인지 실제인지 모른다. 함수 시그니처를 동일하게 유지한다. + +--- + +## 5. Import 경로 규칙 + +`tsconfig.json`의 `@/` alias를 사용한다. 상대경로는 같은 폴더 내에만 허용. + +```typescript +// 올바른 예 +import {Experience} from '@/types/experience'; +import ExperienceCard from '@/components/experience/ExperienceCard'; +import {getExperiences} from '@/lib/mock/experience.mock'; + +// 금지 (상대경로가 폴더를 벗어남) +import {Experience} from '../../types/experience'; +``` + +--- + +## 6. 스타일링 규칙 + +- **Tailwind CSS만 사용**. CSS 파일, CSS Modules, 인라인 스타일 금지. +- 클래스 순서는 prettier-plugin-tailwindcss가 자동 정렬. +- 조건부 클래스는 배열 join 방식 사용 (추후 `clsx` + `tailwind-merge` 도입 예정). + +```typescript +// 조건부 클래스 +const buttonClass = [ + 'rounded-md px-4 py-2 font-medium transition-colors', + isLoading ? 'cursor-not-allowed opacity-50' : 'hover:bg-blue-600', +].join(' '); +``` + +--- + +## 7. 테스트 패턴 (Vitest + React Testing Library) + +> Vitest 미설치. 설치 명령: `pnpm add -D vitest @testing-library/react @testing-library/jest-dom` +> 파일은 미리 작성해 두고, 설치 후 실행한다. + +```typescript +// ExperienceCard.test.tsx +import { render, screen } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import ExperienceCard from './ExperienceCard'; +import { MOCK_EXPERIENCES } from '@/lib/mock/experience.mock'; + +describe('ExperienceCard', () => { + it('경험 제목을 표시한다', () => { + // Arrange + const experience = MOCK_EXPERIENCES[0]; + + // Act + render(); + + // Assert + expect(screen.getByText(experience.title)).toBeInTheDocument(); + }); +}); +``` + +- `data-testid` 대신 접근성 역할(role), 텍스트, 레이블로 쿼리한다. +- 각 테스트는 독립적으로 실행 가능해야 한다. +- AAA 패턴(Arrange - Act - Assert)을 준수한다. + +--- + +## 8. Storybook 패턴 (CSF3) + +> Storybook 미설치. 설치 명령: `pnpm dlx storybook@latest init` +> 파일은 미리 작성해 두고, 설치 후 확인한다. + +```typescript +// ExperienceCard.stories.tsx +import type {Meta, StoryObj} from '@storybook/react'; +import ExperienceCard from './ExperienceCard'; +import {MOCK_EXPERIENCES} from '@/lib/mock/experience.mock'; + +const meta: Meta = { + title: 'Experience/ExperienceCard', + component: ExperienceCard, + parameters: {layout: 'centered'}, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {experience: MOCK_EXPERIENCES[0]}, +}; +``` + +- 스토리 파일은 컴포넌트와 같은 폴더에 둔다. +- 모든 Props 상태를 스토리로 커버한다 (기본, 로딩, 에러, 빈 상태). + +--- + +## 9. Barrel Export 규칙 + +각 도메인 폴더에 `index.ts`를 두어 단일 진입점으로 사용한다. + +```typescript +// src/components/experience/index.ts +export {default as ExperienceCard} from './ExperienceCard'; +export {default as ExperienceForm} from './ExperienceForm'; +export {default as ExperienceList} from './ExperienceList'; + +// 사용 측 +import {ExperienceCard, ExperienceList} from '@/components/experience'; +``` diff --git a/docs/specs/README.md b/docs/specs/README.md new file mode 100644 index 0000000..e6d932b --- /dev/null +++ b/docs/specs/README.md @@ -0,0 +1,46 @@ +# Feature Specs + +`/feature` 커맨드가 생성하는 기능 스펙 문서들이 이 디렉토리에 저장됩니다. + +## 워크플로우 + +``` +/feature 경험 입력 폼 + │ + ▼ +Phase 1: feature-planner 에이전트 + → experience-form-spec.md 생성 + → 사용자 승인 대기 + │ + ▼ (승인) +Phase 2: feature-implementer 에이전트 + → src/components/experience/ExperienceForm.tsx + → src/components/experience/ExperienceForm.test.tsx + → src/components/experience/ExperienceForm.stories.tsx + │ + ▼ +Phase 3: feature-verifier 에이전트 + → TypeScript / ESLint / 테스트 검증 + → 검증 리포트 반환 +``` + +## 스펙 파일 네이밍 + +``` +{feature-kebab-case}-spec.md + +예시: + experience-form-spec.md + job-posting-card-spec.md + cover-letter-editor-spec.md + saved-documents-list-spec.md +``` + +## 추후 설치 예정 도구 + +| 도구 | 설치 명령 | 용도 | +| --------- | --------------------------------------------------------------------- | --------------- | +| Vitest | `pnpm add -D vitest @testing-library/react @testing-library/jest-dom` | 테스트 실행 | +| Storybook | `pnpm dlx storybook@latest init` | 컴포넌트 문서화 | + +테스트·스토리 파일은 도구 설치 전에도 미리 작성해 둡니다.