Weekly Paper 10 애플리케이션 계층별 검증 책임 분리 및 테스트 더블
1. 애플리케이션 계층별 입력값 검증과 트레이드오프
계층별 검증 범위와 책임
- Controller (Presentation Layer): 형태와 기본 규칙 검증
- DB 접근 없이 판단 가능한 1차원적 데이터 형식 검사 (Fast Fail).
- 예: 부서명
@NotBlank, 예산@Min(0)처리.
- Service (Business Layer): 비즈니스 상태 및 논리 검증
- 시스템 정책 위배 여부 및 DB 데이터를 활용한 상태 검사.
- 예:
departmentRepository.existsByName()을 활용한 부서명 중복 검증, 권한 확인.
- DB (Data Access Layer): 구조적 무결성 검증
- 외부 시스템 직접 접근 등 애플리케이션 계층이 우회되었을 때를 대비한 최후 방어선.
- 예: 부서 테이블 이름 컬럼에
UNIQUE제약조건 설정.
트레이드오프 및 중복 검증 방안
- 트레이드오프: Controller에서 처리할 단순 형식 검사를 Service로 넘기면 불필요한 리소스 낭비가 발생하고, 반대로 Controller에 DB 조회 로직이 포함되면 계층 간 결합도가 높아짐.
- 방안: 형식 검증은 Controller, 비즈니스 검증은 Service로 역할을 철저히 분리하되, 최후의 무결성 보장을 위해 DB 제약조건 수준의 최소한의 방어막은 중복으로 구성하는 것이 안정적임.
2. 단위 테스트: Mockito Mock, Stub, Spy의 개념 및 선택 기준
테스트 시 검증 대상이 아닌 외부 의존성(Repository 등)을 대체하기 위해 사용하는 테스트 더블(Test Double) 객체들.
개념 및 구체적 예시
- Stub (상태 기반 가짜 객체)
- 개념: 호출 시 미리 준비된 하드코딩된 상태(데이터)를 반환하도록 세팅된 객체.
- 선택 기준/예시: Service 로직 진행을 위해 특정 데이터 반환이 필수적일 때 사용. (예:
findById(1)호출 시 테스트용 ‘개발팀’ 부서 엔티티를 반환하도록 세팅)
- Mock (행위 검증 가짜 객체)
- 개념: 상태를 반환하는 것이 아니라, 특정 메서드가 호출되었는지, 파라미터는 무엇인지 행동(Behavior) 자체를 추적하고 검증하는 객체.
- 선택 기준/예시: 반환 값이 없는(
void) 메서드의 실행 여부를 확인할 때 사용. (예: 부서 삭제 기능에서deleteById(1)이 정확히 1회 호출되었는지verify로 검증)
- Spy (부분 실제 객체 래퍼)
- 개념: 원본 객체의 실제 로직을 그대로 실행하면서, 특정 메서드 하나만 가짜 동작(Stub)으로 덮어씌우거나 호출을 추적할 때 사용하는 객체.
- 선택 기준/예시: 실제 엔티티의 핵심 내부 비즈니스 로직(예: 예산 계산 로직)은 실제 코드로 테스트하고, 외부 시스템과 연동되는 메서드(예: 알림 발송) 하나만 동작을 제한하고 싶을 때 활용.