Enum 유효성 검사하기
요약
- @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'
다른 방법이 있다면 공유부탁드리겠습니다^^ ㅎㅎ