빨강을 초록으로 바꾸는 방법은 하나가 아니다. 이 장은 세 가지 전략과 그 선택 기준을 다룬다. 핵심 통찰은 1장에서 이미 봤다 — 초록을 먼저 만들면 그 다음 모든 걸음을 테스트가 지켜 준다. 문제는 “얼마나 정직하게” 초록을 만드느냐다.

flowchart TD
    START["빨강 — 통과시켜야 할 테스트"] --> Q{구현이 명백한가?}
    Q -->|"자신 있다"| OBV["명백한 구현<br/>바로 진짜를 짠다"]
    Q -->|"막연하다"| FAKE["가짜로 구현하기<br/>상수를 박아 초록"]
    FAKE --> Q2{일반화가 보이는가?}
    Q2 -->|"아직 아니다"| TRI["삼각측량<br/>두 번째 예를 추가"]
    Q2 -->|"이제 보인다"| GEN["상수를 변수로<br/>일반화"]
    TRI --> GEN
    OBV --> DONE["초록 → 리팩터링"]
    GEN --> DONE

상수를 박고 시작한다

가짜로 구현하기. 실패하는 테스트를 통과시키는 가장 빠른 방법은 기대값을 상수로 그대로 반환하는 것이다. return 10;. 부끄럽지만 초록이다. 초록이 된 뒤엔 그 상수와 테스트 사이의 중복을 제거하며 진짜 구현으로 나아간다. 1장amount = 10이 정확히 이것이다.

이게 왜 정당한가. 두 가지 효과가 있다. 첫째, 심리적 — 초록 막대와 빨간 막대의 차이는 크다. 초록 위에 서면 자신 있게 리팩터링할 수 있다. 둘째, 범위 통제 — 지금 이 테스트 하나에만 집중하게 되어 상상 속 일반 사례에 휘둘리지 않는다.

Money times(int multiplier) {
    return new Money(10, currency);  // 가짜 — 상수를 박았다
}

판단 기준: 진짜 구현이 어떻게 생길지 감이 안 오면 일단 가짜로 초록을 만든다. 함정: 가짜 상태로 커밋하고 잊으면 그 상수가 운영에 새어 나간다 — 가짜는 반드시 같은 세션 안에서 중복 제거로 지운다.

예를 늘려 일반화를 끌어낸다

삼각측량. 어떻게 일반화할지 확신이 없을 때, 두 개 이상의 예로 코드를 몰아붙여 일반화를 강제한다. 2×2=4만 있으면 return 4로 속일 수 있지만, 3×3=9를 추가하면 상수로는 둘 다 만족시킬 수 없어 a×a라는 진짜 구현이 드러난다. 두 점이 있어야 직선을 그을 수 있는 것과 같은 원리다. 3장에서 동치성(equals)을 일반화할 때 쓴 그 기법이다.

# 하나면 return 4로 속일 수 있다 → 두 번째 예가 일반화를 강제한다
assert square(2) == 4
assert square(3) == 9   # 이제 상수로는 못 버틴다 → return n * n

판단 기준: 일반화의 방향이 안 보일 때만 삼각측량을 꺼낸다 — 방향이 보이면 낭비다. 함정: 이미 구현이 명백한데 형식적으로 예를 두 개씩 만들면 테스트만 늘고 진도는 안 나간다. 삼각측량은 목발이지 목적이 아니다.

확신이 서면 바로 짠다

명백한 구현. 무엇을 어떻게 짜야 할지 이미 안다면, 가짜니 삼각측량이니 돌아가지 말고 그냥 진짜 구현을 바로 쓴다. TDD는 느리게 가라는 규율이 아니라, 필요할 때 속도를 낮출 수 있는 기어다. 자신 있으면 명백한 구현으로 빠르게 가다가, 빨강이 예상 밖으로 뜨면 가짜 구현·삼각측량으로 기어를 낮춘다.

판단 기준: “이건 5초면 짠다”는 확신이 서면 명백한 구현이다. 함정: 명백한 줄 알았는데 빨강이 뜨면 자만의 신호다 — 즉시 가짜 구현으로 기어를 내려 걸음을 잘게 쪼갠다. 확신과 자만은 종이 한 장 차이다.

하나를 먼저, 여럿은 나중에

하나에서 여럿으로. 컬렉션을 다루는 오퍼레이션을 만들 때, 곧장 리스트를 상대하지 말고 원소 하나에 대해 먼저 동작하게 만든다. 하나가 초록이 되면, 그 다음에 인자를 컬렉션으로 바꾸고 내부에서 하나짜리 로직을 반복하도록 일반화한다. 어려운 문제(집계)를 쉬운 문제(단일 원소)로 먼저 환원하는 것이다.

# 1) 하나에 대해 먼저
def test_sum_one():
    assert total([5]) == 5
# 2) 통과 후 여럿으로 일반화
def test_sum_many():
    assert total([5, 3, 2]) == 10

판단 기준: 집계·반복 로직은 거의 항상 “원소 하나”에서 출발하는 게 빠르다. 함정: 처음부터 여러 원소와 빈 컬렉션·경계를 동시에 잡으려 들면 걸음이 커져 빨강의 원인이 뒤엉킨다.

다음장으로 29장