AI가 코드를 생성해주는 시대가 되었습니다. GitHub Copilot, Claude Code, Cursor 같은 도구들이 개발자의 일상에 깊이 자리 잡았습니다. 그런데 한 가지 질문이 남습니다. AI가 코드를 짜준다면, 개발자는 무엇을 해야 할까요?
답은 **명세(Specification)**에 있습니다. AI에게 “무엇을 만들어”라고 말하는 것과 “이 명세에 맞게 구현해”라고 말하는 것은 결과가 전혀 다릅니다. 이 글에서는 Spec-Driven Development(SDD)와 TDD를 결합하여 AI 코딩 에이전트를 효과적으로 다루는 방법을 소개합니다.
SDD란 무엇인가
Spec-Driven Development(SDD)는 잘 정제된 소프트웨어 명세서를 AI 코딩 에이전트의 프롬프트로 사용하여 실행 가능한 코드를 생성하는 개발 패러다임입니다. 명세가 단순한 문서가 아니라 “실행 가능한 아티팩트”로 취급된다는 점이 핵심입니다.
이 개념은 갑자기 등장한 것이 아닙니다. 1986년 Bertrand Meyer가 Eiffel 언어에서 주창한 Design by Contract(DbC), 이후 TDD와 DbC의 시너지가 학문적으로 논의된 흐름, 그리고 Contract-First API 개발까지 이어지는 긴 역사가 있습니다. 2020년대 LLM 기반 에이전트 워크플로우의 등장으로 SDD는 르네상스를 맞이하고 있습니다.
3가지 제어 평면
SDD는 세 가지 제어 평면을 통해 AI의 코드 생성을 통제합니다.
| 제어 평면 | 역할 | 예시 |
|---|---|---|
| 선언적 의도 정의 | 무엇(What)을 할지만 정의 | API 스펙, 타입 정의, 인터페이스 |
| 결정론적 구체화 | 동일 명세에서 일관된 결과물 생성 | 코드 생성 템플릿, 제약 조건 |
| 지속적 제약 검증 | 설계 의도 이탈 방지 | 테스트, 린트 규칙, 타입 체크 |
“무엇을”은 사람이 정의하고, “어떻게”는 AI가 처리합니다. 그리고 “제대로 되었는지”는 자동화된 검증이 보장합니다.
SDD 도입의 3단계
SDD를 당장 전면 도입하기는 어렵습니다. 단계적으로 접근할 수 있습니다.
- Spec-Reference — 기존 문서를 AI에게 참고 자료로 제공합니다. API 문서, README, 아키텍처 다이어그램 등을 컨텍스트에 포함시키는 수준입니다.
- Spec-as-Prompt — 명세 자체를 프롬프트로 구조화합니다. 타입 정의, 인터페이스, 제약 조건을 명시적으로 작성하여 AI에게 전달합니다.
- Spec-as-Source — 명세가 코드의 진실의 원천(source of truth)이 됩니다. 명세가 변경되면 코드가 자동으로 재생성되고, 명세와 코드의 불일치는 CI에서 잡아냅니다.
constitution.md로 프로젝트 원칙을 정의하고, /specify로 명세를 생성하며, /plan으로 구현 계획을 수립하는 워크플로우를 제공합니다. GitHub Copilot, Claude Code, Gemini CLI 등 주요 에이전트를 모두 지원합니다.
SDD + TDD 시너지: Red-Green-Refactor 재해석
TDD의 Red-Green-Refactor 사이클은 AI 시대에 더욱 강력해집니다. SDD의 명세가 TDD의 테스트 케이스로 자연스럽게 변환되기 때문입니다.
flowchart TD
subgraph Red["🔴 Red: 명세 → 테스트"]
A[명세 작성] --> B[테스트 케이스 도출]
B --> C[AI가 테스트 코드 생성]
C --> D[테스트 실패 확인]
end
subgraph Green["🟢 Green: AI 코드 생성"]
E[테스트를 AI에게 전달] --> F[AI가 구현 코드 생성]
F --> G[테스트 통과 확인]
end
subgraph Refactor["🔵 Refactor: 아키텍처 유지"]
H[AI가 리팩토링 제안] --> I[개발자가 구조 검토]
I --> J[테스트 재실행으로 검증]
end
D --> E
G --> H
J --> |다음 명세| A
Red: 명세가 곧 테스트 케이스
SDD에서 명세를 먼저 작성하면, 그 명세에서 테스트 케이스를 도출하는 것은 기계적인 작업이 됩니다. AI는 이 변환을 매우 잘 수행합니다.
// 명세: "사용자 이메일은 반드시 유효한 형식이어야 하며,
// 도메인은 허용 목록에 포함되어야 한다"
// AI가 명세에서 도출한 테스트
describe("validateUserEmail", () => {
it("유효한 이메일 형식을 통과시킨다", () => {
expect(validateUserEmail("user@allowed.com")).toBe(true);
});
it("잘못된 이메일 형식을 거부한다", () => {
expect(validateUserEmail("not-an-email")).toBe(false);
});
it("허용 목록에 없는 도메인을 거부한다", () => {
expect(validateUserEmail("user@blocked.com")).toBe(false);
});
});
Green: AI가 테스트를 통과시킨다
실패하는 테스트가 있으면, AI 에이전트에게 “이 테스트를 통과시켜”라고 지시합니다. AI는 테스트라는 명확한 목표가 있을 때 훨씬 정확한 코드를 생성합니다. 모호한 자연어 지시보다 실패하는 테스트 하나가 더 강력한 프롬프트입니다.
Refactor: 아키텍처 일관성 유지
AI가 생성한 코드는 동작하지만, 프로젝트의 아키텍처 컨벤션을 완벽히 따르지 않을 수 있습니다. 이때 개발자가 구조를 검토하고, AI에게 리팩토링을 지시합니다. 테스트가 있으니 리팩토링 후에도 안전하게 동작을 보장할 수 있습니다.
개발자 마인드셋 전환
AI 도구의 등장으로 개발자의 역할이 근본적으로 변하고 있습니다. 업계 전반의 트렌드를 보면, 코드 직접 작성의 비중은 줄어들고 아키텍처와 거버넌스의 비중이 늘어나고 있습니다.
벽돌공에서 사령관으로
과거의 개발자가 한 줄 한 줄 코드를 쌓아가는 벽돌공이었다면, AI 시대의 개발자는 전체 시스템을 조율하는 사령관에 가깝습니다.
AI가 처리하는 영역:
- 보일러플레이트 코드 생성
- CRUD 작업 구현
- 기본 테스트 케이스 생성
- 문서화
개발자가 집중해야 할 영역:
- 시스템 아키텍처 설계
- 명확한 명세 작성 (프롬프트 엔지니어링)
- AI 출력물 검증과 비판적 평가
- 표준과 정책 수립
추상화 수준의 상승
flowchart LR
subgraph 과거["과거"]
A1[요구사항] --> A2[설계]
A2 --> A3[코드 작성]
A3 --> A4[테스트]
A4 --> A5[배포]
end
subgraph 현재["AI 시대"]
B1[요구사항] --> B2[명세 작성]
B2 --> B3["AI 코드 생성"]
B3 --> B4["자동 검증"]
B4 --> B5[배포]
end
style A3 fill:#ff6b6b,color:#fff
style B2 fill:#51cf66,color:#fff
과거에는 개발자의 핵심 역량이 “코드 작성” 단계에 집중되었습니다. AI 시대에는 그 핵심이 “명세 작성” 단계로 이동합니다. 코드를 잘 짜는 능력보다 문제를 잘 정의하는 능력이 더 중요해지는 것입니다.
검증 기반 신뢰
AI가 생성한 코드를 무조건 신뢰할 수는 없습니다. 하지만 무조건 불신할 필요도 없습니다. 중요한 것은 검증 체계입니다.
- 타입 시스템이 구조적 정합성을 보장하는가?
- 테스트가 비즈니스 로직을 커버하는가?
- CI 파이프라인이 일관성을 지키고 있는가?
이 세 가지가 갖춰져 있다면, AI가 생성했든 사람이 작성했든 코드의 품질은 동일하게 보장됩니다.
실전 적용 가이드
SDD + TDD를 실무에 적용하는 구체적인 워크플로우를 살펴봅니다.
워크플로우 예시
하나의 기능을 구현할 때의 흐름입니다.
1단계: 명세 작성
## 기능: 사용자 프로필 이미지 업로드
### 제약 조건
- 허용 형식: JPEG, PNG, WebP
- 최대 파일 크기: 5MB
- 업로드 후 256x256 리사이즈
- S3에 저장, CDN URL 반환
### 인터페이스
- POST /api/users/:id/avatar
- Request: multipart/form-data (file)
- Response: { url: string, updatedAt: string }
### 에러 케이스
- 415: 지원하지 않는 파일 형식
- 413: 파일 크기 초과
- 404: 존재하지 않는 사용자
2단계: 명세 → 테스트 (AI 활용)
명세를 AI에게 전달하여 테스트 케이스를 생성합니다. 생성된 테스트를 검토하여 누락된 엣지 케이스를 보완합니다.
3단계: 테스트 → 구현 (AI 활용)
실패하는 테스트를 AI에게 전달하여 구현 코드를 생성합니다. 테스트가 모두 통과하는지 확인합니다.
4단계: 리팩토링 (개발자 + AI)
프로젝트 컨벤션에 맞게 코드 구조를 정리합니다. 테스트 재실행으로 안전성을 확인합니다.
도구별 활용 팁
| 도구 | 강점 | SDD+TDD 활용법 |
|---|---|---|
| GitHub Copilot | 테스트-코드 순환 최적화 | Spec Kit과 함께 사용, 테스트에서 구현 자동 생성 |
| Claude Code | 컨텍스트 이해와 계획 수립 | 명세를 CLAUDE.md에 포함, TDD 사이클 전체 관리 |
| Cursor | 빠른 편집과 자동완성 | 인라인 명세 주석으로 가이드, 짧은 반복 사이클 |
인지 위축에 대한 경고
AI 도구가 강력해질수록, 한 가지 위험이 커집니다. **인지 위축(Cognitive Atrophy)**입니다.
AI가 코드를 대신 짜주니 직접 코딩하는 빈도가 줄어듭니다. 코딩 빈도가 줄면 문제 해결 능력과 디버깅 감각이 둔해집니다. 이것이 인지 위축입니다. 마치 내비게이션에 의존하다 보면 길을 찾는 감각이 사라지는 것과 같습니다.
SDD + TDD는 이 문제에 대한 안전장치이기도 합니다.
- 명세 작성은 문제를 깊이 이해해야만 가능합니다
- 테스트 설계는 엣지 케이스를 직접 사고해야 합니다
- 코드 리뷰는 AI 출력물을 비판적으로 평가하는 훈련이 됩니다
AI를 활용하되, 핵심 역량의 근육은 꾸준히 키워야 합니다. “사령관”이라 하더라도 전장의 기본 원리를 모르면 잘못된 명령을 내릴 수밖에 없습니다.
마무리
AI 시대의 개발자에게 가장 중요한 능력은 코드를 빨리 짜는 것이 아닙니다. 무엇을 만들어야 하는지 정확히 정의하고, 만들어진 결과를 검증하는 능력입니다.
SDD는 명세를 통해 AI에게 명확한 방향을 제시하고, TDD는 테스트를 통해 결과물의 정확성을 보장합니다. 이 둘의 결합은 AI 코딩 에이전트를 “운에 맡기는 도구”에서 “예측 가능한 파트너”로 바꿔줍니다.
시작은 간단합니다. 다음에 AI에게 코드를 요청할 때, 자연어 지시 대신 명세를 먼저 작성해보세요. 그리고 테스트를 먼저 만들어 보세요. 그 작은 변화가 AI와의 협업 품질을 크게 바꿀 것입니다.