개요
- Enum 의 대한 Request를 받는 DTO를 개발하던 중, Enum 대한 DTO를 어떤 자료형으로 받는게 맞을지 생각을 하다가, Enum.Class 에 저장되지 않은 값이 들어왔을 때에는 어떻게 처리하면 좋을 지에 대해 의문이 들었다.
- 그러하여 만든 것이 Enum 에 대한 Vaildation 검사를 수행하는 Anotation 이였다.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValid {
String message() default "올바른 값이 아닙니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> enumClass();
}
- 기본적인 Anotation의 코드의 내용은 이렇게 되어있다.
- Vaildation이 성립되지 않을 시 출력되는 message에 값과 여러가지 클래스가 담기는 객체들로 이루어져 있다.
- 중요한 부분은 Class<? extends Enum<?>> 이다.
- Enum Class를 받는 부분인데 , 하나의 Enum 클래스에 종속되지 않고 여러가지 Enum.Class 에 사용하고 싶어 제네릭을 이용해 객체를 받을 수 있게 만들었다.
그리고 EnumVaild 를 구현한 구현체 Class 이다.
public class EnumValidator implements ConstraintValidator<EnumValid, String> {
private Class<? extends Enum<?>> enumClass;
private String message;
@Override
public void initialize(EnumValid constraintAnnotation) {
this.enumClass = constraintAnnotation.enumClass();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
String toEnumStr = "";
if("".equals(value) || value == null || enumClass == null) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("객체가 비어있습니다").addConstraintViolation();
return false;
}
for (Enum<?> enumVal : enumClass.getEnumConstants()) {
toEnumStr += enumVal.name()+", ";
if (enumVal.name().equals(value)) {
return true;
}
}
toEnumStr = toEnumStr.substring(0,toEnumStr.length()-2);
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("유효하지 않은 값입니다 (유효한 값 :" + toEnumStr + ")").addConstraintViolation();
return false;
}
}
- 형태를 살펴보게 되면 ConstraintValidator 로 Vaildation을 수행하는 인터페이스를 상속받게 되어있는데, ConstraintValidator 는 <어노테이션, 자료형> 을 받게된다.
- 그래서 ConstraintValidator<EnumValid, String> 을 상속받게 설정하여 준다.
- enumClass 와 message를 Custom 가능할 수 있도록 인자로 받게된다.
- 2개의 메서드를 수정할 수 있도록 Override 받게 된다.
- initialize 는 EnumValid 에서 Value로 받은 EnumClass 를 현재 Class에서 선언한 enumClass로 주입 시켜 준다.
- isValid 에선 유효성 검사에 대한 로직을 수행하게 되는데 매게변수로 EnumVaild를 선언해 준 DTO의 객체의 값을 value로 받게 되고, 유효성 검사에 대한 결과 정보를 담은 객체 ConstraintValidatorContext를 받게된다.
- EnumValid를 선언해준 DTO의 객체를 Null 에 대한 검사를 진행한다.
if("".equals(value) || value == null || enumClass == null) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("객체가 비어있습니다").addConstraintViolation();
return false;
}
- 만약 null 일 경우 context에 대한 Message 값을disableDefaultConstraintViolation 메서드를 실행해 초기화 시켜준 다음 buildConstraintViolationWithTemplate 에 Message를 만들어주고 addConstraintViolation 로 주입 시켜준다.
- 그리고 결과는 false 을 return 시켜준다. ( 유효성 검사 실패 )
- 선언한 Enum.Class 에 해당하는 값인지 검사를 진행한다.
String toEnumStr = "";
for (Enum<?> enumVal : enumClass.getEnumConstants()) {
toEnumStr += enumVal.name()+", ";
if (enumVal.name().equals(value)) {
return true;
}
}
- 주입 받은 enumClass 를 이용해 현재의 value 값이 Enum.Class 에 있는지 반복문을 통해 검사한다.
- value 값이 enumClass 값과 일치하면 true 를 return 시켜준다. ( 유효성 검사 성공 )
- toEnumStr 로 주입받은 enumClass 의 값을 message 출력을 위해 String으로 가지게 된다.
- 일치하는 값이 없으면 유효하지 않은 값으로 유효성 검사를 끝내게 된다.
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("유효하지 않은 값입니다 (유효한 값 :" + toEnumStr + ")").addConstraintViolation();
return false;
- 마찬가지로 message의 초기화를 위해 disableDefaultConstraintViolation 메서드를 실행 시켜 초기화를 진행 주 유효하지 않은 값이라는 message 에 Enum에 대한 Name값을 합친 String 값을 포함시켜 Exception Message를 context 객체에 주입 시켜준다.
- 그리고 false 을 return 시켜준다. ( 유효성 검사 실패 )
사용하는 예시는
@EnumValid(enumClass = SendLocation.class)
private String sendLocation;
- String 으로 Request를 받아주고 @EnumValid 를 선언해 준다음 enumClass 에 EnumClass 에 대한 주입을 해주면 완성된다.
'Spring boot' 카테고리의 다른 글
| i18N 다국어 API 개발 (0) | 2023.08.14 |
|---|---|
| Request Test (0) | 2023.08.01 |
| ResponseEntity Code Convention (0) | 2023.07.26 |
| ResponseResult 개발 진행 (0) | 2023.07.26 |
| Spring Filter (0) | 2022.05.11 |
댓글