Software Design

TDD 테스트 주도 개발, 레드-그린-리팩터 사이클의 실체 (실무 후기)

"테스트를 먼저 짜라니, 아직 코드도 없는데 뭘 테스트해요?"

이석우iOS Developer4분 읽기
TDD 테스트 주도 개발, 레드-그린-리팩터 사이클의 실체 (실무 후기) 대표 이미지

“테스트를 먼저 짜라니, 아직 코드도 없는데 뭘 테스트해요?”

TDD(Test-Driven Development, 테스트 주도 개발)를 처음 접하면 누구나 드는 생각이에요. 순서가 거꾸로인 것 같고, 괜히 일만 두 배로 늘어나는 기분이 들거든요.

그런데 몇 달 붙잡고 실무에 써보면 생각이 좀 달라집니다.

결론부터 말씀드리면, TDD는 “실패하는 테스트(Red) → 통과시키는 최소 코드(Green) → 정리(Refactor)“를 아주 짧게 반복하는 개발 방식이에요. 핵심은 이 사이클을 몇 초에서 몇 분 단위로 빠르게 돈다는 거예요.

오늘은 이 레드-그린-리팩터 사이클이 실제로 어떻게 굴러가는지 차근차근 풀어볼게요.

TDD 레드-그린-리팩터, 결국 이 세 단계 반복이 전부예요
TDD 레드-그린-리팩터, 결국 이 세 단계 반복이 전부예요

TDD 레드-그린-리팩터, 3단계 핵심 요약 ✍️

먼저 사이클 전체를 한눈에 정리해드릴게요.

  1. Red(레드): 아직 없는 기능에 대한 테스트를 먼저 작성한다. 당연히 실패한다.
  2. Green(그린): 그 테스트를 통과시킬 최소한의 코드만 짠다. 예쁠 필요 없다.
  3. Refactor(리팩터): 테스트가 통과하는 상태를 유지하며 코드를 정리한다.

이 세 단계를 한 덩어리로 묶어 계속 반복하는 게 전부예요.

이 세 칸을 뱅뱅 도는 게 TDD의 전부예요
이 세 칸을 뱅뱅 도는 게 TDD의 전부예요

중요한 건 각 단계를 크게 잡지 않는 겁니다. “로그인 기능 전체”가 아니라 “이메일이 비어 있으면 에러를 반환한다” 정도로 잘게 쪼개요.

한 사이클이 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가 어렵게 느껴진다면, 거창하게 시작하지 마시고 함수 하나부터 레드-그린-리팩터를 돌려보세요.

작은 성공 사이클이 몸에 붙는 순간, 왜 사람들이 이 방식을 놓지 못하는지 조금은 이해가 되실 거예요. 오늘 딱 하나의 테스트부터 빨간불을 켜보시길 응원합니다.

함께 보면 좋은 글