Controller 계층 (프레젠테이션 계층)
책임과 범위
- 단일 필드 또는 DTO 내부 필드 간의 기본적 검증
- 예: null/blank, 길이, 숫자 범위, 정규식, 이메일 형식 등
- 기술적 제약
- 예: 타입, 필수값, 값의 기본 범위 등
- 주로 입력값 자체에 대한 검증이 이뤄진다.
- API 명세에 따른 검증 및 사용자 친화적 에러 메시지 제공
- 클라이언트가 잘못된 값을 보냈을 때 빠르게 차단, 구체적 피드백
방법
- 주로 Bean Validation(@Valid, @NotBlank, @Email, @Min, @Pattern 등) 활용.
예시
public class JoinRequest {
@NotBlank
private String nickname;
@Min(1) @Max(100)
private Integer age;
}
Service 계층 (비즈니스/도메인 계층)
책임과 범위
- 비즈니스 규칙 검증
- 예: 이메일 중복, 상태 전이, 여러 객체 간의 관계, 트랜잭션 일관성, 복합 조건
- 도메인 모델의 불변성, 상태 변경 규칙, 업무적 제약
- 예: 채팅방 인원 제한, 상품 상태 값이 허용된 값인지 등
- 복잡한 업무 로직, 데이터 간 상호관계, DB 조회 기반 검증
방법
- 서비스/도메인 로직에서 직접 if문, 커스텀 Validator, 예외 발생 등으로 처리
예시
if (userRepository.existsByEmail(request.getEmail())) {
throw new DuplicateEmailException();
}
if (room.isFull()) {
throw new RoomFullException();
}
DB 계층 (Repository/엔티티/데이터베이스)
책임과 범위
- 데이터 무결성의 최후 방어선
- 예: UNIQUE, NOT NULL, FOREIGN KEY, CHECK 제약조건
- 애플리케이션에서 누락된 검증을 보완
방법
- DB 스키마 제약조건, JPA 엔티티의 제약조건 등.
예시
- UNIQUE 제약: 이메일 중복 방지
- NOT NULL: 필수값 보장
중복 검증을 피하면서 안정성 확보하는 방안
- 역할 분리 원칙 준수:
- 컨트롤러: 입력 데이터의 구조/형식만 검증
- 서비스/도메인: 비즈니스 규칙만 검증
- DB: 무결성만 보장
- 공통 검증 유틸리티/Validator 활용:
- 여러 계층에서 동일한 검증이 필요하면 별도의 Validator 클래스로 추출해 재사용
- 책임 연쇄 패턴 등 도입:
- 계층별 검증을 체인 형태로 연결해, 각 계층의 책임을 명확히 하면서 중복을 줄임
- DB 제약조건 적극 활용:
- 예: 이메일 중복은 DB UNIQUE 제약조건 + 서비스 계층 사전 체크
- 단, 복잡한 CHECK 제약조건 같은 경우는 DB 성능을 저하시킬 수 있으므로 지양할 것
- 에러 메시지 일관성 유지:
- 각 계층에서 발생한 에러가 클라이언트에 일관되게 전달되도록 예외 처리 체계 설계
트레이드오프
