ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot 시리즈 8편 – 입력값 검증 전략: @Valid부터 도메인 중심 유효성 설계까지
    개발/Spring Boot 2025. 4. 24. 08:00

    Spring Boot에서 @Valid를 활용한 입력값 검증부터 커스텀 Validator, 도메인 기반 유효성 설계까지 실무 중심으로 정리했습니다. 예외 처리 통합 전략도 포함됩니다.


    Spring Boot 시리즈 8편 – 입력값 검증 전략: @Valid부터 도메인 중심 유효성 설계까지

    REST API에서 입력값 유효성 검증은 단순히 올바른 형식을 검사하는 것을 넘어,
    비즈니스 로직을 보호하고, 클라이언트의 예측 가능한 에러 응답을 보장하는 중요한 첫 관문입니다.

    이번 글에서는 @Valid와 Bean Validation 기본 사용법부터,
    복잡한 검증 로직을 도메인 내부로 이동시키고,
    필요한 경우 Validator 클래스를 직접 커스터마이징하는 전략까지 단계별로 설명합니다.


    🧱 1. 기본 검증 구조 – DTO + @Valid

    1️⃣ DTO에 검증 애노테이션 적용

    public class RegisterUserRequest {
    
        @NotBlank(message = "이름은 필수입니다.")
        private String name;
    
        @Email(message = "이메일 형식이 아닙니다.")
        @NotBlank(message = "이메일은 필수입니다.")
        private String email;
    
        @Size(min = 4, max = 20, message = "비밀번호는 4~20자 사이여야 합니다.")
        private String password;
    }
    
    • Bean Validation(Jakarta Validation) 기반으로 제공
    • @NotBlank, @Email, @Size, @Pattern 등 기본 제약조건을 조합하여 사용

    2️⃣ Controller에서 @Valid 적용

    @PostMapping("/api/users")
    public ResponseEntity<ApiResponse<Void>> register(@Valid @RequestBody RegisterUserRequest request) {
        userService.register(request);
        return ResponseEntity.ok(ApiResponse.success());
    }
    
    • @Valid 사용 시 자동으로 유효성 검사가 수행되며, 실패 시 MethodArgumentNotValidException 발생

    ❌ 2. 검증 실패 시 예외 처리 통합

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponse<List<FieldErrorResponse>>> handleValidationException(MethodArgumentNotValidException ex) {
        List<FieldErrorResponse> errors = ex.getBindingResult().getFieldErrors().stream()
            .map(err -> new FieldErrorResponse(err.getField(), err.getDefaultMessage()))
            .toList();
        return ResponseEntity.badRequest().body(ApiResponse.fail("C001", "유효성 검사 실패", errors));
    }
    
    • FieldErrorResponse 클래스는 각 필드별 오류를 클라이언트에게 명확하게 전달합니다.
    • 응답 형식은 7편에서 다룬 ApiResponse<T> 구조와 일관성 있게 유지합니다.

    🔁 3. 커스텀 유효성 검사 – 직접 Validator 만들기

    예시: 중복 이메일 방지 @EmailNotRegistered


    1️⃣ 애노테이션 정의

    @Target({ ElementType.FIELD })
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = EmailNotRegisteredValidator.class)
    public @interface EmailNotRegistered {
        String message() default "이미 등록된 이메일입니다.";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    2️⃣ Validator 구현

    @Component
    public class EmailNotRegisteredValidator implements ConstraintValidator<EmailNotRegistered, String> {
    
        private final UserRepository userRepository;
    
        public EmailNotRegisteredValidator(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            return value != null && !userRepository.existsByEmail(value);
        }
    }
    

    3️⃣ DTO에 적용

    public class RegisterUserRequest {
    
        @EmailNotRegistered
        private String email;
    }
    
    • Repository 의존성이 필요한 검증도 깔끔하게 구성할 수 있습니다.
    • 코드 재사용성과 명확성이 모두 높아집니다.

    🧠 4. 도메인 내부 유효성 검증 전략

    복잡한 비즈니스 조건은 DTO가 아니라 도메인 객체 내부에서 검증하는 것이 바람직합니다.

    예시: User 엔티티 내부에서 검증

    @Entity
    public class User {
    
        public void changePassword(String current, String newPassword) {
            if (!this.password.equals(current)) {
                throw new BusinessException(ErrorCode.INVALID_PASSWORD);
            }
            this.password = newPassword;
        }
    }
    
    • 비밀번호 일치 여부, 가입 자격 조건, 정책 기반 유효성 등은 도메인 책임
    • Entity가 스스로 자신의 유효한 상태를 유지하도록 설계

    ✅ 5. 실무 적용 전략 정리

    구분 적용 위치 설명

    형식 검증 DTO, @Valid JSON 역직렬화 전후 기본 조건 확인
    외부 의존 검증 커스텀 Validator DB 조회, 외부 API 의존 검증
    정책 기반 검증 Domain 객체 비즈니스 규칙, 상태 전이 조건 등
    공통 오류 처리 GlobalExceptionHandler MethodArgumentNotValidException, BindException 등 처리

    🧪 테스트 전략

    검증 대상 테스트 방식

    기본 검증 (@Valid) 컨트롤러 통합 테스트 (MockMvc or RestAssured)
    커스텀 Validator 단위 테스트 (스프링 컨텍스트 없이 가능)
    도메인 유효성 도메인 단위 테스트, 상태 전이 테스트

    ✅ 마무리 요약

    항목 핵심 내용

    검증 도구 @Valid, @NotBlank, @Size, 커스텀 애노테이션
    실패 처리 @ExceptionHandler(MethodArgumentNotValidException)
    고급 설계 DB 기반 검증은 Validator로, 정책 검증은 도메인 내부에
    응답 통일 ApiResponse<T> 구조로 에러 응답 포함
    테스트 책임 분리된 계층마다 명확한 테스트 전략 수립

    📌 다음 편 예고

    Spring Boot 시리즈 9편: 로그인 및 인증 처리 전략 – JWT 기반 인증 흐름 구현과 보안 고려사항

     

    LIST
Designed by Tistory.