@JsonCreater
가게의 카테고리를 Enum을 사용하여 표현해 주었다.
@Getter
@AllArgsConstructor
public enum Category {
RESTAURANT("음식점"),
CAFE("카페"),
BAKERY("베이커리"),
PUB("주점"),
BAR("바"),
ETC("기타");
private String viewName;
}
모든 가게의 정보가 이미 데이터베이스에 들어있거나 enum값(CAFE)을 이용하여 가게를 추가하는 게 아니라
요청 시 viewName을 사용하기 위해선 enum값을 맵핑해줘야 한다.
@Getter
@NoArgsConstructor
public class PostStoreDto {
...
@ApiModelProperty(value = "가게 종류", example = "음식점")
private Category category;
...
}
Jackson Annotation 중 @JsonCreater를 사용해 매핑함수를 만들어주면 된다.
public enum Category {
...
@JsonCreator
public static Category ofView(String viewName) {
for (Category category : Category.values()) {
if (category.getViewName().equals(viewName)) {
return category;
}
}
return null;
}
...
}
이렇게 하면 request body로 들어온 String값 viewName과 일치하는 Category의 viewName을 찾아,
Category 객체를 반환해 주기 때문에 그것으로 맵핑해 준다.
어떻게 이렇게 작동하는 걸까?
@JsonCreator는 JSON 데이터에서 Enum 값을 역직렬화할 때 사용되는데,
일반적으로 역직렬화하기 위해선 해당 Enum의 문자열 값과 일치하는 문자열을 파싱 해서 Enum 객체를 생성했다.
그러므로 @JsonCreator을 사용하여 Enum객체 생성조건을 직접 만들어주었다.
@EnumValid
만약 위 코드에서 null값이 반환된다면 어떻게 될까? JSON parse error가 발생한다.
이대로 둔다고 해도 큰 문제가 발생하는 것은 아니나 원하는 방식대로 handling이 불가능하다.
일관성 있는 ErrorResponse를 반환하기 위해 ConstraintValidator를 사용하였다.
/**
* EnumValidate 확인을 위한 어노테이션입니다.
* */
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = EnumValidValidator.class)
public @interface EnumValid {
String message() default "Invalid Enum";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class EnumValidValidator implements ConstraintValidator<EnumValid, Enum<?>> {
@Override
public void initialize(EnumValid constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(Enum<?> value, ConstraintValidatorContext context) {
return value != null;
}
}
JSON 변환 후 validation이 이뤄지기 때문에 어떤 enum type 인지와 관계없이 null 값 여부만 확인해 주면 된다. → isValid
@Getter
@NoArgsConstructor
public class PostStoreDto {
...
@ApiModelProperty(value = "가게 종류", example = "음식점")
@EnumValid
private Category category;
...
}
ConstraintViolationException는 반환 시 500 에러로 나타나 이를 400 에러로 바꿔주기 위해
@RestControllerAdvice를 활용했다.
@RestControllerAdvice
public class ExceptionAdvice {
...
/**
* 값이 유효하지 않을 때
* */
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ResponseEntity<ErrorResponse> handleConstraintViolationException(ConstraintViolationException constraintViolationException) {
constraintViolationException.getConstraintViolations().stream().peek(o -> log.error(o.getMessage()));
final ErrorResponse response = ErrorResponse.of(ErrorCode._BAD_REQUEST);
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
...
}
https://velog.io/@rmswjdtn/Spring-Enum-Type-Request-DTO-Mapping
[Java] @JsonCreater : Enum Type Request DTO Mapping
Enum Type Request DTO Mapping Enum type을 쓰다보면 별칭을 주는 경우들이 있습니다. 이렇게 별칭을 줘서 사용할 경우 Enum Type의 Request DTO에 Request body가 매핑되는 과정에서 매핑오류가 나는 경우가 생깁니
velog.io
Spring 기반 - ConstraintValidator을 이용해서 효과적인 검증 | Popit
해당 코드는 Github 에 공개되어 있습니다. 스프링에서는 JSR 303 기반 어노테이션 기반으로 일관성 있는 Validation을 진행할 수 있습니다. 하지만 @NotNull , @NotEmpty , @Email 과 같은 검증은 가능하지만
www.popit.kr
'💻 프로젝트 > 🍝 홍잇' 카테고리의 다른 글
[HongEat] 리팩토링 - Enum/ParameterBinding(Converter) (0) | 2023.10.03 |
---|---|
[HongEat] 리팩토링 - Enum/toJson(@JsonValue) (0) | 2023.04.22 |
[HongEat] 리팩토링 - 변경사항 (0) | 2023.04.22 |
[HongEat] 리팩토링, 어떻게 진행했는가 (0) | 2023.04.22 |