[Test Code] Ch 02. Test Driven Development by.Kent Beck

Eunbi Lee·2026년 3월 15일

SeaVantage

목록 보기
16/16
post-thumbnail

서론; TDD 를 돌아보자.

ADD(Artificial Intelligence Driven Development; 내가 지은 단어) 를 진행하며, 오히려 기본기가 더 중요하다는 점을 실감하고 있는 요즘이다.

가장 최근에 읽었던 여러 아티클만 봐도, 좋은 코드란 무엇인가? 또 나는 좋은 코드의 기준을 잘 알고 있는지? 에 대한 고민을 끝없이 하고 있다.

  • AI 에게 제공할 .md (promting file) 와 자체 1차 코드 리뷰에서 내가 좋은 코드를 잘 알고 있어야, 충분히 좋은 생산물이 나올 수 있기 때문이다.

그러던 중, 백엔드 개발자에게 바이블로 취급되는 몇몇 책을 접하게 되었고 시간이 날 때마다 완독을 목표로 조금씩 읽을 예정이다.

  • 기간을 따로 정하지 않을 예정이다. 시간을 두고 찬찬히 읽고 싶을 때, 강박을 느끼지 않고 내용을 곱씹고 싶기 때문이다.

제목에서 언급했듯이 이번에 다룰 TDD도 조금씩 알고 있었던 내용이지만, 당연히 따라야 하는 내용이지! 라는 생각을 갖고 있었지만, 마음에 와닿지 않았던 과거가 있었다. 그리고 지금은 와닿을 순간이 왔다고 생각했기에 TDDRed-Green Refactor 에 대해 정리해보고자 한다.

TDD 의 철학: : 두려움을 관리하자.

테스트 코드를 통해 개발자가 느끼는 두려움을 관리하고, 결함이 적으면서도 설계가 깔끔한 코드를 작성하자.

  1. 오직 자동화된 테스트가 실패할 경우에만 새로운 코드를 작성한다.
  2. Refactor 를 통해 중복을 제거한다.

Red - Green - Refactor 사이클

개발의 리듬 구축하기

Red

  • 단계: 구현하려는 기능의 아주 작은 단위에 대한 테스트를 먼저 작성한다. 즉, 테스트를 통과시킬 가장 빠른 방법을 찾는다.
  • 목표: 실패하는 테스트를 만드는 것
    • 이때, 컴파일 오류도 실패 로 간주한다.
      • 아직 존재하지 않는 클래스나 메서드를 작성하여 호출하는 단계이기 때문이다.

Green

  • 단계: 테스트를 통과할 만큼의 최소한의 코드만 작성한다.
  • 목표: 일단 테스트가 성공하게 만드는 것이다.
    • 구현의 정도는 신경쓰지 않는다.
      • 상수를 하드코딩하거나, 코드가 충분히 지저분해도 된다.

Refactor

  • 단계: Green 단계에서 발생한 중복을 제거하고, 설계를 개선한다.
    • 테스트 코드는 건드리지 않은 채 내부 구조만 개선한다.
  • 목표: 테스트가 여전히 통과(Green)하는 상태를 유지하면서, 코드의 가독성을 높이고 결합도를 낮춘다.

실전 예제

아래 예제는 책에서 다뤄지는 금액 곱셈 예제를 발췌했다.

Step 1. Red(실패하는 테스트 작성)

이때, Dollar 클래스는 존재하지 않는다.

public void testMultiplication() {
   Dollar five = new Dollar(5);
   five.times(2);
   assertEquals(10, five.amount);
}

Step 2. Green (가장 빠르게 통과시키기)

이때, Kent Beck 은 테스트를 통과시키기 위한 최단 루트로 하드코딩을 권장한다.

class Dollar {
   int amount = 10; // 테스트를 통과시키기 위한 최소한의 하드코딩!
   Dollar(int amount) {}
   void times(int multiplier) {}
}

Step 3. Refactor (중복 제거 및 일반화)

실제 코드의 10과 테스트 코드의 (5 * 2) ` 사이의 중복을 발견하고, 이를 수식으로 바꾼다.

class Dollar {
   int amount;
   Dollar(int amount) {
      this.amount = amount;
   }
   void times(int multiplier) {
      amount *= multiplier; // 하드코딩을 실제 로직으로 교체
   }
}

왜 이렇게까지 테스트 하나를 통과시키기 위해 수정하는 코드의 양과 시간을 줄여야 할까?

줄인다 의 정의는 다음과 같다.

  1. 단위의 최소화: 단순히 "로그인 기능을 만든다"가 아니라, "잘못된 비밀번호를 입력했을 때 에러 메시지가 표시되는가?" 수준으로 쪼갠다.
  2. 시간의 최소화: Red에서 Green으로 가는 시간이 보통 1분 내외, 길어도 5분을 넘기지 않아야 한다.
  3. 복잡도의 최소화: 오직 현재의 테스트를 통과시키는 것 외에는 어떤 미래의 설계나 예외 처리도 고민하지 않아야 한다.

책 속에서 말하는 이유는 다음과 같다.

① 제어(Control): "심리적 확신과 문제 범위의 축소"
초보 개발자가 처음부터 실제 로직(amount * multiplier)을 짜다가 실수로 amount + multiplier라고 적었다고 가정했을 때, 개발자는 내가 짠 로직이 틀렸나?' 와 '내 테스트 코드가 잘못됐나?'라는 두 가지 변수를 동시에 고민해야한다.

② 설계의 명확성: "추상화를 향한 힌트 발견"
Kent Beck은 구현 코드의 10과 테스트 코드의 5 * 2가 중복(Duplication) 이라고 한다.

  • 상태: 구현에는 10이 있고, 테스트에는 5 * 2가 있다.
  • 발견: "아, 이 10은 사실 5와 2라는 데이터의 조합이구나!"라는 것을 깨닫게 된다.
  • 추상화: 이 중복을 제거하려고 노력하다 보면, 자연스럽게 amount * multiplier라는 일반적인 수식이 도출된다.

이를 삼각 측량(Triangulation) 과 연결시키면 다음과 같다.

만약 상수 10만 적고 넘어가면 어떻게 될까? 다른 값(예: $5 * 3)을 넣었을 때 테스트가 깨질 것이다.

이때, Kent Beck은 두 번째 테스트 케이스를 추가하라고 한다.

testMultiplication(5, 2) -> 구현에 10 적음 (Green)

testMultiplication(5, 3) -> Red! (이제 10으로는 부족함)

두 테스트를 모두 만족하는 일반적인 해인 amount * multiplier를 작성함 (Green)

결론: TDD 의 이점

동작하는 깔끔한 코드(Clean code that works)를 얻을 수 있다.

  1. 코드의 품질: 리팩터링 단계를 거치며 코드는 점점 간결해진다.
  2. 살아있는 문서: 테스트 코드는 그 시스템이 어떻게 동작해야 하는지 알려주는 가장 정확한 명세서가 된다.

후기

지금을 기점으로 테스트를 먼저 작성해보려는 시도를 해볼 것이다. 그리고 시간에 쫓겨서 테스트 코드를 작성하지 않았던 과거처럼 느릴 것 같다는 생각이 들었다.

하지만, AI 와 함께 개발하는 지금 AI 가 우후죽순으로 쏟아내는 코드를 더 좋은 품질로 도출시키기 위해선, 더 좋은 설계서가 필요하고 이를 위해 개발자인 내가 무엇을 원하는지 또는 무엇을 구현하려고 하는지 를 명확하게 인지하고 전달할 수 있어야 한다고 생각한다. 🤔

참고 링크

profile
안녕하세요, 개발자 비비입니다.

0개의 댓글