이 책의 첫 번째 학습 과정은 테스트와 리팩토링이다.
처음엔 “테스트”라고 해봤자 별거 없었다.
그냥 main()에서 코드를 호출해보고 System.out.println() 찍어서 결과가 맞는지 눈으로 확인하는 방식.
책에서도 계산기 구현 예제로 이 흐름을 시작한다.
실제 서비스 로직(프로덕션 코드)과, 그 로직이 정상 동작하는지 확인하려고 임시로 붙이는 main() 코드가 한 클래스에 섞이면서 문제가 시작된다.
- Calculator 클래스에 add/subtract/multiply/divide 구현
- main()에서 객체 만들고
- println() 결과 보고 “오케이” 하고 넘어감
근데 이 방식은 코드가 조금만 커져도 바로 한계가 온다.
책에서도 이걸 “main()으로 테스트하는 방법의 문제점”으로 단계적으로 보여준다.
테스트 코드가 프로덕션 코드에 섞이기 시작한다
처음엔 편해서 Calculator 클래스 안에 main()을 같이 둔다.
- 서비스 로직(연산 메서드)도 같은 클래스
- 검증용 main()도 같은 클래스
문제는 시간이 지나면 main()이 점점 “테스트 코드”라기보단 잡동사니가 된다는 거다.
프로덕션 코드 안에 검증 코드가 섞이면, 읽는 사람 입장에서도 흐름이 끊긴다.
그래서 자연스럽게 첫 개선은 이거다.
- 프로덕션 코드(Calculator)와 테스트 코드(CalculatorTest)를 분리
public class CalculatorTest {
public static void main(String[] args) {
Calculator cal = new Calculator();
System.out.println(cal.add(9, 3));
System.out.println(cal.subtract(9, 3));
System.out.println(cal.multiply(9, 3));
System.out.println(cal.divide(9, 3));
}
}
분리 자체는 나쁘지 않다.
근데 여기서 끝이 아니고 더 큰 문제가 남아있다.
main() 하나로 결국 “모든 메서드”를 테스트하게 된다
개발할 때 흐름은 보통 이렇다.
add() 먼저 구현 → add()만 집중해서 확인 → 통과하면 subtract()로 넘어감
근데 main() 기반 검증은 구조상 점점 이렇게 된다.
- 지금은 add만 보고 싶은데
- subtract/multiply/divide도 같이 실행됨
그래서 결국 하게 되는 짓:
- 다른 메서드 호출은 주석 처리했다가
- 다시 풀었다가
- 또 주석 처리했다가…
테스트하려고 만든 코드가 오히려 개발 흐름을 방해하기 시작한다.
이걸 조금이라도 개선하려고 테스트 코드를 메서드 단위로 쪼개는 방식으로 해보았다.
main() 테스트를 리팩토링해도 ‘본질’은 해결이 안 된다
main()에서 바로 출력하지 말고 메서드로 분리하면 이런 형태가 된다.
public class CalculatorTest {
public static void main(String[] args) {
Calculator cal = new Calculator();
add(cal);
subtract(cal);
multiply(cal);
divide(cal);
}
private static void add(Calculator cal) {
System.out.println(cal.add(9, 3));
}
private static void subtract(Calculator cal) {
System.out.println(cal.subtract(9, 3));
}
private static void multiply(Calculator cal) {
System.out.println(cal.multiply(9, 3));
}
private static void divide(Calculator cal) {
System.out.println(cal.divide(9, 3));
}
}
이렇게 하면 “지금 보고 싶은 것만 실행”하기는 좀 더 편해진다.
하지만 이건 결국 임시방편이고, 핵심 문제는 그대로 남아있다.
진짜 문제: 테스트 결과를 사람이 매번 “눈으로” 확인해야 한다
main() 기반 검증은 결국 이 방식이다.
- 결과를 출력한다
- 사람이 보고 판단한다
로직이 단순하면 괜찮다.
근데 조금만 복잡해지면 바로 지옥이 된다.
- 콘솔에 나온 값이 맞는지 머리로 다시 계산해야 함
- 테스트 케이스가 늘어나면 출력도 늘어나고, 확인 시간도 늘어남
- 무엇보다 “실패했는지”를 프로그램이 알려주지 않음
그리고 제일 치명적인 건 이거다.
회귀 테스트가 안 된다.
어떤 기능을 고쳤을 때
- 기존 기능이 깨졌는지
- 새로운 요구사항이 빠졌는지
이걸 자동으로 잡아주는 장치가 없다.
결국 시간이 지날수록 테스트를 덜 하게 되고, “감”으로 개발하게 된다.
그래서 필요한 건 println이 아니라 “assert”다
결론은 자연스럽게 이렇게 내려온다.
- 출력으로 확인하는 건 테스트라기보다 “실행 예제”에 가깝다
- 진짜 테스트는
- 기대값을 코드로 고정하고
- 맞으면 통과 / 틀리면 실패를 알려주고
- 여러 케이스를 자동으로 돌릴 수 있어야 한다
그래서 main() 검증에서 JUnit 같은 테스트 프레임워크로 넘어가게 된다.
JUnit을 쓰면 이제부터는 결과를 눈으로 확인하는 게 아니라, 테스트가 스스로 통과/실패를 판정한다. 그리고 이걸 한 번 경험해보려고, 전에 구현해둔 문자열 계산기를 가져와서 요구사항을 테스트로 먼저 박아두고 → 구현으로 통과시키는 흐름(TDD)을 처음으로 따라가보겠다.
출처 : 《자바 웹 프로그래밍 Next Step》, 박재성, 로드북
'Book > 자바 웹 프로그래밍 Next Step' 카테고리의 다른 글
| 3장_ 원격 서버 배포 전 준비 : UTF-8 세팅하기 (0) | 2026.01.28 |
|---|---|
| 3장_ 실습 환경 구축 (0) | 2026.01.28 |
| 2장_ 문자열 계산기 추가 요구사항 : 중복 제거, 읽기 좋은 코드를 구현하기 위한 리팩토링 (0) | 2026.01.27 |
| 2장_ 문자열 계산기 리팩토링: JUnit으로 요구사항 검증하면서 구현해보기 (0) | 2026.01.27 |
| 왜 이 책으로 시작했나 (0) | 2026.01.27 |
