
TDD: 적록 리팩토링을 사용하여 AI가 작은 조치를 취하도록 강제
Matt Pocock은 스스로를 "에이전트 출력 품질을 향상시키는 가장 안정적인 방법"이라고 부릅니다. 그의 TDD 스킬이 수평적 적록 리팩토링이 아닌 수직적 리팩토링을 고집하는 이유, 테스트와 구현 간의 결합을 피하는 방법, AI 시대 TDD의 새로운 의미에 대한 심층 분석
The rate of feedback is your speed limit. Don't outrun your headlights.
실패 모드: "AI가 옳은 일을 하지만 실행할 수 없습니다."
Matt의 강연에 나오는 세 번째 실패 모드: 방향은 옳지만 작동하지 않습니다.
가장 직접적인 해결책은 AI용 피드백 인프라를 설치하는 것입니다.
- TypeScript(정적 타이핑이 없으면 이상해)
- LLM이 브라우저에 액세스하여 페이지 자체를 볼 수 있도록 허용
- 자동화된 테스트
그러나 Matt는 한 가지 사실을 발견했습니다. 이 피드백을 설치해도 LLM이 제대로 작동하지 않습니다. 한 번에 500줄을 쓰다가 "아, 그거 입력해야지"라고 생각하는 경향이 있습니다. 이것이 실용주의 프로그래머가 헤드라이트를 앞지르기라고 부르는 것입니다. 헤드라이트가 밝힐 수 있는 것보다 더 빠르게 운전하십시오. 그리고 벽에 부딪히는 것은 시간 문제일 뿐입니다.
"The rate of feedback is your speed limit, which means you should be testing as you go, taking small deliberate steps. And the AI by default is really not very good at that."
이 문제를 해결하려면 도구 수준에서 AI를 단계별로 강제로 중지해야 합니다. Matt의 대답은 TDD입니다. 먼저 테스트하면 체크포인트가 강제 실행될 수 있습니다.
고전 이론: 켄트 벡(Kent Beck)의 적록 재구성
TDD의 표준 리듬은 Kent Beck이 2003년 저서 "Test-Driven Development: By 예제"에서 정의했습니다.
- 빨간색: 실패한 테스트 작성(무엇을 해야 할지 설명)
- 녹색: 테스트를 통과할 만큼 충분히 작은 코드를 작성합니다.
- REFACTOR: 테스트 보호 중인 코드 구조 개선
각 루프는 몇 분 정도로 매우 짧습니다. 모든 단계에는 자동화된 검사(테스트 통과/실패)가 있습니다.
Matt는 이 리듬을 직접 따르지만 그의 SKILL.md는 반패턴에 대해 이야기하는 데 많은 시간을 소비합니다. 이것이 핵심입니다.
주요 안티 패턴: 빨간색과 녹색을 가로로 슬라이스
많은 사람들은 TDD가 "모든 테스트를 먼저 작성한 다음 모든 구현을 작성"하는 것을 의미한다고 생각합니다. Matt는 SKILL.md에서 이것이 잘못되었다고 직접 말합니다.
WRONG (horizontal slicing):
RED: test1, test2, test3, test4, test5
GREEN: impl1, impl2, impl3, impl4, impl5
RIGHT (vertical slicing via tracer bullets):
RED→GREEN: test1→impl1
RED→GREEN: test2→impl2
RED→GREEN: test3→impl3
...왜 수평이 틀렸나요? SKILL.md는 세 가지 이유를 제시했습니다.
- Tests written in bulk test imagined behavior, not actual behavior
- You end up testing the shape of things (data structures, function signatures) rather than user-facing behavior
- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine
인간의 말: 모든 테스트를 한 번에 작성하는 것은 실제 코드가 아니라 머릿속에 있는 것을 테스트하는 것입니다. impl3을 작성하다 보면 test1의 디자인이 잘못되었음을 깨닫게 됩니다. 하지만 이때 test2/test3/test4는 모두 잘못된 디자인과 결합되어 있습니다. 돌아가서 변경하세요.
올바른 접근 방식은 하나의 구현을 테스트한 후 한 쌍을 작성한 후 다음 쌍을 여는 것입니다. 각 쌍이 완료되고 이 구현에서 무언가를 배운 후에는 상상이 아닌 실제 경험을 기반으로 다음 테스트 쌍을 설계할 수 있습니다.
스킬 전문 구조
engineering/tdd/SKILL.md은 TDD 자체에 많은 뉘앙스가 있기 때문에 Matt가 작성한 가장 긴 기술 중 하나입니다. 핵심 구조는 다음과 같습니다.
철학
Core principle: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
Good tests are integration-style: they exercise real code paths through public APIs. They describe what the system does, not how it does it. A good test reads like a specification.
Bad tests are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly). The warning sign: your test breaks when you refactor, but behavior hasn't changed.
진단을 기억하세요. 내부 함수의 이름을 바꾸면 테스트가 중단됩니다. 그러면 이 테스트는 동작이 아닌 구현을 테스트하는 것이므로 나쁜 테스트입니다.
워크플로(체크리스트 포함)
1. Planning
코드를 작성하기 전에 사용자와 일치시키십시오.
[ ] Confirm with user what interface changes are needed
[ ] Confirm with user which behaviors to test (prioritize)
[ ] Identify opportunities for deep modules (small interface, deep impl)
[ ] Design interfaces for testability
[ ] List the behaviors to test (not implementation steps)
[ ] Get user approval on the plan핵심 질문: "공개 인터페이스는 어떤 모습이어야 합니까? 어떤 동작을 테스트해야 합니까?"
"You can't test everything. Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case."
이것은 매우 반직관적입니다. 기본적으로 AI는 모든 극단적인 경우를 소진하기를 원하지만 Matt는 우선순위를 강조합니다. 모든 행동을 측정할 가치가 있는 것은 아니며 핵심 경로에 화력을 집중합니다.
2. Tracer Bullet
한가지 사항을 확인하는 **테스트를 작성하세요.
RED: Write test for first behavior → test fails
GREEN: Write minimal code to pass → test passes이것은 "추적 총알"입니다. 먼저 쏘고 시력을 확인하십시오. Matt는 이 작업이 엔드 투 엔드여야 한다고 강조했습니다. 스키마를 먼저 작성한 다음 API를 작성한 다음 UI를 작성하는 것이 아니라 전체 스택을 통과하는 가장 얇은 경로를 자르는 것입니다.
3. Incremental Loop
이후의 각 동작에 대해 RED→GREEN을 반복합니다.
RED: Write next test → fails
GREEN: Minimal code to pass → passes규칙:
- 한 번에 하나의 테스트
- 현재 테스트를 통과할 수 있을 만큼만 코드를 작성하세요.
- 향후 테스트를 예측하지 마세요
- 테스트는 관찰 가능한 동작에 중점을 둡니다.
"예측하지 마십시오"가 특히 중요합니다. AI는 어쩔 수 없이 "이 함수는 X를 지원해야 하는데, 그런데 추가하자"라고 생각하고, 이것이 수평 슬라이싱을 시작합니다.
4. Refactor
모든 테스트를 통과한 후 리팩토링 기회를 찾으십시오.
[ ] Extract duplication
[ ] Deepen modules (move complexity behind simple interfaces)
[ ] Apply SOLID principles where natural
[ ] Consider what new code reveals about existing code
[ ] Run tests after each refactor stepNever refactor while RED. Get to GREEN first.
빨간색으로 리팩토링 = 테스트와 코드를 동시에 변경 = 테스트가 잘못된 것인지 코드가 잘못된 것인지 알 수 없습니다. 먼저 녹색을 그린 다음 리팩터링합니다.
Per-Cycle Checklist
각각의 빨간색과 녹색 주기가 끝나면 Matt는 AI에게 자체 점검을 요청합니다.
[ ] Test describes behavior, not implementation
[ ] Test uses public interface only
[ ] Test would survive internal refactor
[ ] Code is minimal for this test
[ ] No speculative features added이 5가지 포인트는 잘못된 테스트와 과도한 구현을 식별하는 데 사용됩니다. AI 자가 점검은 가장 흔히 발생하는 실수를 방지할 수 있습니다.
실사용 : 발행부터 홍보까지
/tdd은 /to-issues의 Matt 워크플로의 다음 단계입니다. 수직 슬라이스 문제가 있는 경우 프로세스는 다음과 같습니다.
你: 实现 issue #43
↓
/tdd
↓
Claude 读 issue acceptance criteria
↓
Claude 探索代码库 → 找到 CONTEXT.md → 用项目术语
↓
Planning 阶段:
- 列出准备改的接口
- 列出准备测的行为(按优先级排序)
- 让你点头
↓
Tracer Bullet:
- RED: 写第一个测试(基于 acceptance criteria 第 1 条)
- 跑测试,确认 fail
- GREEN: 写最小实现
- 跑测试,确认 pass
↓
Incremental Loop:
- 每个 acceptance criteria 一个 RED→GREEN
↓
Refactor:
- 看 deep module 提取机会
- 每次重构后跑全套测试
↓
PR빨간색과 녹색 주기마다 AI가 중지되고 "테스트 실패"/"테스트 통과, 차이점은 다음과 같습니다"라는 상태를 제공합니다. 이러한 일시 중지는 헤드라이트를 앞지르기 위한 해독제입니다 - AI는 한 번에 천 줄을 배치할 가능성이 없습니다.
Mock 정보: Matt의 강력한 의견
SKILL.md는 모의의 위험성을 구체적으로 언급합니다. 그는 또한 별도의 mocking.md을 제공합니다. 핵심 아이디어:
"Bad tests... mock internal collaborators."
모의 내부 협력자 = 테스트와 구현 간의 1:1 결합 = 리팩토링 시 테스트 팀이 무릎을 꿇습니다. Matt가 선호하는 것은 통합 스타일 테스트입니다. 실제 데이터베이스(인 메모리 또는 테스트 컨테이너), 실제 HTTP(MSW) 및 실제 파일 시스템(tmp dir)을 사용해 보십시오. 매우 비용이 많이 들거나 불안정한 경계(예: OpenAI API 호출)에서만 모의합니다.
이는 많은 팀의 현재 상황과 반대됩니다. 대부분의 코드 라이브러리는 단위 테스트로 가득 차 있으며 실제 코드보다 모의 코드가 더 많습니다. Matt는 연설에서 다음과 같이 판단했습니다. 좋은 코드 기반 = 테스트하기 쉬운 코드 기반. 테스트하기 위해 여러 가지를 모의해야 한다면 코드 구조에 문제가 있다는 뜻이므로 먼저 아키텍처를 변경해야 합니다(/improve-codebase-architecture으로 이동).
AI 시대 TDD의 새로운 의미
23년 전 Kent Beck이 이 책을 썼을 때 TDD의 핵심 이점은 "사람들이 잘못된 코드를 작성하지 않는다"는 것이었습니다. AI 시대에 TDD는 추가적인 의미를 갖습니다.
AI가 이해할 수 있는 유일한 "성공 기준"입니다.
Matt는 나중에 그의 연설에서 Karpathy의 지혜로운 말을 인용했습니다.
LLMs are exceptionally good at looping until they meet specific goals. Don't tell it what to do, give it success criteria and watch it go.
"성공 기준"의 가장 좋은 형태는 테스트입니다. 이는 기계로 검증할 수 있고 바이너리이며 논쟁할 수 없습니다. AI에게 테스트 스위트 제공 + "통과"는 AI에게 요구 사항 설명 + "구현하세요"보다 10배 더 안정적입니다.
따라서 /tdd은 단순한 품질 보증 도구가 아니라 에이전트 루프에 대한 입력 인터페이스입니다. 각각의 빨간색과 녹색 주기는 완전한 "입력 → 작업 → 피드백"입니다. AI는 사이클에서 이 구현의 실제 상황을 학습하고 다음 사이클은 더 정확해질 것입니다.
설치 및 사용방법
npx skills@latest add mattpocock/skillstdd + setup-matt-pocock-skills을 확인하세요.
지금 Codex를 주로 사용한다면 설치 제품을 .agents/skills/에 인계받고, 프로젝트 수준 워크플로, 테스트 명령, 이슈 트래커 규칙을 AGENTS.md에 작성하세요. Matt의 /tdd의 핵심은 Claude Code에 묶여 있지 않은 적록 리팩토링 주기입니다.
통화 방법:
- 직접:
/tdd- 현재 대화 컨텍스트에서 무엇을 측정할지 추론하도록 합니다. - 이슈 가져오기:
/tdd implement #43- 이슈를 가져온 다음 엽니다. - 버그 수정:
/tdd reproduce this bug then fix it- 먼저 버그를 재현할 수 있는 실패한 테스트를 작성한 다음 수정합니다.
메모
모든 작업에 적합하지는 않습니다. 일회성 스크립트, 놀이터 탐색 코드, UI 미세 조정 - TDD를 사용하지 마십시오. 속도가 느려집니다. Matt 자신은 TDD가 "지속적인 가치가 있고 유지 관리가 필요한" 코드에 적합하다고 말했습니다.
먼저 테스트 인프라를 준비하세요. 프로젝트가 테스트 프레임워크(Vitest / Jest / Playwright 등)를 설치하지 않은 경우 먼저 설치한 다음 /tdd을 사용하세요. 그렇지 않으면 먼저 설치해 주지만 해당 단계에서 많은 질문이 있습니다.
e2e 테스트를 자동으로 추가하지 마세요. e2e는 느리고 선명하며 TDD 리듬은 분 수준입니다. /tdd은 기본적으로 e2e가 아닌 통합 테스트로 설정되어 있지만 "e2e가 아닌 유닛 + 통합만"이라고 명시적으로 알릴 수 있습니다.
재구성 단계는 통제에서 벗어나기 가장 쉬운 단계입니다. AI가 GREEN 상태를 얻은 후에는 여러 가지를 신나게 리팩토링합니다. 이를 응시하고 각 리팩토링 후에 테스트를 실행합니다. 이 부분은 AI 편차가 발생할 위험이 높은 영역입니다.
참조 리소스
tdd/SKILL.md (源码)
Full TDD philosophy, horizontal-slice anti-pattern, complete workflow checklists, and per-cycle self-check.
Test-Driven Development: By Example
The original book that defined the red-green-refactor loop.
TDD with AI Coding(中文)
Companion piece exploring how TDD shifts meaning when AI writes the implementation.
다음 기사: 코드베이스 아키텍처 개선: 얕은 모듈을 깊은 모듈로 재구성 - 정기적인 유지 관리를 통해 AI가 장기적으로 코드 베이스에서 실행될 수 있습니다.
댓글
PRD 및 문제
Matt Pocock 워크플로의 중간 부분인 /to-prd는 대화를 PRD로 바꾸고, /to-issues는 PRD를 독립적으로 수집할 수 있는 수직으로 분할된 이슈로 자릅니다. AI 시대의 '추적탄'과 '수직 슬라이스'라는 두 가지 낡은 개념의 새로운 역할에 대한 심층적 해석
코드 아키텍처 심화
Matt Pocock 워크플로우의 마지막 단계는 "심화 기회"를 찾기 위해 코드 베이스를 주기적으로 스캔하는 것입니다. John Ousterhout의 심층 모듈 이론, Matt의 원래 삭제 테스트, 그리고 이 기술이 AI 시대에 특히 필요한 이유를 깊이 이해합니다.