Validation 클래스 단위 제약과 조건부 검사 설정

POST 컨트롤러 호출시 @RequestBody로 요청받는 Request의 dto는 다음과 같습니다.

public class Request {

    private String name;

    private ColorType colrType;

    private String redApple;

    private String greenApple;

    private String redOrange;

    private String greenOrange;
    
    //getter, setter ...
   public enum ColorType {
      RED, GREEN
    }

colorType의 값이 RED면 redApple과 redOrange의 값은 필수,

GREEN면 greenApple과 greenOrange의 값에 대한 필수값 체크를 하고 싶었습니다.

 

우선 필드의 그룹지정을 위한 인터페이스를 만듭니다

public interface Red {

}
public interface Blue{

}

 

만든 그룹을 DTO의 필드에 각각 지정해줍니다.

public class Request {

    private String name;

    private ColorType colrType;

    @NotEmpty(groups = {Red.class})
    private String redApple;
    
    @NotEmpty(groups = {Green.class}) 
    private String greenApple;

	@NotEmpty(groups = {Red.class}) 		 
    private String redOrange;

	@NotEmpty(groups = {Green.class}) 
    private String greenOrange;
    
    //getter, setter ...

그룹으로 묶으면 해당 그룹을 지정후 그룹에 속한 필드의 유효성을 검사할 수 있습니다.

※ @NotEmpty의 groups의 인자는 비열을 받기 때문에 2개 이상의 Class 타입을 지정할 수 있습니다.

 

@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
public @interface NotEmpty {

	Class<?>[] groups() default { };

	//...

 

새로운 제약을 나타내는 어노테이션을 명시합니다.

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = ColorTypeConstraintValidator.class)
public @interface ColorTypeConstraint {
    String message() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

 

ColorTypeConstraintValidator의 구현부 입니다.

colorType이 RED일때 Group이 Red로 설정된 필드에 대한 유효성을 검사합니다.

Green일때는 Group이 Green으로 설정된 필드에 대한 유효성을 검사합니다.

public class ColorTypeConstraintValidator implements
    ConstraintValidator<ColorTypeConstraint, Request> {

    private Validator validator;

    public ColorTypeConstraintValidator(Validator validator) {
        this.validator = validator;
    }

    @Override
    public boolean isValid(AmSchInsertReq value, ConstraintValidatorContext context) {
        if(value.getColorType = ColorType.RED){
            final Set<ConstraintViolation<Object>> constraintViolations = validator.validate(value, Red.class);
            return isValid(constraintViolations, context);
        }else {
            final Set<ConstraintViolation<Object>> constraintViolations = validator.validate(value, Green.class);
            return isValid(constraintViolations, context);
        }
    }

    private boolean isValid(Set<ConstraintViolation<Object>> constraintViolations, ConstraintValidatorContext context){
        if (CollectionUtils.isNotEmpty(constraintViolations)) {
            context.disableDefaultConstraintViolation();
            constraintViolations
                .forEach(constraintViolation -> {
                    context.buildConstraintViolationWithTemplate(constraintViolation.getMessageTemplate())
                        .addPropertyNode(constraintViolation.getPropertyPath().toString())
                        .addConstraintViolation();
                });
            return false;
        }
        return true;
    }

}

 

※ 참고로 CollectionUtils.isNotEmpty를 사용하기 위해선 아래 라이브러리를 추가해야 합니다.

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.1</version>
</dependency>

 

해당 DTO의 클래스에 새롭게 정의한 어노테이션을 붙힙니다.

@ColorTypeConstraint
public class Request {

    private String name;

    private ColorType colrType;

    @NotEmpty(groups = {Red.class}) //중복 지정 가능
    private String redApple;

    @NotEmpty(groups = {Green.class}) //중복 지정 가능
    private String greenApple;

    @NotEmpty(groups = {Red.class}) //중복 지정 가능
    private String redOrange;

    @NotEmpty(groups = {Green.class}) //중복 지정 가능
    private String greenOrange;

//getter, setter ...

 

컨트롤러 메소드의 인자에 명시한 Request 옆에 @Validated와 @Valid 어노테이션을 붙힙니다.

@PostMapping("mapping")
public Response fruit(
        @RequestBody @Validated @Valid Request reqDTO

 

 

https://meetup.toast.com/posts/223

 

Validation 어디까지 해봤니? : NHN Cloud Meetup

TOAST Cloud의 메시징 플랫폼 상품인 Notification은 메시지, 이메일 주소 형식, 수신/발신자의 번호 등 클라이언트의 입력값에 대해 많은 검증을 진행합니다.

meetup.toast.com