✅ TDD란?
TDD는 소프트웨어 개발 방법론 중 하나이다.
기존 개발 방식이 코드 작성 -> 테스트 코드 작성이었다면, TDD는 반대로 실패하는 테스트 코드 작성 -> 프로덕션 코드 작성 -> 리팩토링 단계를 반복한다.
TDD는 단순히 테스트를 위한 기술이 아니라, 좋은 설계를 이끌어내는 데 중점을 둔 개발 방법론에 가깝다. 코드를 작성하기 전에 '무엇을 만들 것인가'에 집중하게 해주는 설계 기술이다.
✅ TDD 개발 주기
TDD는 Red - Green - Refactor 세 가지 단계를 반복하며 진행된다.

📑 Red
: 실패하는 테스트 작성
Red에서는 아직 존재하지 않는 프로덕션 코드를 위한 테스트 코드를 먼저 작성한다. 앞으로 만들 코드가 어떤 기능을 해야 하는지, 어떤 결과를 반환해야 하는지 정의하는 과정과 같다.
테스트 코드를 먼저 작성하기 때문에 테스트 코드가 기대하는 로직이 실행되지 않아 테스트는 실패할 것이다. 핵심은 실패하는 테스트 코드를 작성하는 것이다.
@Test
void plusTest() {
//given (준비)
Calculator calculator = new Calculator();
//when (실행)
int result = calculator.plus(1, 3);
//then (검증)
assertThat(result).isEqualTo(4);
}
📑 Green
: 테스트를 통과하는 최소한의 코드 작성
Green에서는 방금 작성한 테스트를 통과시키는 것을 목표로 최소한의 프로덕션 코드를 작성한다. 이때 복잡한 로직을 모두 구현하려 하지 않고 테스트를 통과할 만큼만 빠르게 코드를 만든다. 이를 '가짜 구현(Fake it)'이라고도 부른다.
빠르게 구현할 수 있는 깔끔한 코드가 있다면 바로 작성하면 되지만 깔끔하게 코드를 작성하는 일이 오래 (몇 분 정도) 걸릴 것 같다면 일단 테스트 코드를 통과하는 데에만 집중하여 코드를 작성해도 된다. 켄트백은 이를 죄악이라고 표현하는데, Green을 보기 위해서는 어떤 죄악을 저질러도 상관없다.
Green을 위해서 실제 코드를 구현하는 방법도 있지만, 최대한 빨리 Green을 보기 위해서 상수를 반환하고 점진적으로 변수로 바꾸는 방법도 있다.
public class Calculator {
public int plus(int a, int b) {
return 4; // 일단 테스트 통과를 위해 상수를 반환
}
}
📑 Refactor
: 코드를 개선
테스트를 통과한 후에는 코드를 더 좋게 다듬는 리팩토링 단계를 수행한다.
Green 단계에서 임시로 작성한 코드를 실제 로직에 맞게 수정하고, 중복 제거 또는 가독성을 높이는 등의 작업을 수행한다.
public class Calculator {
public int plus(int a, int b) {
return a + b; // 올바른 로직으로 리팩토링
}
}
TDD는 이렇게 작은 단위를 반복하며 점진적으로 코드를 개선할 수 있게 해 준다.
✅ TDD 원칙
- 실패하는 단위 테스트를 작성하기 전에는 프로덕션 코드를 작성하지 않는다.
- 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
- 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
✅ TDD의 필요성
- 변화에 대한 불안감 해소
- 새로운 기능을 추가하거나 기존 코드를 수정할 때 사이드 이펙트가 발생할까 봐 불안해할 필요가 없다. 미리 작성된 테스트 코드가 있으면, 다른 코드에 미치는 영향을 즉시 파악하고 문제를 빠르게 해결할 수 있다.
- 한 번에 한 가지 일에 집중
- 현재 실패한 테스트 케이스를 통과하는 데만 집중하도록 만든다. 개발 과정에서 다른 기능이나 모듈에 신경이 분산되는 것을 막아주어 명확한 목표를 가지고 효율적으로 코딩할 수 있다.
- 빠른 피드백과 점진적 설계 개선
- 코드를 작성하는 즉시 테스트를 통해 피드백을 받을 수 있다. 처음부터 완벽한 설계를 하기보다는, 작은 기능부터 구현하고 테스트를 통과시키며 점진적으로 설계를 개선할 수 있다.
- 테스트 코드가 곧 문서 역할
- 테스트 코드는 해당 코드가 원래 어떻게 작동해야 하는지를 보여준다.
- 더 높은 코드 품질
- TDD는 낮은 결합도와 높은 응집도를 가진 코드를 작성하도록 유도한다. 테스트하기 어려운 코드는 대개 모듈 간 의존성이 높기 때문에 자연스럽게 테스트하기 쉬운, 잘 설계된 코드를 만들게 된다.