본문 바로가기

Programing/Framework

[spring] JSR-303 과 @Valid 과 @Validated

Controller 에서는 요청바디에 대해서는 @Valid 라는 annotation을 붙이면 Bean Validation(JSR-303)기능을 수행해준다.

하지만 @PathVariable 에 대해서는 적용이 불가능하다.

 

만약 bean validation 을 통해 하고자 한다면 @Validated 을 Controller 클래스(타입)에 붙여주면 동작을 한다.

@Valid의 경우 바인딩이 실패가되면 MethodArgumentNotValidException 을 발생시킨다.

하지만 @Validated 로 바인딩 validation 실패가 발생하면 ConstraintViolationException 예외가 발생하였다.

javax.validation.ConstraintViolationException: null at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:147) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)

 

참고로 @Validated 을 사용하려면 MethodValidationInterceptor 이 동작을 해야 한다.

이 동작을 위해서는 MethodValidationPostProcessor 를 빈으로 등록해주면 된다.

@Configuration
public class ValidationConfig {

    @Bean
    public Validator validator() {
        return new LocalValidatorFactoryBean();
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }

다만 @Validated 가 타입에 붙게 되면 @Valid 붙은 것들도 모두 MethodValidationInterceptor 에 의해 처리가 되어 

기존에 MethodArgumentNotValidException 예외가 발생하던 것이 ConstraintViolationException로 바뀌게 된다.

문제는 MethodArgumentNotValidException 에서 제공하는 BindingResult 의 getFieldErrors() 메서드로 획득할 수 있는 List<FieldError> 중 FieldError 의 getField() 를 얻을 수 없다는 점이다.

 

물론, ConstraintViolation<T> 는 getPropertyPath() 메서드를 제공하지만 이름이 사람이 알아보기가 어렵게 메서드 이름 + 인자 번호 식으로 되어 있다.

save.arg0 가 뭐더냐...