
이번 포스트는 프로젝트 방법론 중 하나인 테스트 주도 개발(TDD) 의 개념과 중요성, 사용해본 후 느낀 점을 정리한 글입니다.
-TDD(Test Driven Development) 개념
-TDD를 처음 접했을 때 내 반응
-TDD를 알기 전에는 어떻게 개발했을까?
-TDD의 개발 방법론 (RGB) 를 적용해보자
-[1] Red : 일반 실패하는 코드를 작성하라
-[2] Green : 실패했던 테스트가 통과되도록 코드를 수정하라
-[3] Refactor: 테스트가 통과하면 코드를 깔끔하게 개선하라
-TDD를 적용하고 난 후 느낀 장점
-TDD의 아쉬웠던 점
-정리
TDD는 테스트 주도 방법론으로 "테스트를 먼저 작성하고 이를 통과하는 코드를 구현하는 방식" 을 의미합니다. 예로 김치를 만드는 과정을 떠올려봅시다.
(1) 김치에 양념을 아주 조금 바른다. (초기 작성)
(2) 맛을 본다. 음 너무 싱거운데? (테스트)
(3) 김치에 양념을 추가한다. (개발)
(4) 맛을 본다. 앗 너무 짜 (테스트)
(5) 배추를 더 추가한다. (개발)
(6) 맛을 본다. (테스트) ... 반복 ...
(7) 김치 완성!
이처럼 중간중간 맛을 확인해가며 김치를 만드는 과정을 TDD 방식을 적용했다고 생각하시면 됩니다.
이를 프로그래밍에 접목해본다면 테스트 케이스를 먼저 작성하고(맛을 보고) 개발을 진행하는(양념 추가 혹은 배추 추가) 과정을 반복해 프로그램을 완성시킨다고 볼 수 있습니다.
이게 뭐시여 ;;;
처음 TDD를 보고 들었던 생각은 "이해가 안된다" 였습니다.
왜냐하면 일반적으로 무언가를 먼저 만들고 나중에 테스트를 한다는 통념을 가지고 있었기 때문입니다. 게다가 정보처리기사 공부를 하며 배웠던 폭포수 모델이 이 통념을 더욱 굳히는 데 한몫했습니다.
폭포수 모델 : 전통적인 소프트웨어 개발 방법론으로 요구사항 분석 -> 설계 -> 구현 -> 테스트 -> 배포 .. 순서로 이전 단계를 완료해야 다음 단계로 넘어가는 방법론을 의미한다.
TDD를 알기 전에는 개발을 다 하고나서 테스트를 진행했습니다.
코드로 예시를 보겠습니다.
[요구사항]
입력값이 20 이상이면 "20이상 입니다" 를 출력하고
입력값이 20 미만이면 "20미만 입니다" 를 출력하라.
만약 숫자가 100을 초과하면 "숫자는 0~100만 가능합니다" 를 출력하라
Scanner sc = new Scanner(System.in); //키보드로 사용자의 입력값을 받는다.
int number = sc.nextInt(); //키보드로 정수 한 개를 입력받는다.
if(number > 100) {
System.out.println("숫자는 0~100만 가능합니다.");
} else if(number >= 20) {
System.out.println("20이상 입니다") ;
} else if(number < 20) {
System.out.println("20미만 입니다") ;
}
개발을 완료했으니 테스트를 진행합니다.
테스트할 때 21, 19를 입력했을 때 "20이상 입니다" 와 "20미만 입니다"가
잘 출력되었으며 101 을 입력하면 "숫자는 0~100만 가능합니다" 가
출력되었습니다. 따라서 이 코드는 완벽한 코드입니다. (정말?)
이것봐. 개발해놓고 테스트해도 충분하잖아. 뭐하러 반대로 해?
하지만 이 코드는 큰 문제점이 있습니다.
어떤 문제가 있는지 TDD 방식을 적용해가며 알아보겠습니다.
TDD는 Red -> Green -> Refactor 의 과정을 가지고 있습니다.
Red : 실패하는 테스트를 먼저 작성한다. (무조건 실패하도록)
Green : 실패했던 테스트가 통과되도록 코드를 수정한다.
Refactor: 테스트가 통과하면 코드를 깔끔하게 개선한다.
이를 토대로 위 로직의 문제점을 알아보겠습니다.
아래 코드에 number 값으로 3,000,000,000(30억)을 입력해 실행해보겠습니다.
Scanner sc = new Scanner(System.in); //키보드로 사용자의 입력값을 받는다.
int number = sc.nextInt(); //키보드로 정수 한 개를 입력받는다.
if(number > 100) {
System.out.println("숫자는 0~100만 가능합니다.");
} else if(number >= 20) {
System.out.println("20이상 입니다") ;
} else if(number < 20) {
System.out.println("20미만 입니다") ;
}

분명 "숫자는 0~100만 가능합니다" 가 출력되어야 하는데 이상한 오류가 발생했습니다.
즉, 테스트에 실패했습니다.
이 코드의 문제점은 입력값의 범위를 검증하지 않았다는 점입니다.
(다른 문제는 없다고 가정)
입력값 범위 검증을 해결하기 위해 try - catch 방식으로 오류를 잡아 처리하도록 합니다.
Scanner sc = new Scanner(System.in); //키보드로 사용자의 입력값을 받는다.
try {
int number = sc.nextInt(); //키보드로 정수 한 개를 입력받는다.
if (number > 100) {
System.out.println("숫자는 0~100만 가능합니다.");
} else if (number >= 20) {
System.out.println("20이상 입니다");
} else if (number < 20) {
System.out.println("20미만 입니다");
}
} catch (InputMismatchException e) {
System.out.println("숫자는 0~100만 가능합니다.");
}
이제 30억을 입력해도 "숫자는 0~100만 가능합니다" 가 출력되는 것을 확인할 수 있습니다.

테스트가 통과되었으니 이제 코드를 정리하고 개선해봅니다.
try-with-resources 구문을 활용해 Scanner 스트림이 정상적으로 닫히도록 했고 number > 100 초과 혹은 이상한 값이 들어오면 InputMismatchException 예외를 생성해 던지도록 개선했습니다.
try(Scanner sc = new Scanner(System.in)) {
int number = sc.nextInt();
if(number > 100) {
throw new InputMismatchException();
}
if (number >= 20) {
System.out.println("20이상 입니다");
} else {
System.out.println("20미만 입니다");
}
} catch(InputMismatchException e) {
System.out.println("숫자로 0~100까지만 입력해주세요 ");
}

TDD를 알고 막상 사용을 해보니 확실한 장점을 느낄 수 있었습니다.
[1] 처음부터 오류 케이스를 발생시키고 해결함으로써 안정성을 강화했습니다.
개발을 처음 시작했을 땐, 프로그램을 작성하면 끝이라는 안일한 생각을 했고 예기치 못한 오류가 발생되면 부랴부랴 고치며 야근을 했던 기억이 있습니다.
하지만 TDD를 적용함으로써 오류를 하나씩 잡아가며 개발을 하게 되니
"최소한 이 정도 안전은 보장되는 코드다!" 라는 확신을 가지며 일할 수 있게 되었습니다.
[2] 외부 증빙자료로 줄 수 있습니다
Spring으로 개발을 할 때, build를 해야 실행가능한 프로그램이 생성되는데,
테스트케이스가 모두 통과되어야 build가 됩니다.
즉, build가 되었다는 의미는 모든 테스트를 통과했다는 의미가 되고, 그만큼 코드가 안정적이라는 점을 외부에 증명할 수 있었습니다.
[3] 전체적으로 보았을 때 개발 속도가 향상 됩니다
TDD로 개발을 하면 여러 오류를 고민하고 고쳐가기에 시간이 예전보다 오래걸렸습니다.
하지만 미래에 예기치않은 오류로 인해 밤샘 작업을 해야 하는 상황을 고려한다면, TDD를 이용한 개발방법이 훨씬 빠른 개발 속도를 내줄 수 있었습니다 (안정성은 덤!)
아무래도 개발 속도 자체는 아쉬웠습니다.
개발 단계에서 테스트를 반복하며 오류를 잡아가기 때문에 구현에 시간이 오래걸렸습니다. 또한 개발 도중에 "아, 이 오류도 처리해야겠네"라는 생각이 들면 테스트 코드를 다시 작성해야 하는 문제도 있었습니다.
그래서 TDD를 실무에서 아예 처음부터 적용하는 것은 무리가 있어 보였습니다.
개인적으로 TDD 적용은 어느 정도 구현이 완료된 후, 테스트가 필요한 부분이나 예상되는 오류 위주로 적용하는 방식이 현실적인 타협점이라고 느꼈습니다.
빠른 개발을 해내는 개발자도 중요하지만, 안전하게 개발하는 개발자가 더 중요하다고 생각합니다. TDD를 적용함으로써 더 안정적인 개발을 할 수 있었고, 결과적으로 생산성도 높일 수 있는 좋은 개발 방법론이라고 생각했습니다. 하지만 실무를 고려했을 때 아예 처음부터 TDD를 적용하기 보다는 어느 정도 개발이 진행된 후에 적용해보는 것이 현실적인 접근일 수 있습니다.