핵심 인사이트

  1. 본질: TDD (Test Driven Development, 테스트 주도 개발)는 Red → Green → Refactor의 짧은 주기를 반복해, 테스트가 없으면 코드를 작성하지 않는 원칙으로 설계와 검증을 동시에 달성하는 개발 방법론이다.
  2. 가치: TDD는 단순한 테스팅 기법이 아니라 설계 도구다—테스트를 먼저 작성하면 코드의 인터페이스(API)를 사용자 관점에서 설계하게 되어, 불필요한 복잡도를 자연스럽게 제거한다.
  3. 판단 포인트: 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

구분TDDBDDATDD
초점구현 단위 (함수/메서드)행위/시나리오인수 기준
언어프로그래밍 언어Given/When/Then (자연어)인수 테스트
작성자개발자개발자+QA+비즈니스전체 팀
테스트 수준단위 테스트통합/행위 테스트인수 테스트
도구JUnit, pytestCucumber, SpecFlowFitNesse, Robot

테스트 피라미드

테스트 피라미드:
              /\
             /  \
            / E2E\   ← 少 (UI, 시스템 전체, 느림)
           /──────\
          / 통합  \  ← 中 (API, 컴포넌트 통합)
         /────────\
        / 단위     \  ← 多 (TDD, 빠름, 격리)
       /────────────\
  
  TDD는 단위 테스트 계층을 풍부하게 구성
  → 빠른 피드백, 낮은 비용

📢 섹션 요약 비유: 테스트 피라미드는 건물의 기초와 같다. 가장 많은 단위 테스트(기초)가 있어야 통합 테스트(벽)와 E2E 테스트(지붕)가 안정적으로 올라설 수 있다—기초(단위 테스트)가 약하면 전체 구조가 흔들린다.


Ⅳ. 실무 적용 및 기술사 판단

TDD 도입 장애물 및 극복

장애물 1: "시간이 더 걸린다"
  현실: 단기적으로 10~20% 추가 시간 소요
  반론: 장기적으로 디버깅·재작업 시간이 크게 줄어 순이익

장애물 2: "레거시 코드에는 적용 불가"
  해결: Strangler Fig 패턴—새 기능은 TDD로, 레거시는 리팩터링 시 점진적 적용

장애물 3: "테스트 작성 방법을 모른다"
  해결: 페어 프로그래밍, TDD 코칭, Kata(연습 문제) 활용

장애물 4: "UI는 테스트하기 어렵다"
  해결: UI 로직을 ViewModel/Presenter로 분리해 단위 테스트 가능하게 설계

기술사 시험 판단 포인트

  1. TDD 순서: Red → Green → Refactor (항상 이 순서)
  2. 테스트 실패가 시작: 테스트가 통과하는 상태에서 시작하면 TDD가 아님
  3. FIRST 원칙: 좋은 테스트의 5가지 특성 암기
  4. TDD ≠ BDD: TDD는 구현 중심, BDD는 행위/비즈니스 가치 중심
  5. 회귀 테스트: TDD 결과로 쌓인 테스트 수트가 회귀 방지의 핵심

📢 섹션 요약 비유: TDD 도입은 자전거 타는 법 배우기와 같다. 처음에는 더 느리고 어색하지만, 한번 익히면 평생 사용할 수 있는 기술이 된다—초기 투자가 장기 효율로 돌아온다.


Ⅴ. 기대효과 및 결론

TDD를 체계적으로 적용하면:

  1. 결함 밀도 감소: 연구에 따르면 TDD를 적용한 프로젝트는 결함이 40~80% 감소한다.
  2. 설계 품질 향상: 테스트 가능한 코드는 자연스럽게 SOLID 원칙을 따른다.
  3. 자신감 있는 리팩터링: 테스트 수트가 안전망이 되어 코드 개선을 두려워하지 않는다.
  4. 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줄 비유 설명

  1. TDD는 게임 미션을 먼저 정하고(Red) 해결 방법을 찾는 것이에요 — "이 적을 3번 만에 무찌르기"라는 목표(테스트)를 먼저 쓰고, 그것을 통과하는 전략(코드)을 짜요.
  2. 처음엔 실패하는 게 당연해요(Red) — 아직 코드가 없으니까요. 코드를 짜서 통과하면(Green), 더 멋지게 다듬어요(Refactor).
  3. 테스트가 있으면 나중에 코드를 바꿔도 안심이에요 — 친구가 내 숙제를 검사해주면 실수가 없는 것처럼, 테스트가 항상 내 코드를 지켜줘요.