“테스트를 먼저 짜라니, 아직 코드도 없는데 뭘 테스트해요?”
TDD(Test-Driven Development, 테스트 주도 개발)를 처음 접하면 누구나 드는 생각이에요. 순서가 거꾸로인 것 같고, 괜히 일만 두 배로 늘어나는 기분이 들거든요.
그런데 몇 달 붙잡고 실무에 써보면 생각이 좀 달라집니다.
결론부터 말씀드리면, TDD는 “실패하는 테스트(Red) → 통과시키는 최소 코드(Green) → 정리(Refactor)“를 아주 짧게 반복하는 개발 방식이에요. 핵심은 이 사이클을 몇 초에서 몇 분 단위로 빠르게 돈다는 거예요.
오늘은 이 레드-그린-리팩터 사이클이 실제로 어떻게 굴러가는지 차근차근 풀어볼게요.
TDD 레드-그린-리팩터, 3단계 핵심 요약 ✍️
먼저 사이클 전체를 한눈에 정리해드릴게요.
- Red(레드): 아직 없는 기능에 대한 테스트를 먼저 작성한다. 당연히 실패한다.
- Green(그린): 그 테스트를 통과시킬 최소한의 코드만 짠다. 예쁠 필요 없다.
- Refactor(리팩터): 테스트가 통과하는 상태를 유지하며 코드를 정리한다.
이 세 단계를 한 덩어리로 묶어 계속 반복하는 게 전부예요.
중요한 건 각 단계를 크게 잡지 않는 겁니다. “로그인 기능 전체”가 아니라 “이메일이 비어 있으면 에러를 반환한다” 정도로 잘게 쪼개요.
한 사이클이 30분씩 걸린다면, 그건 TDD가 아니라 그냥 테스트를 곁들인 개발일 확률이 높습니다.
Red 단계, 왜 일부러 실패하는 코드를 짜나요?
처음 TDD를 배울 때 가장 이해가 안 가는 부분이에요. 실패할 걸 뻔히 아는데 왜 실행하냐는 거죠.
이유는 단순합니다. 그 테스트가 “제대로 실패하는지” 확인하려고요.
테스트를 짜자마자 통과해버리면, 사실 그 테스트는 아무것도 검증하지 못하는 껍데기일 수 있어요. 실패를 먼저 보는 건 테스트 자체를 테스트하는 과정인 셈이죠.
간단한 예로, 두 수를 더하는 함수를 만든다고 해볼게요. 코드보다 테스트가 먼저 나옵니다.
// add 함수는 아직 존재하지 않는다. 그래서 이 테스트는 실패한다(Red)
struct AddTests {
@Test func 덧셈_1더하기2는3이다() {
#expect(add(1, 2) == 3)
}
}
이 상태로 돌리면 “cannot find ‘add’ in scope” 같은 에러가 뜹니다. 저는 이 빨간불을 보는 게 오히려 마음이 편해지더라고요. 목표가 명확해지거든요.
지금부터 제 할 일은 딱 하나, 저 빨간불을 초록불로 바꾸는 것뿐입니다.
Green과 Refactor, 여기서 진짜 실력이 갈립니다
Green 단계의 원칙은 “부끄러울 만큼 단순하게”예요.
앞의 테스트를 통과시키는 가장 빠른 코드는 이겁니다.
// 테스트를 통과시키는 최소한의 코드(Green)
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
너무 당연해 보이죠. 그런데 초보일수록 여기서 미래를 걱정하며 예외 처리, 로깅, 설정값까지 미리 넣으려고 해요.
TDD는 그걸 일부러 참게 만듭니다. 지금 테스트가 요구하지 않는 코드는 짜지 않는 게 규칙이에요.
그리고 초록불이 켜진 뒤에야 Refactor로 넘어갑니다. 이때 변수명을 다듬거나 중복을 걷어내죠.
핵심은 리팩터 중에는 새 기능을 절대 추가하지 않는다는 거예요. 오직 테스트가 통과하는 상태를 지키면서 구조만 손봅니다.
혹시 정리하다 빨간불이 뜨면? 방금 건드린 걸 되돌리면 그만이에요. 조금 전까지 멀쩡했다는 걸 테스트가 보장해주니까요.
이 안전망 덕분에 리팩터링이 훨씬 과감해졌습니다.
TDD 하면 개발이 느려지지 않나요? 🤔
가장 많이 받는 질문이에요. 실제로 써보니 답은 “때에 따라 다르다”였습니다.
제가 느낀 차이를 표로 정리해봤어요.
| 구분 | TDD 적용 | 테스트 나중에 작성 |
|---|---|---|
| 초반 속도 | 다소 느림 | 빠름 |
| 버그 발견 시점 | 작성 즉시 | 한참 뒤 |
| 리팩터링 부담 | 낮음 | 높음 |
| 복잡한 로직 | 유리 | 불리 |
단순한 화면 하나 뚝딱 만들 땐 솔직히 TDD가 거추장스러울 때도 있어요.
반대로 결제, 정산, 할인 계산처럼 조건이 얽힌 로직에선 확실히 빛을 봤습니다. 경우의 수를 테스트로 하나씩 못 박아두니, 나중에 코드를 뜯어고쳐도 두렵지 않더라고요.
결국 TDD는 만능이 아니라 “복잡함을 다스리는 도구”에 가까웠어요.
마무리하며
TDD가 어렵게 느껴진다면, 거창하게 시작하지 마시고 함수 하나부터 레드-그린-리팩터를 돌려보세요.
작은 성공 사이클이 몸에 붙는 순간, 왜 사람들이 이 방식을 놓지 못하는지 조금은 이해가 되실 거예요. 오늘 딱 하나의 테스트부터 빨간불을 켜보시길 응원합니다.
