애플리케이션 계층별 검증 범위와 책임

썬쑨·2025년 5월 18일

Spring

목록 보기
9/12

애플리케이션 계층별로 입력값 검증을 적절히 분리하고 중복을 최소화하면서도 신뢰성과 안정성을 확보하는 것은 시스템 설계에서 매우 중요한 문제이다. 아래는 검증 범위와 책임을 나눈 방식과 그에 따른 트레이드오프에 대한 설명이다.

✅ 계층별 입력값 검증의 책임과 범위

1. 컨트롤러 계층

  • 책임: 외부에서 들어오는 비신뢰 데이터에 대한 형식적(validity) 검증
  • 검증 내용:
    • 형식 검증 (문자열 길이, 숫자/날짜 타입 여부)
    • 필수 입력값 여부
    • @Valid, DTO 제약조건 (Bean Validation) 사용
  • 목적: 빠르게 실패하고, 내부로 유효하지 않은 값이 침투하지 않도록 차단

2. 서비스 계층

  • 책임: 도메인 규칙 및 비즈니스 조건에 대한 검증
  • 검증 내용:
    • ID 중복 여부
    • 사용자의 권한 유효성
    • 도메인 간 복합 규칙 (예: 유일성 검증, 트랜잭션 일관성)
  • 목적: 비즈니스 규칙 위반 방지, 잘못된 흐름이나 악의적인 시도 방지

3. 도메인 계층

  • 책임: 데이터 무결성 보장
  • 검증 내용:
    • Unique 제약
    • Foreign key 제약
    • NOT NULL 등 스키마 제약 조건
  • 목적: 최종 데이터 무결성 보장

입력값을 DTO로 받고, 서비스에서 다시 도메인 규칙을 검증하며, DB에서도 제약 조건을 거는 구조에서 중복 검증이 자주 발생하게 된다. 이러한 상황에서는 검증 책임을 명확히 나누지 않으면 불필요한 코드 중복과 예외 처리 복잡도가 증가하게 된다.

✅ 중복 검증 피하기 위한 전략

1. 계층별 책임 명확화

: 각 계층에서의 검증 책임을 명확히 하여 중복을 최소화한다.
- 컨트롤러에서는 형식 검증만 수행한다.(@Valid 활용), 중복 여부나 상태값 검증은 서비스 계층에서만 처리하도록 분리한다.
- ex) 이메일 형식 검증은 @Email로 컨트롤러에서 처리, 이메일 중복 여부는 서비스에서만 체크.

2. 공통 검증 로직의 재사용

: 동일한 검증 로직이 여러 서비스에서 반복된다면, 이를 별도의 Validator 클래스나 컴포넌트로 분리한다. -> 코드 중복줄일 수 있음.

    // ex) 닉네임 유효성 검사용 Validator 클래스
  	@Component
  	public class NicknameValidator {

       private static final Pattern NICKNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9가-힣]{2,12}$");

       public void validate(String nickname) {
           if (nickname == null || !NICKNAME_PATTERN.matcher(nickname).matches()) {
               throw new IllegalArgumentException("닉네임은 2~12자의 영문/숫자/한글만 허용됩니다.");
           }
       }
  	}

3. 데이터베이스 제약 조건 활용

ex)
- UNIQUE 제약으로 중복 데이터 방지
- CHECK 제약으로 값 범위 강제

트레이드오프와 균형

모든 계층에 위같은 검증을 넣으면 안정성은 올라가지만,그만큼 중복 코드가 많아지고 유지보수성이 떨어질 수 있다.
반대로, 검증을 한 곳에서만 처리하면 코드가 깔끔해지지만,
실수나 우회 루트를 통해 잘못된 데이터가 들어갈 가능성이 있다.

profile
천천히 굴러갑니다!

0개의 댓글