#1 테스트 코드의 필요성을 절실히 느낀 이유...

2025. 11. 23. 16:52토이프로젝트/테스트

테스트의 시작

 

개발자인 경우, 누구나 자신이 만든 기능이 정상 수행하는지 확인하는 절차를 가져야한다.
처음에는 만들면서 케이스들을 고려하여 구현하게되고, 마무리 단계에서 몇가지의 테스트들을 통해 직접 검증하는 방식으로 확인하였다.

하지만, 그런 검증 방식에서 당연히 되야한다고 생각했던것들에서 항상 1~2개씩 놓치게되어 다시 재배포하는 경우가 발생하게 되었다.
처음에는 QA단계에서 발생하여 다행이다라고 생각을 하게되었지만, 몇번 이런 일들이 생기게되니 점차 스스로도 신뢰하지 못하는 단계에 이르게 되었다.

그래서 테스트의 필요성을 깨닫고, 테스트를 시작하며 직면한 문제(?)에 대해서 기록해보고자 한다.

 


# 테스트를 해야겠다고 느낀 이유

 

1. 완성했다고 생각한 기능에서 계속되는 문제 발생

 

기능을 구현하고 "이제 동작하겠지"라고 생각했는데 실제로 테스트해보면 자꾸 예상치 못한 케이스에서 오류가 발생
public int getLength(String input){
    return input.length();
}

" 위의 코드에서 고려해야하는 것들이 어떤게 있을까? "

  • 빈 문자열 처리 ("")
  • 공백 처리 (" ")
  • null 입력
  • 특수문자 처리
  • 특정 인코딩 문자 길이
위의 예외 케이스 중 한개라도 방어 로직이 들어가지 않는다면, 해당 문제를 직면했을때 곧바로 예외처리 혹은 오류로 인해서 원활한 서비스를 하지 못하게 될것이다.

 

 

2. 리펙토링을 하고 싶어도, 어떤일이 발생할지(?) 짐작이 안가서 도전을 못함

“리팩토링 후 코드가 정상 동작하는지 확인할 방법이 ‘직접 검증’밖에 없었다.”

 

기능이 하나일 때는 직접 테스트하는 게 어렵지 않았지만,
기능이 복잡해지고 외부 서비스와 얽히기 시작하면 직접 검증은 사실상 불가능해진다.

  • API 호출 직접 테스트
  • 파일 업로드 직접 테스트
  • 데이터베이스 상태 직접 확인
  • 특정 조건 재현을 위한 환경 세팅

이 모든 것을 수동으로 반복하는 것은 개발 효율을 크게 떨어뜨렸다.
결국 리팩토링을 하고 싶어도, **“두려워서 할 수 없는 코드”**가 되어버렸다.

이때부터 자동으로 검증해주는 테스트가 필요하다는 것을 진짜로 깨닫게 되었다.

 

 


 

# 테스트를 도입했지만, 잘못된 방식으로 사용하고 있었다

테스트의 필요성을 크게 느낀 후, 나는 곧바로 테스트 작성에 들어갔다.
문제는, 그 방식이 TDD에서 말하는 테스트 기반 개발 흐름과 전혀 달랐다는 점이다.

 

❌ 테스트 방식은 이런 흐름이었다

  1. 테스트 폴더로 이동한다. (src/test/java)
  2. 테스트 케이스를 작성한다.
  3. 테스트 안에서 직접 메서드를 만들어본다.
  4. 테스트 안에서 만든 기능이 어느 정도 작동하는 것 같으면,
    프로덕션 코드로 옮기는 것이 아니라…
  5. main 폴더에서 기능을 처음부터 다시 만든다. (테스트 케이스의 내용들을 참고하며...)
    즉, 테스트는 테스트대로 있고, 프로덕션 코드는 다시 처음부터 작성하는 구조가 되어버렸다.

 


# 왜 이 방식이 비효율적이었나?

1) 테스트 코드와 실제 코드가 완전히 따로 놀았다

테스트 안에서 프로토타입처럼 코드를 작성했지만,
그 코드를 그대로 프로덕션으로 승격하지 않고 별도로 다시 구현했다.

 

이 때문에

  • 로직이 중복되고
  • 테스트와 프로덕션이 서로 다른 방향으로 진행되고
  • 버그가 발생해도 양쪽을 모두 봐야 했고
  • 생산성은 눈에 띄게 낮아졌다

# 테스트는 검증 도구가 아니라 참고용 코드가 되어버렸다

테스트의 근본적인 의미는 “동작을 보장하는 자동화된 안전망”이다.
하지만 나는 테스트 코드를 단순히 참고용 예제처럼 사용했기 때문에 테스트의 장점을 전혀 살리지 못했다.

결국

  • 테스트는 작성하는 데만 시간이 들고
  • 실제 코드 품질 개선에는 기여하지 못하며
  • 리팩토링의 안전망 역할도 하지 못했다

즉, 테스트의 목적 자체를 잘못 사용하고 있었던 것이다.

 


# 지금 돌아보면 TDD와는 정반대 흐름이었다

1) TDD는 다음 흐름을 따른다.

  1. 실패하는 테스트 작성 (RED)
  2. 테스트를 통과시키기 위한 최소한의 코드 작성 (GREEN)
  3. 구조 개선 (REFACTOR)

2) 하지만 나는 이렇게 했다.

  1. 테스트에서 기능을 만들어봄 (RED/GREEN 뒤섞임)
  2. 프로덕션 코드에서 기능을 다시 구현
  3. 테스트는 참고용으로만 사용
  4. 설계 개선은 없음

즉, ‘테스트를 쓰긴 했지만’
테스트가 개발을 이끄는 구조가 아니라 개발을 방해하는 구조가 되어버린 셈이다.

 


# 하지만 이 시행착오가 다음 단계의 시작이 되었다

이 비효율적인 경험 덕분에 나는 한 가지 중요한 사실을 깨달았다.

“테스트의 목적은 기능 확인이 아니라, 설계를 더 좋게 만드는 것”

 

이 깨달음 이후,

1. 나는 테스트를 기반으로 책임을 분리하고,
2. 기능을 테스트에서 검증한 후 프로덕션 코드로 자연스럽게 승격시키는 구조를 만들기 시작했다.

 

이 과정은 2편에서 자세히 다뤄보려고 한다.

 


👉 2편 예고

  • 테스트에서 작은 기능을 먼저 만들고
  • 그 기능을 main 코드로 승격시키는 진짜 TDD 흐름
  • 책임 분리, 클래스 분리, DI 구성
  • @SpringBootTest 기반 테스트 개선
  • Given–When–Then만 남는 깔끔한 구조
  • 테스트가 리팩토링을 가능하게 만든 과정

다음 글에서 이어서 설명하겠다.