카테고리 없음

[Spring][Validation] 3. Spring Bean Validation

noahkim_ 2025. 10. 13. 14:36
  • Spring은 Bean Validation API를 완전하게 지원

 

1. LocalValidatorFactoryBean

  • Spring ↔️ Bean Validation 제공자를 연결해주는 브리지/어댑터 빈
  • Binding: Spring 바인딩 모델 연동 (Errors, BindingResult)
  • i18n: Spring MessageSource 연동
  • ✅ 커스텀 검증기 주입: 빈 등록을 통해 추가 가능
  • ✅ 제공자 감지: 클래스패스의 기본 Bean Validation 제공자 1개를 자동으로 감지 및 부트스트랩
  • ✅ 인터페이스: jakarta.validation.Validator, org.springframework.validation.Validator 

 

설정) LocalValidatorFactoryBean

더보기
@Configuration
public class AppConfig {
    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}
  • 'hibernate-validator' 의존성이 있으면, 자동으로 등록됨

 

예제) LocalValidatorFactoryBean 사용

더보기
@Service
public class MyService {
    @Autowired
    private jakarta.validation.Validator validator;
    // private org.springframework.validation.Validator validator; // 이것도 됨
}

 

메시지 커스터마이징

  • Bean Validation 메시지 인터폴레이션을 MessageSource와 연결하기
  • ✅ 메시지 키로 에러 메시지를 찾아 i18n/커스터마이즈 가능

 

흐름) 메시지 인터폴레이션

더보기
  1. 메시지 템플릿 결정
    • 제약 애노테이션의 'message'값 사용
    • 비워둘 경우, 표준 기본 메시지를 사용함 (jakarta.validation.constraints.{annotation name}.message)
  2. 키 해석
    • 메시지 소스에서 메시지 템플릿으로 조회
    • 파일 이름: ValidationMessages_{locale}.properties,  ValidationMessages.properties
  3. 치환 (인터폴레이션)
    • 템플릿 내부의 이름 기반 파라미터를 값으로 대체
  4. 로케일 선택
    • LocaleContextHolder가 현재 Locale로 해당 언어 번들에서 메시지 선택

 

설정) 메시지 인터폴레이션

더보기

1. 메시지 파일 생성

# src/main/resources/ValidationMessages.properties

Size.person.name=이름은 {2}자 이상 {1}자 이하여야 합니다.
NotBlank.person.name=이름은 필수 항목입니다.
Max.age=나이는 {1} 이하여야 합니다.
  • {0}: 필드명
  • {1}, {2}: 제약 조건 파라미터

 

2. 제약 조건 어노테이션에 메시지 키 지정 (@Constraint 속성)

public class Person {

    @Size(min = 2, max = 10, message = "{Size.person.name}")
    private String name;

    @Max(value = 100, message = "{Max.age}")
    private int age;
}
  • message = "{메시지 키}" 형태로 지정하면 MessageSource에서 해당 메시지를 찾아 사용함.

 

3. MessageSource 빈 등록하기

@Configuration
public class MessageConfig {

    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("ValidationMessages"); // 확장자 없이 basename만
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}
  • 커스터마이징 메시지 프로퍼티를 사용하기 위해 설정 필요
  • 다국어 지원 시, ValidationMessages_ko.properties, ValidationMessages_en.properties 등 추가 가능

 

4. LocalValidatorFactoryBean에 MessageSource 연결

@Bean
public LocalValidatorFactoryBean getValidator(MessageSource messageSource) {
    LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
    validator.setValidationMessageSource(messageSource);
    return validator;
}

 

2. MethodValidationPostProcessor

  • 메서드 검증을 켜주는 BeanPostProcessor
  •  AOP 기반 동작 (메서드 호출을 프록시가 가로채고, 호출 전/후에 jakarta validator로 유효성 검증)
  • ✅ 프록시 (검증 애노테이션을 사용하려는 클래스를 프록시화 함)
  •  어드바이저 부착 (검증 애노테이션을 대상으로 포인트컷이 설정됨. MethodValidationInterceptor가 검증을 담당함)

 

설정) MethodValidationPostProcessor

더보기
@Configuration
public class ApplicationConfiguration {
    @Bean
    public static MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

 

예시) 사용

더보기
@Service
@Validated
public class MyService {
    public void addStudent(@Valid Person person, @Max(2) int degrees) {
        // ...
    }
    
    @NotNull  // 반환값이 null이면 예외 발생
    public String getUserName(Long id) {
        // DB에서 못 찾았다고 치자
        return null;
    }
}

 

MethodValidationInterceptor

  • 메서드 검증을 실제로 수행하는 AOP 인터셉터

 

출처