핵심 인사이트 (3줄 요약)
- 본질: TDD (Test Driven Development, 테스트 주도 개발)는 실패하는 테스트를 먼저 쓰고, 그 테스트를 통과시키는 최소 코드와 리팩터링을 반복하는 개발 방식이다.
- 가치: TDD는 결함 검출 기법을 넘어 인터페이스와 책임을 먼저 설계하게 만들어, 코드 구조를 단순하고 검증 가능하게 만든다.
- 판단 포인트: TDD의 성패는 테스트 개수보다 주기의 짧음과 피드백의 신뢰성에 달려 있으므로, 작은 단위·빠른 실행·명확한 의도를 유지해야 한다.
Ⅰ. 개요 및 필요성
TDD는 켄트 벡 (Kent Beck)이 익스트림 프로그래밍 (Extreme Programming, XP) 실천법으로 체계화한 개발 접근이다. 핵심은 "구현 전에 기대 동작을 먼저 기술한다"는 점이다. 즉 테스트는 개발이 끝난 뒤 품질을 검사하는 사후 절차가 아니라, 개발을 시작하는 명세가 된다.
이 방식이 필요한 이유는 전통적인 코드 우선 개발이 쉽게 두 가지 문제를 만들기 때문이다. 첫째, 개발자가 구현 세부사항에 먼저 빠져 실제 요구사항을 놓치기 쉽다. 둘째, 나중에 테스트를 붙이면 이미 커진 코드 구조를 뒤늦게 검증해야 하므로 결합도가 높고 테스트 작성도 어려워진다. TDD는 이 흐름을 뒤집어 "어떻게 만들까"보다 "무엇이 성공인가"를 먼저 고정한다.
예를 들어 할인 쿠폰 도메인 서비스를 만든다고 하자. 테스트를 먼저 쓰면 만료 쿠폰은 거절, 중복 적용 금지, 최대 할인 한도 1만 원 같은 비즈니스 규칙이 함수 시그니처보다 먼저 드러난다. 이렇게 하면 구현은 자연스럽게 작고 명확한 책임으로 쪼개지고, 이후 지속적 통합 (Continuous Integration, CI) 파이프라인에서도 회귀 검증이 쉬워진다.
- 📢 섹션 요약 비유: TDD는 집을 다 지은 뒤 줄자를 대는 방식이 아니라, 먼저 설계도에 문 크기와 방 배치를 적고 그 기준에 맞춰 벽을 세우는 방식이다.
Ⅱ. 아키텍처 및 핵심 원리
TDD의 기본 사이클은 Red → Green → Refactor다. 먼저 실패하는 테스트를 작성해 요구사항을 코드로 표현하고(Red), 그 테스트를 통과시키는 가장 단순한 구현을 만든 뒤(Green), 마지막으로 중복 제거와 구조 개선을 수행한다(Refactor). 여기서 중요한 것은 Green 단계에서 "정답 같은 코드"를 만들려 하지 않는 것이다. 우선 통과시키고, 설계 개선은 리팩터 단계로 분리해야 주기가 짧아진다.
┌──────────────────────────────────────────────────────────────┐
│ TDD의 Red → Green → Refactor │
├──────────────────────────────────────────────────────────────┤
│ 1) RED : 실패하는 테스트 작성 │
│ └─ 요구사항을 실행 가능한 명세로 표현 │
│ 2) GREEN : 최소 코드로 테스트 통과 │
│ └─ 과설계 금지, 일단 동작 우선 │
│ 3) REFACTOR : 중복 제거, 이름 개선, 책임 분리 │
│ └─ 테스트가 안전망 역할 수행 │
│ ▲ │
│ └──────── 다음 요구사항으로 반복 │
└──────────────────────────────────────────────────────────────┘
좋은 TDD를 위해서는 테스트 자체의 품질도 중요하다. 흔히 FIRST 원칙이라 하여 빠름 (Fast), 독립성 (Independent), 반복 가능성 (Repeatable), 자기 검증성 (Self-Validating), 시기적절함 (Timely)을 요구한다. 테스트 실행이 몇 분씩 걸리거나 외부 환경에 따라 흔들리면, 개발자는 점점 테스트를 덜 돌리게 되고 TDD의 피드백 루프가 끊긴다.
| 원칙 | 의미 | 실무 적용 포인트 |
|---|---|---|
| Fast | 빨라야 자주 돌린다 | 단위 테스트는 초 단위가 아니라 가능하면 밀리초 수준 목표 |
| Independent | 순서 의존이 없어야 한다 | 공유 상태 제거, 테스트별 초기화 분리 |
| Repeatable | 환경이 달라도 같은 결과가 나와야 한다 | 시간·난수·외부 응용 프로그래밍 인터페이스 (Application Programming Interface, API)를 격리 |
| Self-Validating | 사람이 로그를 읽지 않아도 합격/실패를 판단해야 한다 | 명확한 Assert 작성 |
| Timely | 구현 직전 또는 동시에 작성해야 한다 | 사후 보완식 테스트 지양 |
외부 의존성은 목 객체 (Mock), 스텁 (Stub), 페이크 (Fake)로 격리한다. 예를 들어 주문 서비스 테스트에서 실제 데이터베이스를 붙이면 테스트 속도가 느려지고 실패 원인이 모호해진다. 이때 인메모리 저장소나 결제용 응용 프로그래밍 인터페이스 (Application Programming Interface, API) 스텁을 사용하면 비즈니스 규칙 검증에 집중할 수 있다. 핵심은 "무엇을 검증할 것인가"를 명확히 분리하는 것이다.
- 📢 섹션 요약 비유: TDD는 요리를 바로 손님상에 내는 것이 아니라, 먼저 한입 크기 샘플로 맛을 확인하고 조미료를 조금씩 조정하는 과정과 같다. 큰 냄비를 한 번에 고치려 하면 늦고 위험하다.
Ⅲ. 비교 및 연결
TDD는 단순히 테스트를 많이 쓰는 것과 다르다. 테스트 후 개발은 구현이 끝난 뒤 검증을 붙이므로, 코드 구조가 이미 굳은 다음 테스트가 따라간다. 반면 TDD는 테스트가 설계를 이끌기 때문에 인터페이스와 책임이 먼저 정제된다. 따라서 둘의 차이는 순서가 아니라 설계 주도권이 어디에 있느냐에 있다.
| 구분 | TDD | 테스트 후 개발 |
|---|---|---|
| 시작점 | 실패 테스트 | 구현 코드 |
| 테스트 역할 | 명세 + 설계 도구 | 검증 도구 |
| 피드백 시점 | 구현 직전·직후 | 구현 완료 후 |
| 구조 영향 | 작은 객체, 낮은 결합도 유도 | 구조 개선 효과 제한 |
또한 TDD는 행위 주도 개발 (Behavior Driven Development, BDD), 인수 테스트 주도 개발 (Acceptance Test Driven Development, ATDD)과도 연결된다. TDD가 개발자 관점의 단위 수준 명세에 강하다면, BDD는 사용자 행위와 시나리오 표현에, ATDD는 비즈니스 수용 기준 합의에 강하다. 실무에서는 세 접근이 경쟁 관계라기보다 계층 관계에 가깝다. 아래층은 TDD의 빠른 단위 피드백, 위층은 BDD·ATDD의 시나리오 검증으로 구성된다.
사용자 가치 검증
▲
│ ATDD / BDD
│
통합 시나리오 검증
▲
│
TDD 기반 단위 테스트
▲
│
구현 설계 개선
따라서 TDD는 테스트 피라미드의 가장 넓은 하단을 튼튼하게 만드는 방식으로 이해하면 좋다. 상단의 종단간 테스트 (End-to-End Test, E2E Test)는 꼭 필요하지만 느리고 비싸다. 반대로 TDD로 만든 단위 테스트는 빠르고 세밀하게 실패 지점을 알려 주므로, 전체 품질 비용을 낮추는 기반이 된다.
- 📢 섹션 요약 비유: TDD는 건물 기초 공사에 가깝다. 눈에 잘 보이지는 않지만, 기초가 튼튼해야 위층의 화려한 인테리어와 지붕도 안전하게 올라간다.
Ⅳ. 실무 적용 및 기술사 판단
실무에서 TDD를 도입할 때 가장 많이 나오는 반론은 "지금도 일정이 빠듯한데 테스트를 먼저 쓰면 더 느려진다"는 것이다. 단기적으로는 사실일 수 있다. 그러나 결함 수정, 회귀 장애, 리팩터링 공포로 잃는 시간이 더 크다. 특히 여러 개발자가 동시에 만지는 서비스에서는 작은 단위의 자동 검증이 없으면 변경 비용이 눈덩이처럼 커진다.
예를 들어 결제 정산 모듈을 개발한다고 하자. 정산 금액 계산 규칙, 환불 시 수수료 처리, 영업일 마감 배치 조건은 작은 오차도 큰 손실을 만든다. 이런 영역은 사용자 인터페이스 (User Interface, UI)부터 테스트하려 하기보다 계산 로직과 도메인 규칙을 TDD로 먼저 굳히는 것이 효과적이다. 반대로 화면 배치나 픽셀 단위 스타일링처럼 변경이 잦고 규칙성이 약한 부분은 TDD보다 스냅샷 테스트나 탐색적 검증이 더 효율적일 수 있다.
기술사 판단 체크리스트
- 순서: 실패 테스트 → 최소 구현 → 리팩터 순서를 지키는가.
- 범위: 도메인 규칙처럼 안정된 핵심 로직부터 적용하는가.
- 속도: 로컬에서 수초 내 반복 가능한 테스트 세트를 유지하는가.
- 격리: 데이터베이스, 메시지 브로커, 외부 API 의존성을 직접 붙이지 않는가.
- 운영 연계: CI와 코드 리뷰에서 테스트를 품질 게이트로 활용하는가.
안티패턴
- 처음부터 거대한 통합 테스트만 작성하고 이를 TDD라 부르는 경우
- Green 단계에서 미래 확장까지 미리 구현해 과설계하는 경우
- 실패 원인이 모호한 테스트를 많이 쌓아 개발자 신뢰를 잃는 경우
레거시 시스템이라고 해서 TDD를 아예 못 하는 것은 아니다. 새 기능부터 작은 경계에 도입하고, 변경 전 Characterization Test로 기존 동작을 고정한 뒤 점진적으로 확장하면 된다. 기술사 답안에서는 이런 점진 도입 전략까지 언급해야 현실성이 높다.
- 📢 섹션 요약 비유: TDD는 매번 먼 길을 뛰는 훈련이 아니라, 짧은 왕복 달리기를 반복해 자세를 고치는 연습과 같다. 짧게 자주 반복할수록 실전에서 더 빨라진다.
Ⅴ. 기대효과 및 결론
TDD를 꾸준히 적용하면 결함 발견 시점이 앞당겨지고, 설계가 자연스럽게 모듈화된다. 이는 변경 영향 범위를 줄이고, 리팩터링을 두려워하지 않는 개발 문화를 만드는 데 큰 역할을 한다. 특히 장기 운영 서비스에서는 테스트 스위트가 일종의 살아 있는 문서 역할을 하여, 신규 인력 온보딩과 회귀 검증 모두에 도움이 된다.
물론 TDD에도 비용은 있다. 테스트 작성 역량이 부족하면 형식적인 테스트만 늘고, 잘못된 추상화 위에 테스트를 과도하게 고정하면 리팩터링이 오히려 어려워질 수 있다. 따라서 TDD의 목표는 테스트 수를 늘리는 것이 아니라, 설계와 피드백 루프를 건강하게 유지하는 것이다.
결론적으로 TDD는 "테스트를 먼저 쓰는 습관"이 아니라 "작게 설계하고 빠르게 검증하는 개발 리듬"이다. 이 리듬이 정착되면 품질과 속도는 경쟁 관계가 아니라 서로를 강화하는 관계로 바뀐다.
- 📢 섹션 요약 비유: TDD는 어두운 길을 손전등으로 한 걸음씩 비추며 걷는 방식이다. 멀리까지 한 번에 보이지는 않지만, 넘어지지 않고 끝까지 가게 해 준다.
📌 관련 개념 맵
| 개념 | 연결 포인트 |
|---|---|
| XP (Extreme Programming) | TDD를 핵심 실천법으로 포함하는 애자일 개발 방식 |
| 리팩터링 (Refactoring) | 동작을 유지한 채 구조를 개선하는 TDD의 3단계 |
| 테스트 더블 (Test Double) | Mock, Stub, Fake 등 의존성 격리 수단 |
| CI (Continuous Integration) | 누적 테스트를 자동 실행해 회귀를 방지하는 운영 기반 |
| BDD (Behavior Driven Development) | 사용자 행위 시나리오에 초점을 두는 상위 검증 방식 |
| E2E Test (End-to-End Test) | 시스템 전체 흐름을 검증하는 상위 계층 테스트 |
📈 관련 키워드 및 발전 흐름도
요구사항 명세
│
▼
실패 테스트 작성 (Red)
│
▼
최소 구현 (Green)
│
▼
리팩터링 (Refactor)
│
▼
테스트 스위트 축적
│
▼
CI 기반 회귀 방지 · 지속적 설계 개선
이 흐름은 "명세를 실행 코드로 고정"한 뒤, 반복 가능한 자동 검증 자산으로 성장시키는 TDD의 본질을 보여준다.
👶 어린이를 위한 3줄 비유 설명
- TDD는 먼저 "이 장난감 자동차가 앞으로 잘 가야 해"라고 시험 문제를 만드는 거예요.
- 그다음 자동차를 조금 만들고 시험해 보고, 다시 고치면서 점점 잘 달리게 만들어요.
- 그래서 나중에 바퀴를 바꿔도 자동차가 제대로 가는지 바로 확인할 수 있어요.