Development/Java

[Spring] @Valid Collection Validation 관련

반응형

Spring 이라기보다 Java에서 원래 제공하던 놈(javax) 중에


@Valid 라는 것이 있다.


Spring에서는 해당 @Valid를 Validator라는 인터페이스로 지원을 하고 있다.


이는, 해당 Object 내의 field들을 검사해서 NULL인지 혹은 타입체크(email, phone 등) 여부를 체크할 수 있는 좋은 어노테이션인데,


현재 기준으로 그냥 쓰면 안되는 것 중에 하나가


Collection을 Validate 하는 부분이 안된다.


Google에 검색을 해보아도 Collection은 @Valid가 적용이 안된다는 걸 알 수 있는데,


컨트롤러단에서 아래와 같이 받았다고 치자.


public ResponseEntity registration(@Valid @RequestBody List<CohortPostDto> dto)


그럼 해당 dto는 List로 받아오기 때문에 @Valid로 설정했던 값들


@NotNull, @Nullable 들이 적용이 안되게 된다.


List를 하나의 Object라 생각하기 때문이다.


반면에 아래와 같은 일반 단일 Object는 Validation이 가능하다.




public ResponseEntity registration(@Valid @RequestBody CohortPostDto dto)



요점은, 해당 List로 받아오는 List<Object> 에 대해서 검사를 어떻게 할 것인가에 대한 점인데


이미 누군가가 올려둔 방법이 있다


https://github.com/HomoEfficio/dev-tips/wiki/SpringMVC%EC%97%90%EC%84%9C-Collection%EC%9D%98-Validation


다만 코드를 보면 아래와 같다.


@Component
public class CustomCollectionValidator implements Validator {

    private SpringValidatorAdapter validator;

    public CustomCollectionValidator() {
        this.validator = new SpringValidatorAdapter(
                Validation.buildDefaultValidatorFactory().getValidator()
        );
    }

    @Override
    public boolean supports(Class clazz) {
        return Collection.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {

        Collection collection = (Collection) target;

        for (Object object : collection) {
            validator.validate(object, errors);
        }
    }
}


위와 같은 Component를 만들어서 Bean으로 등록해서 쓰는 방법인데,


이거 사용하면 된다


중요한건 Collection에 대한 @Valid만 적용이 된다.


위의 경우, supports() 함수에서 Collection의 클래스일 경우에만 Validation을 지원하겠다고 선언하여, 다른 Object들에 대한 Validation이 먹히지 않게 된다.


그래서 아래와 같이 수정을 하면 다른 일반 Collection이 아닌 클래스의 Validation도 원래와 같이 잘 작동하는 것을 보장할 수 있다.


위 코드를 쓰다가, 기존에 사용하던 Object들에 대한 Validation이 안되어 궁금해서 찾아보던 차 회사 동료에게 자문을 구해 알게된 내용이다.



/**
 * Created by stacks5978 on 2017-06-22.
 */
public class CustomCollectionValidator implements Validator {

    private SpringValidatorAdapter validator;

    public CustomCollectionValidator() {
        this.validator = new SpringValidatorAdapter(
                Validation.buildDefaultValidatorFactory().getValidator()
        );
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }

    @Override
    public void validate(Object target, Errors errors) {
        if(target instanceof Collection){
            Collection collection = (Collection) target;

            for (Object object : collection) {
                validator.validate(object, errors);
            }
        } else {
            validator.validate(target, errors);
        }

    }
}


코드 수정한건 간단한데,


supports를 모든 타입에 대해 true를 넘겨주는 것으로 변경이 되었고,


validate()를 하는 부분에 Collection인 경우에만 Collection 내의 Object들에 대한 Validation을 진행하고,


그렇지 않은 일반 클래스인 경우엔 원래 Validation을 하는 방식으로 분기를 태웠다.


이렇게 두고 사용하니 원하는 대로 잘 작동한다.

반응형