TDD 워크플로우 제대로 돌리는 법도 알려드릴게요. RED-GREEN-REFACTOR 사이클이에요.
RED는 실패하는 테스트 먼저 쓰는 거예요. 아직 기능 없으니까 당연히 실패하죠. GREEN은 테스트 통과할 만큼만 코드 짜는 거예요. REFACTOR는 그 코드를 깔끔하게 리팩토링하는 거고요.
이 순서 지키면 버그가 확 줄어들어요. 해커톤 때 이렇게 했더니 테스트 커버리지 80% 넘겼거든요.
TL;DR
- TDD = Anthropic 공식 권장 워크플로우 ("Anthropic-favorite workflow")
- RED → GREEN → REFACTOR 사이클 반복
- Claude에게 명시적으로 TDD임을 알려야 함 (mock 방지)
- 테스트 먼저 작성 → 실패 확인 → 최소 구현 → 리팩토링
- /tdd 커스텀 명령어로 자동화 가능
- AI + TDD = 최강 조합 (명확한 목표 + 빠른 피드백)
왜 TDD가 AI와 잘 맞는가?
Claude의 기본 성향: Implementation-First
"When I ask Claude to 'implement feature X,' it writes the implementation first." — alexop.dev
Claude는 기본적으로 바로 코드를 짜려고 해요. 테스트 없이 "Happy Path"만 구현하고, 엣지 케이스를 놓치기 쉬워요.
TDD가 해결하는 문제
문제 TDD 해결책
| 엣지 케이스 누락 | 테스트가 요구사항을 명시 |
| 과도한 구현 | 테스트 통과할 만큼만 구현 |
| 버그 발견 지연 | 즉각적인 피드백 |
| 리팩토링 공포 | 테스트가 안전망 역할 |
AI에게 TDD가 효과적인 이유
"Everything that makes TDD a slog for humans makes it the perfect workflow for an AI agent. AI thrives on clear, measurable goals, and a binary test is one the clearest goals you can give it." — Builder.io
- 명확한 목표: 테스트 통과/실패 = 이진 피드백
- 측정 가능: 성공 기준이 명확
- 자동 검증: Claude가 스스로 결과 확인 가능
- 반복 효율: 보일러플레이트 테스트 코드를 빠르게 생성
Anthropic 공식 TDD 가이드
공식 권장 워크플로우
"This is an Anthropic-favorite workflow for changes that are easily verifiable with unit, integration, or end-to-end tests. Test-driven development (TDD) becomes even more powerful with agentic coding." — Anthropic Best Practices
Anthropic이 권장하는 4단계
- 테스트 작성: 예상 input/output 기반으로 테스트 작성
- 실패 확인: 테스트 실행해서 실패하는지 확인
- 구현: 테스트 통과할 최소한의 코드 작성
- 커밋: 테스트 통과하면 커밋
"Ask Claude to write tests based on expected input/output pairs. Be explicit about the fact that you're doing test-driven development so that it avoids creating mock implementations, even for functionality that doesn't exist yet in the codebase." — Anthropic Best Practices
핵심: Claude에게 TDD를 하고 있다고 명시적으로 알려야 해요. 그래야 아직 없는 기능에 대해 mock을 만들지 않아요.
RED-GREEN-REFACTOR 사이클
┌─────────────────────────────────────────────────────────────┐
│ TDD 사이클 │
│ │
│ 🔴 RED ──────────► 🟢 GREEN ──────────► 🔵 REFACTOR │
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 실패하는 테스트 통과할 코드 정리 │
│ 테스트 작성 최소 코드 작성 (테스트 유지) │
│ │
│ │ │
│ ▼ │
│ 다음 기능으로 │
│ ◄────────────────────────────────────── │
└─────────────────────────────────────────────────────────────┘
🔴 RED Phase: 실패하는 테스트 작성
목적: 아직 구현되지 않은 기능의 명세(Specification) 작성
> 사용자 로그인 기능의 테스트를 먼저 작성해줘.
> 아직 구현은 하지 마.
> 테스트가 실패하는지 확인해줘.
Claude 출력 예시:
// auth.test.ts
describe('User Login', () => {
it('should return user object on valid credentials', async () => {
const result = await login('user@example.com', 'password123');
expect(result.success).toBe(true);
expect(result.user.email).toBe('user@example.com');
});
it('should return error on invalid email', async () => {
const result = await login('invalid-email', 'password123');
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid email format');
});
it('should return error on wrong password', async () => {
const result = await login('user@example.com', 'wrongpassword');
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid credentials');
});
});
# 테스트 실행 → 실패 확인 ✅
npm test
# FAIL: login is not defined
🟢 GREEN Phase: 최소 구현
목적: 테스트를 통과할 만큼만 코드 작성 (과도한 구현 금지!)
> 이제 테스트가 통과하도록 최소한의 구현을 해줘.
> 테스트에 없는 기능은 추가하지 마.
Claude 출력 예시:
// auth.ts
export async function login(email: string, password: string) {
// 이메일 형식 검증
if (!email.includes('@')) {
return { success: false, error: 'Invalid email format' };
}
// 실제로는 DB 조회 (여기선 하드코딩)
const validUser = { email: 'user@example.com', password: 'password123' };
if (email === validUser.email && password === validUser.password) {
return { success: true, user: { email } };
}
return { success: false, error: 'Invalid credentials' };
}
# 테스트 실행 → 통과 ✅
npm test
# PASS: All tests passed
🔵 REFACTOR Phase: 코드 정리
목적: 테스트는 유지하면서 코드 품질 개선
> 테스트는 그대로 유지하면서 코드를 리팩토링해줘.
> 중복 제거, 가독성 개선, 타입 추가 등.
Claude 출력 예시:
// auth.ts (리팩토링 후)
interface LoginResult {
success: boolean;
user?: { email: string };
error?: string;
}
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
export async function login(email: string, password: string): Promise<LoginResult> {
if (!isValidEmail(email)) {
return { success: false, error: 'Invalid email format' };
}
const user = await findUserByEmail(email);
if (!user || !verifyPassword(password, user.passwordHash)) {
return { success: false, error: 'Invalid credentials' };
}
return { success: true, user: { email: user.email } };
}
function isValidEmail(email: string): boolean {
return EMAIL_REGEX.test(email);
}
# 리팩토링 후에도 테스트 통과 확인 ✅
npm test
# PASS: All tests passed
실전 /tdd 명령어 사용법
커스텀 /tdd 명령어 만들기
<!-- .claude/commands/tdd.md -->
---
description: Test-Driven Development workflow
---
# TDD Workflow for: $ARGUMENTS
You are doing **Test-Driven Development**. Follow this strict cycle:
## Phase 1: RED 🔴
1. Analyze the feature requirement: $ARGUMENTS
2. Write failing test(s) that define expected behavior
3. Run tests and **confirm they fail**
4. Do NOT write any implementation code yet
5. Wait for my review before proceeding
## Phase 2: GREEN 🟢
After I approve the tests:
1. Write the **minimum code** to make tests pass
2. Do NOT add features not covered by tests
3. Run tests and confirm they pass
4. Commit when green
## Phase 3: REFACTOR 🔵
After tests pass:
1. Improve code quality (readability, DRY, types)
2. Keep all tests passing
3. Run tests after each change
4. Commit the refactored code
## Rules
- Never skip the RED phase
- Never implement more than what tests require
- Always run tests after changes
- One test at a time for complex features
사용 예시
> /tdd 사용자 로그인 기능
# Claude 응답:
🔴 RED Phase 시작
로그인 기능의 테스트를 먼저 작성하겠습니다.
[테스트 코드 작성...]
테스트 실행 결과: FAIL ❌
- login is not defined
테스트가 예상대로 실패합니다.
테스트 코드를 검토해주시고, 진행해도 되면 알려주세요.
> 좋아, 구현해줘
# Claude 응답:
🟢 GREEN Phase 시작
테스트를 통과할 최소한의 구현을 작성하겠습니다.
[구현 코드 작성...]
테스트 실행 결과: PASS ✅
모든 테스트가 통과합니다.
🔵 REFACTOR Phase로 진행할까요?
고급 TDD 패턴
1. Subagent로 컨텍스트 분리
"When everything runs in one context window, the LLM cannot truly follow TDD. The test writer's detailed analysis bleeds into the implementer's thinking." — alexop.dev
문제: 같은 컨텍스트에서 테스트와 구현을 하면 Claude가 "치팅"할 수 있음
해결: Subagent로 각 phase 분리
<!-- .claude/skills/tdd-integration/skill.md -->
---
name: tdd-integration
description: Enforce TDD with strict Red-Green-Refactor cycle using subagents
---
### Phase 1: RED 🔴
Invoke the `tdd-test-writer` subagent with:
- Feature requirement from user request
- Expected behavior to test
**Do NOT proceed until test failure is confirmed.**
### Phase 2: GREEN 🟢
Invoke the `tdd-implementer` subagent with:
- Test file path from RED phase
- Feature requirement context
**Do NOT proceed until test passes.**
### Phase 3: REFACTOR 🔵
Invoke the `tdd-refactorer` subagent with:
- Implementation files from GREEN phase
- Test file for verification
2. TDD Guard (Hooks로 강제)
TDD Guard는 Hooks를 사용해 TDD 원칙을 강제하는 도구예요:
"TDD Guard ensures Claude Code follows Test-Driven Development principles. When your agent tries to skip tests or over-implement, TDD Guard blocks the action and explains what needs to happen instead." — TDD Guard GitHub
설정:
// .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"command": "tdd-guard"
}
]
}
}
작동 방식:
- 파일 수정 전 TDD 원칙 검증
- 테스트 없이 구현하려 하면 차단
- 과도한 구현 감지 및 경고
3. 테스트 유형별 TDD
Unit Test TDD:
> /tdd calculateTotal 함수 - 가격 합계, 할인 적용, 세금 계산
Integration Test TDD:
> /tdd 결제 API 통합 - Stripe 연동, 에러 핸들링, 웹훅 처리
E2E Test TDD:
> /tdd 사용자 회원가입 플로우 - 폼 입력, 이메일 인증, 프로필 설정
CLAUDE.md TDD 설정
기본 TDD 가이드라인
# Testing and TDD
When working on a new feature or a bug fix, you **always write a failing test first** and then wait for me to review them. Then, when I've committed those tests, you add the smallest implementation possible to resolve a single test failure.
## TDD Workflow
1. 🔴 RED: Write failing test(s) first, then wait for review
2. 🟢 GREEN: After commit, implement minimal code to pass
3. 🔵 REFACTOR: Clean up while keeping tests green
Key principle: **Small, iterative cycles.**
## Commands
- `npm test`: Quick unit tests for TDD cycles
- `npm run test:all`: Full test suite before PR
- `npm run test:coverage`: Check coverage
## Rules
- No production code without a failing test
- One test at a time for complex features
- Never mock functionality that doesn't exist yet
- Run tests after every change
엄격한 TDD 강제
# Strict TDD Mode
You are a **TDD-first agent**. Strict constraints:
1. **Never write implementation without a failing test**
- If asked to "create a feature," respond: "Let me write a test first"
- Confirm test fails before implementing
2. **Minimal code always**
- Write the simplest code that passes current test
- Do NOT anticipate future requirements
- Refactor only after tests pass
3. **Test output first**
- Run tests with -q flag for clean output
- Show PASS/FAIL status clearly
4. **Explicit phase announcements**
- 🔴 "RED Phase: Writing failing test..."
- 🟢 "GREEN Phase: Implementing minimal solution..."
- 🔵 "REFACTOR Phase: Cleaning up..."
실무 TDD 시나리오
시나리오 1: 새 기능 개발
> /tdd 장바구니에 상품 추가 기능
# RED
🔴 테스트 작성:
- 상품 추가 시 장바구니에 포함되는지
- 동일 상품 추가 시 수량 증가하는지
- 재고 초과 시 에러 반환하는지
# GREEN
🟢 최소 구현:
- addToCart 함수
- 수량 관리 로직
- 재고 체크
# REFACTOR
🔵 개선:
- 타입 추가
- 에러 처리 표준화
- 중복 제거
시나리오 2: 버그 수정
> /tdd 결제 시 할인 코드가 두 번 적용되는 버그
# RED
🔴 버그 재현 테스트:
- 할인 코드 두 번 입력 시 한 번만 적용되는지 확인
# GREEN
🟢 수정:
- 중복 할인 방지 로직 추가
# REFACTOR
🔵 개선:
- 할인 로직 추출 및 정리
시나리오 3: 리팩토링
> /tdd 인증 모듈 리팩토링 - 기존 테스트 유지하면서
# 기존 테스트 확인
🟢 모든 테스트 통과 확인
# REFACTOR (반복)
🔵 점진적 리팩토링:
1. 작은 변경
2. 테스트 실행
3. 통과 확인
4. 반복
TDD ROI (투자 대비 효과)
토큰 비용 비교
"The TDD workflow has a higher upfront token tax... But after 10,000 lines of code, the ROI becomes undeniable." — Ali Moradi
Without TDD:
프롬프트: "결제 API 만들어줘"
→ 500줄 코드 생성
→ 버그 발견 (Delete가 cascade 안 됨)
→ 에러 로그 분석 (2,000 토큰)
→ 수정
→ 새 버그 발견
→ 반복...
총 토큰: 50,000+ 토큰
총 시간: 수 시간
품질: 테스트 안 된 취약한 코드
With TDD:
프롬프트: "삭제 시 하위 항목도 삭제되는지 테스트 작성해줘"
→ 테스트 작성 (200 토큰)
→ 실패 확인
→ 최소 구현
→ 통과 확인
→ 다음 요구사항으로
총 토큰: 5,000 토큰
총 시간: 20분
품질: 모든 동작 검증됨
Anthropic 내부 사례
"The Security Engineering team transformed their workflow from 'design doc → janky code → refactor → give up on tests' to asking Claude for pseudocode, guiding it through test-driven development, and checking in periodically." — How Anthropic teams use Claude Code
결과: 더 신뢰할 수 있고 테스트 가능한 코드
주의사항
1. Claude의 자연스러운 성향
"Without explicit guidance, agents naturally skip the refactoring phase of the red-green-refactor cycle or at best make only superficial changes." — TDD Guard Blog
해결: CLAUDE.md에 명시적으로 TDD 규칙 작성
2. Context Pollution
같은 컨텍스트에서 테스트와 구현을 하면 Claude가 테스트를 구현에 맞춰 "치팅"할 수 있어요.
해결:
- Subagent로 phase 분리
- 테스트 작성 후 리뷰 받고 커밋 후 구현
3. 과도한 테스트
모든 것에 TDD를 적용할 필요는 없어요.
TDD 적합:
- 복잡한 비즈니스 로직
- 엣지 케이스가 많은 기능
- 버그 수정
- API 엔드포인트
TDD 불필요:
- 단순 CRUD
- UI 스타일링
- 설정 파일
- 프로토타이핑
체크리스트
TDD 시작 전
- [ ] CLAUDE.md에 TDD 가이드라인 추가
- [ ] /tdd 커스텀 명령어 생성
- [ ] 테스트 프레임워크 설정 (Jest, Vitest, pytest 등)
RED Phase
- [ ] 기능 요구사항을 테스트로 변환
- [ ] 테스트가 실패하는지 확인
- [ ] 구현 코드 작성 전 리뷰
GREEN Phase
- [ ] 테스트 통과할 최소한의 코드만 작성
- [ ] 테스트에 없는 기능 추가 금지
- [ ] 모든 테스트 통과 확인
REFACTOR Phase
- [ ] 코드 품질 개선 (DRY, 가독성, 타입)
- [ ] 리팩토링 후에도 테스트 통과 확인
- [ ] 커밋
고급
- [ ] Subagent로 phase 분리 고려
- [ ] TDD Guard 설치 고려
- [ ] 테스트 커버리지 목표 설정 (70-80%)
마무리
TDD는 Anthropic이 공식 권장하는 워크플로우예요.
"This is an Anthropic-favorite workflow for changes that are easily verifiable with unit, integration, or end-to-end tests." — Anthropic Best Practices
기억하세요:
- RED → GREEN → REFACTOR 사이클 엄수
- Claude에게 TDD임을 명시적으로 알리기
- 테스트 먼저, 구현은 나중에
- 최소 구현 — 테스트 통과할 만큼만
- 리팩토링 시 테스트가 안전망
AI와 TDD는 최강 조합이에요. Claude에게 명확한 목표(테스트)를 주면, 그 목표를 향해 정확하게 달려갑니다. 처음엔 느린 것 같아도, 장기적으로 버그 감소 + 유지보수 용이 + 자신감 있는 리팩토링이라는 엄청난 ROI를 얻을 수 있어요.
참고 자료
Anthropic 공식
- Claude Code Best Practices — TDD 공식 권장
- How Anthropic teams use Claude Code — 내부 TDD 사용 사례
커뮤니티 가이드
- Forcing Claude Code to TDD — Subagent 기반 TDD
- TDD Guard — Hooks로 TDD 강제
- Prevent the Robocalypse with TDD — 실전 TDD 워크플로우
- TDD Paradigm Shift — TDD ROI 분석
도구
- TDD Guard GitHub — TDD 강제 Hooks
- awesome-claude-code — /tdd 명령어 모음
일반 TDD
- Martin Fowler - TDD — TDD 원론
- Kent Beck - Test-Driven Development — TDD 바이블
'Programmer > AI' 카테고리의 다른 글
| Claude Code 생산성 10배 올리기: 키보드부터 자동화까지 (0) | 2026.01.21 |
|---|---|
| Claude Code 보안 Best Practices: 타협 없는 안전한 AI 코딩 (0) | 2026.01.21 |
| Claude Code Extended Thinking: 깊은 사고로 퀄리티 높이기 (1) | 2026.01.21 |
| Claude Code 컨테이너 격리와 병렬 개발: devcontainer & git worktree (0) | 2026.01.21 |
| Claude Code Thinking Mode: ultrathink로 깊게 생각시키기 (0) | 2026.01.21 |