Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions frontend/.claude/commands/commit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
description: 세션 작업 기록 + 기능 문서화 + 변경 내용 커밋
allowed-tools: Bash(mkdir *), Bash(ls *), Bash(date *), Bash(git status), Bash(git diff *), Bash(git log *), Bash(git add *), Bash(git commit *), Read, Write, Edit, Glob, Grep
---

# 작업 지시

현재 세션의 작업 내용을 기록하고, 기능별 문서를 자동 생성한 뒤, 변경 파일을 커밋합니다.

---

## Phase 1: 세션 기록

먼저 세션 작업 내용을 dailyNote에 기록합니다.

1. `dailyNote/` 폴더가 없으면 생성
2. 오늘 날짜 파일 확인 (형식: `YYYY-MM-DD.md`)
3. 파일이 없으면 새로 생성, 있으면 기존 내용 뒤에 `---` 구분선 추가 후 이어서 작성

**기록할 내용:**

- 세션에서 논의한 내용
- 고민한 흔적과 의사결정 과정 (Decision Log)
- 트러블슈팅 경험
- 새로 배운 기술이나 발견한 것들

**기록 형식 예시:**

```markdown
## [14:30] 세션 요약

### 논의 내용

- React Query의 stale time 설정에 대해 고민

### Decision Log

- useEffect 의존성 배열에서 함수 참조 문제 해결을 위해 useCallback 적용 결정

### 트러블슈팅

- MSW 핸들러에서 응답 지연 시 스토리북 렌더링 이슈 발견 → delay 시간 조정으로 해결
```

---

## Phase 2: 기능 문서화 (선택적)

세션에서 새로운 기능을 구현하거나 중요한 변경이 있었다면 문서화합니다.

### 기능 매핑 테이블

| 기능 | 문서 경로 | 키워드 |
| ------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------- |
| Main Page | `docs/features/main/` | MainPage, ClubCard, Filter, Banner, CategoryButton, SearchBox, Popup |
| Club Detail | `docs/features/club-detail/` | ClubDetailPage, ClubFeed, ClubProfileCard, ShareButton, ApplyButton, ClubScheduleCalendar |
| Admin - Info | `docs/features/admin/info/` | ClubInfoEditTab, MakeTags, SelectTags |
| Admin - Intro | `docs/features/admin/intro/` | ClubIntroEditTab, AwardEditor, FAQEditor |
| Admin - Photo | `docs/features/admin/photo/` | PhotoEditTab, ImagePreview, ClubLogoEditor, ClubCoverEditor |
| Admin - Recruit | `docs/features/admin/recruit/` | RecruitEditTab, DateTimeRangePicker, MarkdownEditor |
| Admin - Application | `docs/features/admin/application/` | ApplicationEditTab, QuestionBuilder, ApplicationListTab |
| Admin - Applicants | `docs/features/admin/applicants/` | ApplicantsTab, ApplicantDetailPage, ApplicantsListTab |
| Admin - Calendar | `docs/features/admin/calendar/` | CalendarSyncTab, calendarOAuth |
| Admin - Account | `docs/features/admin/account/` | AccountEditTab, LoginTab, PrivateRoute |
| Application Form | `docs/features/application-form/` | ApplicationFormPage, QuestionAnswerer, QuestionContainer |
| Auth | `docs/features/auth/` | auth, secureFetch, refreshAccessToken, JWT |
| API Layer | `docs/features/api/` | apis, apiHelpers, handleResponse, withErrorHandling |
| Hooks | `docs/features/hooks/` | useClub, useApplication, useApplicants, React Query |
| Store | `docs/features/store/` | Zustand, useCategoryStore, useSearchStore |
| Components | `docs/features/components/` | 공용 컴포넌트 |
| Utils | `docs/features/utils/` | debounce, validateSocialLink, formatRelativeDateTime, recruitmentDateParser |
| Experiments | `docs/features/experiments/` | A/B test, useExperiment, Mixpanel |
| Festival | `docs/features/festival/` | FestivalPage, PerformanceCard, TimelineRow, BoothMapSection |
| Introduce | `docs/features/introduce/` | IntroducePage, FeatureSection |

어려우면 사용자에게 물어봅니다. 매핑에 없는 기능이면 새 폴더를 만듭니다.

**문서 형식:**

```markdown
# [제목 — 세션 핵심 주제]

[본문 — 세션에서 논의/작업한 내용을 정리]

## 관련 코드

- `[세션에서 다룬 파일 경로]` — [간단한 설명]
```

**원칙:**

- 억지로 내용을 늘리지 않는다
- 대화체 → 문서체로 변환
- 제목, 본문: 한국어 / 코드, 경로, 기술 용어: 영어

**결과 출력:**

```text
## 문서화 완료
- **경로**: `docs/features/[기능]/[파일명]`
- **내용**: [1줄 요약]
```

---

## Phase 3: Git Commit

기록이 완료되면 커밋을 수행합니다.

1. `git status`로 변경된 파일 확인
2. `git diff HEAD`로 모든 변경사항 확인 (또는 `git diff`와 `git diff --staged`를 각각 실행)
3. `git log --oneline -5`로 최근 커밋 스타일 참고
4. 변경 내용을 분석하여 커밋 메시지 작성
5. 관련 파일만 `git add`로 스테이징
- `docs/features/` 문서 파일 포함
- `dailyNote/`는 gitignore 대상이므로 제외
6. **커밋 전에 변경 내용과 커밋 메시지를 사용자에게 확인 요청**
7. 사용자 승인 후 커밋 실행

**커밋 메시지 형식:**

```text
<type>(<scope>): <subject>

<body>
```

**타입:**

- `feat`: 새로운 기능
- `fix`: 버그 수정
- `docs`: 문서 변경
- `style`: 코드 포맷팅
- `refactor`: 리팩토링
- `test`: 테스트 추가/수정
- `chore`: 기타 변경

**스코프 예시:**

- `main`, `club-detail`, `admin`, `application`, `auth`, `api`, `hooks`, `store`, `utils`, `components`

---

## 참고사항

- dailyNote는 `.gitignore`에 포함되어 커밋 대상이 아님
- 문서화는 중요한 변경이 있을 때만 수행 (매 세션 필수 아님)
- 사소한 변경은 Phase 1만 수행하고 커밋하지 않아도 됨
220 changes: 220 additions & 0 deletions frontend/.claude/commands/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
---
description: 테스트 코드 작성 (Jest / RTL / Playwright)
allowed-tools: Bash(npm run test *), Bash(npx jest *), Bash(npx playwright *), Read, Write, Edit, Glob, Grep
---

# 테스트 코드 작성

대상 파일 또는 기능에 대한 테스트 코드를 작성합니다.

---

## Step 1: 테스트 유형 확인

사용자에게 테스트 유형을 확인합니다:

1. **단위 테스트 (Jest)** - 유틸리티 함수, 훅, 순수 로직
2. **컴포넌트 테스트 (RTL)** - React 컴포넌트 렌더링 및 인터랙션
3. **E2E 테스트 (Playwright)** - 사용자 시나리오 기반 통합 테스트

---

## Step 2: 대상 파일 분석

테스트 대상 파일을 읽고 분석합니다:

- 함수/컴포넌트의 입력과 출력
- 엣지 케이스 및 예외 상황
- 의존성 (외부 API, 스토어, 라우터 등)

---

## Step 3: 테스트 작성

### 단위 테스트 (Jest)

**파일 위치**: 대상 파일과 동일 경로에 `*.test.ts` 생성

**패턴**:

```typescript
import { targetFunction } from './targetFile';

describe('targetFunction', () => {
beforeEach(() => {
// 테스트 환경 설정
});

afterEach(() => {
// 정리
});

it('정상 케이스를 처리한다', () => {
const result = targetFunction(input);
expect(result).toBe(expected);
});

it('엣지 케이스를 처리한다', () => {
// ...
});

it('예외 상황에서 적절히 동작한다', () => {
// ...
});
});
```

**체크리스트**:

- [ ] 정상 동작 케이스
- [ ] 경계값 테스트
- [ ] 예외/에러 케이스
- [ ] 타이머 사용 시 `jest.useFakeTimers()`
- [ ] 비동기 함수는 `async/await` 사용

---

### 컴포넌트 테스트 (RTL)

**파일 위치**: 컴포넌트와 동일 경로에 `*.test.tsx` 생성

**패턴**:

```typescript
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import TargetComponent from './TargetComponent';

const queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
},
});

const renderWithProviders = (ui: React.ReactElement) => {
return render(
<QueryClientProvider client={queryClient}>
<BrowserRouter>{ui}</BrowserRouter>
</QueryClientProvider>
);
};

describe('TargetComponent', () => {
beforeEach(() => {
queryClient.clear();
});

it('초기 상태가 올바르게 렌더링된다', () => {
renderWithProviders(<TargetComponent />);
expect(screen.getByText('예상 텍스트')).toBeInTheDocument();
});

it('사용자 인터랙션에 올바르게 반응한다', async () => {
renderWithProviders(<TargetComponent />);

fireEvent.click(screen.getByRole('button', { name: '버튼명' }));

await waitFor(() => {
expect(screen.getByText('변경된 텍스트')).toBeInTheDocument();
});
});

it('로딩 상태를 표시한다', () => {
// ...
});

it('에러 상태를 처리한다', () => {
// ...
});
});
```

**체크리스트**:

- [ ] 초기 렌더링 상태
- [ ] 사용자 인터랙션 (클릭, 입력 등)
- [ ] 로딩/에러 상태
- [ ] 조건부 렌더링
- [ ] Props 변경에 따른 업데이트
- [ ] 필요시 MSW로 API 모킹

---

### E2E 테스트 (Playwright)

**파일 위치**: `e2e/` 폴더에 `*.spec.ts` 생성

**패턴**:

```typescript
import { expect, test } from '@playwright/test';

test.describe('기능명', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/target-page');
});

test('사용자 시나리오를 완료한다', async ({ page }) => {
// 1. 페이지 로드 확인
await expect(
page.getByRole('heading', { name: '페이지 제목' }),
).toBeVisible();

// 2. 사용자 액션
await page.getByRole('button', { name: '버튼명' }).click();

// 3. 결과 확인
await expect(page.getByText('성공 메시지')).toBeVisible();
});

test('에러 케이스를 처리한다', async ({ page }) => {
// ...
});
});
```

**체크리스트**:

- [ ] 핵심 사용자 플로우
- [ ] 폼 제출 및 유효성 검사
- [ ] 네비게이션
- [ ] 반응형 (모바일/데스크톱)
- [ ] 에러 처리 및 복구

---

## Step 4: 테스트 실행

```bash
# 단위/컴포넌트 테스트
npx jest path/to/file.test.ts

# 전체 테스트
npm run test

# E2E 테스트
npx playwright test e2e/file.spec.ts

# E2E 테스트 (UI 모드)
npx playwright test --ui
```

---

## 작성 원칙

1. **테스트 설명은 한국어로** - `it('정상적으로 동작한다')`
2. **Given-When-Then 구조** - 준비 → 실행 → 검증
3. **독립적인 테스트** - 테스트 간 의존성 없음
4. **의미 있는 테스트명** - 무엇을 테스트하는지 명확히
5. **과도한 모킹 지양** - 실제 동작에 가깝게

---

## 참고: 프로젝트 테스트 설정

- Jest 설정: `jest.config.js`
- RTL 설정: `@testing-library/react`, `@testing-library/jest-dom`
- MSW 핸들러: `src/mocks/handlers/`
- Playwright 설정: `playwright.config.ts` (없으면 생성 필요)
Loading