티스토리 뷰

반응형

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을 하는 방식으로 분기를 태웠다.


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

반응형
댓글
  • 프로필사진 스프링초절정꽃미남개발군단 ♩♪♩ 이해가 안되는데

    CustomCollectionValidator 이라고 Collection 전용의 Validator 를 이름 짓고 만들었으면서

    Collection 이 아닌 Object에서 분기를 태워 Validator 쓰려는 이유가 모냐?

    ♩♪♩ 이해 안가네 ㅋㅋㅋㅋㅋㅋㅋㅋ
    2021.04.21 01:18
  • 프로필사진 BlogIcon DEV @곰팡 CustomCollectionValidator 에서 나머지 object의 validation까지 진행하는게 이상한 모양이긴 하네요 ㅎㅎ;

    해당 포스트를 작성할 때는, CustomCollectionValidator 를 @Component 어노테이션으로 bean 등록을 시켜버려서 Collection일때만 @Valid 어노테이션을 내부 element에 대해서도 동작하게 하고자 하는 정도로 작성했었습니다.
    (Validator를 전역으로 쓰기 위해서 else문에 object case를 분리해둠)

    collection<object> 로 request를 받을 시, collection 에 대한 validation을 받는게 문제여서

    collection 안에 있는 object를 validation 하기 위해 만들어놓은 샘플코드입니다.

    직관적이진 않아서 Validator를 분리해서 사용하던지, spring-boot-starter-validation 같은 좋은 라이브러리도 있는데 말이죠~

    조언 감사합니다.
    2021.04.21 01:35 신고
  • 프로필사진 이상민 안녕하세요.
    작성해주신 내용을 잘 사용하고 있습니다.
    근데 해당 내용이 @RequestBody 문에서는 작동이 안되는데, 해당 경우에는 적용이 안되는 것이 맞을까요?

    --------
    추가로 확인해보니까 @Valid 에서는 정상작동하는데 groups 부여하기 위해서 @Validated 사용할 때만 미적용되는 것 같네요. @Valid 방식으로 사용해야겠네요. 감사합니다.
    2021.06.18 11:10
댓글쓰기 폼