Spring

Enum 유효성 검사하기

91cm 2021. 8. 23. 22:58
요약
- @Pattern과 enum은 같이 쓸 수 없다.
- 커스텀어노테이션을 만들어서 @JsonCreator와 같이 사용해서 처리했다.

 

파라미터 검증중 @Valid + @Pattern 이용해서 enum을 검증하려고 했다. 

예를들어 Y,N만 들어가는 enum 타입의 멤버필드에  A를 집어 넣으려고한다면???

 

물론 데이터가 들어가진 않겠지만 에러코드가 상당히 이쁘지 않을 것이다.

 

아래 코드를 통해 처리했다.

 

DTO

import com.example.test.test.constant.YnCode;
import javax.validation.constraints.Pattern;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
public class EnumRequestDto {

    private String name ;

    @Pattern(regexp = "Y|N") // 작동할까?
    private YnCode ynCode;

}

enum

public enum YnCode {
    Y("Y"),
    N("N"),
    ;

    private final String code;

    YnCode(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
 }

controller

@PostMapping("/yncode")
    public String insertYnCode(@RequestBody @Valid EnumRequestDto enumRequestDto) {
        log.info("insertYnCode param : {}", enumRequestDto);
        return "OK";
    }

test

 

json parse error! 발생

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `com.example.test.test.constant.YnCode` from String "ABC": not one of the values accepted for Enum class: [NONE, Y, N]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `com.example.test.test.constant.YnCode` from String "ABC": not one of the values accepted for Enum class: [Y, N]

 

 

enum을 검증하려면 커스텀어노테이션을 만들어야 한다...

 

커스텀 어노테이션 만들기

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Constraint(validatedBy = EnumPatternValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumPattern {
    String regexp();
    String message() default "does not match \"{regexp}\"";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EnumPatternValidator implements ConstraintValidator<EnumPattern, Enum<?>> {
    private Pattern pattern;

    @Override
    public void initialize(EnumPattern annotation) {
        try {
            pattern = Pattern.compile(annotation.regexp());
        } catch (PatternSyntaxException e) {
            throw new IllegalArgumentException("pattern regex is invalid", e);
        }
    }

    @Override
    public boolean isValid(Enum<?> value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }

        Matcher m = pattern.matcher(value.name());
        return m.matches();
    }
}

 

자이제 적용해 보자.

 @EnumPattern(regexp = "Y|N", message = "Y 또는 N만 입력 가능합니다")
 private YnCode ynCode;

 

띠롱. JSON parse error 발생..

 

왜냐하면 애당초 enum에 정해진 값외 들어와 버리면 JSON parse error가 난다.

그럼 어떻게 해야할까?

 

@JsonCreator를 이용했다.

이걸 사용하면 하면 JSON 객체를 deserialize해서 객체맵핑시 사용할 생성자를 지정할 수 있게 해준다.

파라미터로 Y,N값이외의 값이 들어오면 null을 전달하게하였다.

그러면 EnumPatternValidator에 NONE이 전달되고 Pattern class를 통해서 일치여부를 검증한다.

import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.stream.Stream;

public enum YnCode {
    Y("Y"),
    N("N")
    ;

    private final String code;

    YnCode(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static YnCode findByCode(String code) {
        return Stream.of(YnCode.values())
            .filter(c -> c.code.equals(code))
            .findFirst()
            .orElse(null);
    }
}

 

아주 친근한 exeption이 발생했다. MethodArgumentNotValidException ^^

이제 에러 메세지를 이쁘게 가공해서 내려주면 끝.

 

 

 

참고로

@JsonCreator와 @Pattern만 사용해도 작동하지 않을까? 생각이들었지만 enum + @Pattern은 작동하지 않는다.

validation.UnexpectedTypeException 발생

 

javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'com.example.test.test.constant.YnCode'. Check configuration for 'ynCode'

 

 

 

다른 방법이 있다면 공유부탁드리겠습니다^^ ㅎㅎ