핵심 인사이트
- 본질: TDD (Test Driven Development, 테스트 주도 개발)는 Red → Green → Refactor의 짧은 주기를 반복해, 테스트가 없으면 코드를 작성하지 않는 원칙으로 설계와 검증을 동시에 달성하는 개발 방법론이다.
- 가치: TDD는 단순한 테스팅 기법이 아니라 설계 도구다—테스트를 먼저 작성하면 코드의 인터페이스(API)를 사용자 관점에서 설계하게 되어, 불필요한 복잡도를 자연스럽게 제거한다.
- 판단 포인트: TDD의 핵심은 "테스트가 먼저, 코드가 나중"—그린(Green) 단계에서 가장 단순한 코드로 테스트를 통과시키고, 리팩터(Refactor) 단계에서 설계를 개선한다.
Ⅰ. 개요 및 필요성
TDD (Test Driven Development, 테스트 주도 개발)는 켄트 벡 (Kent Beck)이 XP (eXtreme Programming)의 핵심 실천법으로 체계화한 개발 방법론이다. 전통적인 "코드 먼저, 테스트 나중" 방식을 뒤집어 "테스트 먼저, 코드 나중"으로 개발의 흐름을 재정의한다.
TDD가 필요한 이유는 세 가지다. 첫째, 품질 보증—테스트를 먼저 작성하면 코드 완성 후 결함을 발견하는 것이 아니라 코드 작성 중 실시간으로 검증한다. 둘째, 설계 개선—테스트 가능한(Testable) 코드는 자연스럽게 단일 책임(SRP)과 낮은 결합도(Low Coupling)를 갖는다. 셋째, 리팩터링 안전망—테스트 수트(Test Suite)가 있으면 코드를 자신있게 개선할 수 있다.
실무에서 TDD의 가장 큰 가치는 회귀 테스트(Regression Test) 자동화다. 새 기능 추가 후 기존 기능이 망가졌는지(Regression) 수동으로 확인하는 대신, 축적된 테스트 수트가 자동으로 검증한다. CI/CD 파이프라인에서 모든 커밋마다 자동 실행되어 개발 속도와 품질을 동시에 향상시킨다.
📢 섹션 요약 비유: TDD는 레시피를 쓰기 전에 먼저 "완성된 음식이 어떤 맛이어야 하는가"를 정의하는 것이다—"매콤달콤하고 바삭해야 한다(테스트 먼저)"고 정의한 후에 그 기준을 만족하는 레시피(코드)를 작성하는 방식이다.
Ⅱ. 아키텍처 및 핵심 원리
TDD 3단계 사이클 (Red-Green-Refactor)
┌───────────────────────────────────────────────────────────────┐
│ TDD 사이클: Red → Green → Refactor │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ 🔴 RED: 실패하는 테스트 작성 │ │
│ │ • 아직 구현 없음 → 테스트 실패(Red) │ │
│ │ • "무엇을 만들 것인가" 명세 │ │
│ └────────────────────┬────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────┐ │
│ │ 🟢 GREEN: 최소한의 코드로 테스트 통과 │ │
│ │ • 가장 단순한 방법으로 테스트 통과 │ │
│ │ • "어떻게든 동작하게" │ │
│ └────────────────────┬────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────┐ │
│ │ 🔵 REFACTOR: 코드 품질 개선 │ │
│ │ • 중복 제거, 명확한 이름, 구조 개선 │ │
│ │ • 테스트는 여전히 통과해야 함 │ │
│ └────────────────────┬────────────────────┘ │
│ ↓ │
│ 다음 테스트 → RED로 반복 │
└──────────────────────────────────────────────────────────────┘
FIRST 원칙
좋은 단위 테스트의 5가지 원칙:
F - Fast (빠름): 빠르게 실행 (밀리초 단위)
I - Independent (독립적): 테스트 간 의존성 없음
R - Repeatable (반복 가능): 환경에 관계없이 동일 결과
S - Self-Validating (자기 검증): Assert로 Pass/Fail 자동 판단
T - Timely (시기적절): 프로덕션 코드 작성 전 또는 동시
→ FIRST를 만족해야 테스트 수트가 신뢰성 있게 동작
Mocking과 Stubbing
외부 의존성 격리 기법:
Mock (목): 행위 기반 검증
→ "서비스가 DB에 저장을 호출했는가?"
→ mockRepository.save()가 1번 호출됐는지 확인
Stub (스텁): 상태 기반 검증
→ 테스트 중 외부 API를 대신하는 가짜 응답 제공
→ when(api.getData()).thenReturn(fakeData)
Spy (스파이): 실제 객체 + 호출 기록
→ 실제 동작하면서 호출 횟수/인수를 기록
Fake (페이크): 실제와 유사한 경량 구현
→ InMemoryRepository (DB 대신 메모리 사용)
TDD 코드 예시 (Python)
# 1. RED: 실패하는 테스트 작성
def test_add():
calc = Calculator()
assert calc.add(2, 3) == 5 # Calculator 클래스 없음 → 실패
# 2. GREEN: 최소 코드로 통과
class Calculator:
def add(self, a, b):
return a + b # 가장 단순한 구현
# 3. REFACTOR: 개선 (예: 타입 힌트 추가)
class Calculator:
def add(self, a: int, b: int) -> int:
return a + b # 테스트 여전히 통과
📢 섹션 요약 비유: TDD의 Red-Green-Refactor는 건물 짓기와 같다. 먼저 "어떤 건물이어야 하는가"를 설계도(테스트)로 그리고(Red), 그 도면을 만족하는 가장 단순한 건물을 짓고(Green), 그 다음 외관을 아름답게 다듬는 것(Refactor)이다.
Ⅲ. 비교 및 연결
TDD vs BDD vs ATDD
| 구분 | TDD | BDD | ATDD |
|---|---|---|---|
| 초점 | 구현 단위 (함수/메서드) | 행위/시나리오 | 인수 기준 |
| 언어 | 프로그래밍 언어 | Given/When/Then (자연어) | 인수 테스트 |
| 작성자 | 개발자 | 개발자+QA+비즈니스 | 전체 팀 |
| 테스트 수준 | 단위 테스트 | 통합/행위 테스트 | 인수 테스트 |
| 도구 | JUnit, pytest | Cucumber, SpecFlow | FitNesse, Robot |
테스트 피라미드
테스트 피라미드:
/\
/ \
/ E2E\ ← 少 (UI, 시스템 전체, 느림)
/──────\
/ 통합 \ ← 中 (API, 컴포넌트 통합)
/────────\
/ 단위 \ ← 多 (TDD, 빠름, 격리)
/────────────\
TDD는 단위 테스트 계층을 풍부하게 구성
→ 빠른 피드백, 낮은 비용
📢 섹션 요약 비유: 테스트 피라미드는 건물의 기초와 같다. 가장 많은 단위 테스트(기초)가 있어야 통합 테스트(벽)와 E2E 테스트(지붕)가 안정적으로 올라설 수 있다—기초(단위 테스트)가 약하면 전체 구조가 흔들린다.
Ⅳ. 실무 적용 및 기술사 판단
TDD 도입 장애물 및 극복
장애물 1: "시간이 더 걸린다"
현실: 단기적으로 10~20% 추가 시간 소요
반론: 장기적으로 디버깅·재작업 시간이 크게 줄어 순이익
장애물 2: "레거시 코드에는 적용 불가"
해결: Strangler Fig 패턴—새 기능은 TDD로, 레거시는 리팩터링 시 점진적 적용
장애물 3: "테스트 작성 방법을 모른다"
해결: 페어 프로그래밍, TDD 코칭, Kata(연습 문제) 활용
장애물 4: "UI는 테스트하기 어렵다"
해결: UI 로직을 ViewModel/Presenter로 분리해 단위 테스트 가능하게 설계
기술사 시험 판단 포인트
- TDD 순서: Red → Green → Refactor (항상 이 순서)
- 테스트 실패가 시작: 테스트가 통과하는 상태에서 시작하면 TDD가 아님
- FIRST 원칙: 좋은 테스트의 5가지 특성 암기
- TDD ≠ BDD: TDD는 구현 중심, BDD는 행위/비즈니스 가치 중심
- 회귀 테스트: TDD 결과로 쌓인 테스트 수트가 회귀 방지의 핵심
📢 섹션 요약 비유: TDD 도입은 자전거 타는 법 배우기와 같다. 처음에는 더 느리고 어색하지만, 한번 익히면 평생 사용할 수 있는 기술이 된다—초기 투자가 장기 효율로 돌아온다.
Ⅴ. 기대효과 및 결론
TDD를 체계적으로 적용하면:
- 결함 밀도 감소: 연구에 따르면 TDD를 적용한 프로젝트는 결함이 40~80% 감소한다.
- 설계 품질 향상: 테스트 가능한 코드는 자연스럽게 SOLID 원칙을 따른다.
- 자신감 있는 리팩터링: 테스트 수트가 안전망이 되어 코드 개선을 두려워하지 않는다.
- CI/CD 통합: 모든 커밋마다 자동 실행되는 테스트로 지속적 품질 검증이 가능하다.
TDD의 궁극적 가치는 개발자의 자신감이다. "내 코드가 올바르게 동작한다"는 것을 매 작성마다 확인받으면, 대규모 변경도 두려움 없이 시도할 수 있다. 이 자신감이 코드베이스의 장기 건강을 유지하는 문화적 기반이 된다.
📢 섹션 요약 비유: TDD 없는 대규모 코드베이스는 지뢰밭과 같다. 어디서 터질지 모르는 버그 때문에 조심스럽게 걷는 것처럼 코드를 수정하게 된다. TDD의 테스트 수트는 지뢰 탐지기처럼, 무엇이 깨질지를 즉시 알려준다.
📌 관련 개념 맵
| 개념 | 설명 | 연관 키워드 |
|---|---|---|
| TDD (Test Driven Development) | Red→Green→Refactor 짧은 주기 개발 | Kent Beck, XP |
| Red (실패) | 아직 구현 없는 실패 테스트 작성 | TDD 1단계 |
| Green (통과) | 최소 코드로 테스트 통과 | TDD 2단계 |
| Refactor (개선) | 중복 제거, 구조 개선 (테스트 유지) | TDD 3단계 |
| FIRST 원칙 | Fast/Independent/Repeatable/Self-Validating/Timely | 단위 테스트 |
| Mock/Stub (목/스텁) | 외부 의존성 격리 기법 | 단위 테스트 |
| 회귀 테스트 | 기존 기능이 깨지지 않았는지 검증 | CI/CD |
| 테스트 피라미드 | 단위>통합>E2E 테스트 비율 | 테스트 전략 |
👶 어린이를 위한 3줄 비유 설명
- TDD는 게임 미션을 먼저 정하고(Red) 해결 방법을 찾는 것이에요 — "이 적을 3번 만에 무찌르기"라는 목표(테스트)를 먼저 쓰고, 그것을 통과하는 전략(코드)을 짜요.
- 처음엔 실패하는 게 당연해요(Red) — 아직 코드가 없으니까요. 코드를 짜서 통과하면(Green), 더 멋지게 다듬어요(Refactor).
- 테스트가 있으면 나중에 코드를 바꿔도 안심이에요 — 친구가 내 숙제를 검사해주면 실수가 없는 것처럼, 테스트가 항상 내 코드를 지켜줘요.