TDD(Test-Driven Development)는 Kent Beck이 제안한 개발 방법론으로, "테스트 코드를 먼저 작성하고, 테스트를 통과하는 코드를 구현하며, 이를 반복적으로 개선하는 개발 방식"이다.
TDD의 기본 사이클은 다음과 같다:
1. Red: 실패하는 테스트를 먼저 작성한다 (아직 기능이 구현되지 않았기 때문)
2. Green: 테스트를 통과하도록 최소한의 코드를 작성한다
3. Refactor: 중복을 제거하고 구조를 개선한다
이 흐름은 단순히 테스트 중심의 개발이 아니라, 안정성과 리팩토링 가능성을 확보하는 개발 전략이다.
처음엔 TDD가 어렵게 느껴졌다.
테스트 코드를 작성하는 것조차 익숙하지 않았고,
“어디까지 테스트를 해야 하지?” “테스트는 언제 해야 해?” 같은 고민이 많았다.
TDD를 처음 시작했을 때는 이런 생각이 들었다.
처음엔 테스트 작성이 너무 번거롭고, 시간 낭비처럼 느껴지기도 했다. 하지만 점점 기능 단위로 분리된 테스트를 작성하고, 그 테스트를 통해 빠르게 피드백을 받다 보니 "테스트는 개발의 일부이며, 궁극적으로는 유지보수 비용을 줄이기 위한 투자"라는 걸 체감했다.
그리고 이번 과정을 통해 느낀 건 단순했다.
진짜 중요한 건 '이 코드가 진짜 안전한가?' 를 확인하는 것
이전에는 기능을 만든 후 curl, postman, 프론트 요청 등으로 일일이 확인했다.
시간도 오래 걸리고, 매번 같은 테스트를 반복하면서 실수도 생겼다.
하지만 지금은 다르다.
// 이전: postman → DB 확인 → 로그 확인
// 지금: 테스트 코드 한 줄 → assertThat(result).isEqualTo(...)
그리고 무엇보다 중요한 건 '빠른 검증'이다. 한 기능을 만들고 그것이 잘 작동하는지 검증하기 위해 굳이 전체 애플리케이션을 실행하거나, API 호출 → DB 확인 → 로그 추적 등의 수고를 들이지 않아도 된다. 정말 중요한 기능 단위만 쏙 빼서, 빠르게 테스트할 수 있다.
이걸 가능하게 만들기 위해선 "불필요한 의존성 제거"가 핵심이었다. 테스트하려는 단위 외 모든 것들을 "더미"로 치환하고, 진짜 중요한 그 한 단위만 순수하게 검증하는 구조를 만들기 위해 연습했고, 고민했다.
첨예한 테스트 케이스를 만들기 위해선 어떤 조건에서 실패할 수 있는지를 먼저 고민해야 했다. 실패할 수 있는 조건을 설계하고, 그걸 테스트로 먼저 박아두니 기능 자체도 더 날카로워졌고, 사소한 케이스도 놓치지 않게 됐다.
| 테스트 종류 | 역할 |
|---|---|
| Unit Test | 하나의 클래스/기능만 테스트 (Mock 적극 사용) |
| Integration Test | 여러 Bean이 얽힌 흐름 테스트 (DB, API 포함) |
| E2E Test | 사용자의 실제 시나리오 기반 전체 흐름 검증 |
→ 이번엔 주로 단위 테스트에 집중하며, 구조를 테스트하기 좋은 형태로 리팩토링하는 경험을 했다.
"예외를 발생시킬 수 있는 모든 경우를 내가 직접 컨트롤할 수 있어야 한다."
TDD를 통해 설계와 책임 분리에 대해 끊임없이 고민하다 보면, 자연스럽게 클린 아키텍처(Clean Architecture)의 흐름으로 이어질 수밖에 없다.
클린 아키텍처는 관심사의 분리, 의존성 역전, 테스트 용이성을 극대화하기 위한 구조다. 이번 과정을 통해 다음과 같은 관점을 가지게 되었다:
TDD는 단순히 테스트를 위한 기술이 아닌, 이런 구조를 가능하게 만들도록 우리를 좋은 방향으로 유도해주는 길잡이 역할을 한다.
TDD는 결국, '테스트 코드'가 중요한 게 아니라 '검증할 수 있는 구조'를 만들도록 유도해주는 도구였다.