Controller에 로직이 몰려 있을 땐 테스트를 아예 안 하게 되거나, Mock을 과하게 쓰게 된다.

구조를 바꾼 뒤 테스트 작성은 더 쉬워졌고, 테스트 자체의 품질도 높아졌다.


🧪 기존 구조에서의 테스트 문제점

📌 기존 Controller 예시

@PostMapping("/orders")
public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
    // 검증, DB 접근, 외부 API 호출, 로직 포함
    ...
    return ResponseEntity.ok(...);
}

❌ 테스트 시 문제점

  • Controller 단에서 Mock 대상이 너무 많음 (Service, Repository, 외부 API 등)
  • 단일 책임 테스트가 어려움
  • 테스트 대상이 너무 넓고 느슨함
  • 실패 시 어디서 오류가 났는지 파악 어려움
  • 테스트 네이밍이 애매하고, 비즈니스 의도 표현이 어렵다

✅ 책임 분리 후 테스트 전략

구조 예시

Controller → UseCase → (DomainService, Component)
계층테스트 대상Mock 대상테스트 전략
Controller요청/응답 흐름UseCaseWebMvcTest + MockBean
UseCase로직 흐름, 트랜잭션Domain, 외부 API순수 단위 테스트
DomainService계산, 정책없음순수 단위 테스트
Component외부 API 통신실제 API 또는 Stub통합 테스트 (TestContainers 등)

테스트 계층 구조 요약


🎯 실제 코드 기반 테스트 예시

1️⃣ Controller 테스트 (요청-응답)

@WebMvcTest(OrderController.class)
class OrderControllerTest {
    ...
}

2️⃣ UseCase 테스트 (비즈니스 흐름)

class OrderCreateUseCaseTest {
    ...
}

🧱 도메인 로직 테스트 예시

3️⃣ DomainService 테스트 (정책 로직)

class PricingServiceTest {
    ...
}

📊 테스트 전략 요약

항목기존 구조분리 후 구조
테스트 난이도높음 (통합 모킹 필요)낮음 (단일 책임 테스트 가능)
실패 원인 파악어려움 (모듈 간 책임 혼재)쉬움 (계층별 명확)
유지보수테스트 코드도 자주 변경변경 영향도 낮음
테스트 커버리지낮음 (테스트 회피 경향)높음 (단위 테스트 용이)

⏱️ 테스트 효율 비교 예시

항목리팩토링 전리팩토링 후
평균 테스트 코드 라인 수100줄 이상30~50줄
Mock 객체 수5개 이상2개 내외
테스트 실행 시간평균 3~5초평균 1초 이하
실패 시 디버깅 시간10분 이상2~3분 내외

🧠 테스트 설계 팁

  • Controller는 WebMvcTest, 나머지는 JUnit + Mockito
  • 외부 API는 Stub 또는 TestContainer 활용
  • 복잡한 로직은 반드시 UseCase나 DomainService에 위임 후 단위 테스트
  • 테스트 네이밍: "주어진 조건에서, 어떤 결과가 나와야 하는가" 형태로 작성

🔍 마무리

Controller 책임을 분리했을 때, 테스트 전략도 함께 바뀌어야 효과가 극대화된다.
UseCase 중심의 단위 테스트는 빠르고 정확하며, 실패 원인 추적도 훨씬 명확하다.

실무에서 통하는 구조는 결국 테스트가 가능한 구조다.

오늘도 성장하는 개발자 되기! ㅍㅇㅌ!

0개의 댓글