카테고리 없음

📝 TIL: @ValidEnum을 사용한 Enum 값 검증 및 Jackson 설정

creator7087 2025. 4. 21. 21:03

1. 목표

  • DTO에서 Enum 값 검증을 통해 유효하지 않은 값에 대해 커스텀 예외를 던지고, 상세한 에러 메시지를 반환하는 방법을 학습했습니다.
  • Jackson 설정을 통해 Enum 값을 매핑하고, 대소문자 무시 설정을 적용했습니다

 

2. 핵심 개념

a. @ValidEnum 커스텀 어노테이션

  • @ValidEnum은 enum 값을 검증하기 위해 사용되는 커스텀 어노테이션입니다.
  • 이 어노테이션을 통해 enum 필드 값이 유효한 값인지 검증하고, 유효하지 않으면 커스텀 예외를 던질 수 있습니다.

b. EnumValidator

  • EnumValidator는 @ValidEnum 어노테이션을 실제로 처리하는 유효성 검증 클래스입니다.
  • Enum 클래스에서 지정된 값만을 유효한 값으로 인정하고, 유효하지 않으면 예외를 발생시킵니다.

c. Jackson 설정

  • Jackson의 DeserializationFeature.ACCEPT_CASE_INSENSITIVE_ENUMS 옵션을 사용하여 대소문자 무시를 처리합니다.
  • DTO에서 enum 타입을 직접 받도록 설정하고, String 대신 Enum 타입을 받도록 수정합니다.

3. @ValidEnum 커스텀 어노테이션 구현

@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
@Documented
public @interface ValidEnum {

    String message() default "유효하지 않은 값입니다.";

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

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

    Class<? extends Enum<?>> enumClass();

    boolean ignoreCase() default false;
}

 

  • enumClass: 유효성 검증할 enum 클래스를 지정.
  • ignoreCase: 대소문자를 구분할지 여부를 설정할 수 있는 옵션.

4. EnumValidator 클래스

 

public class EnumValidator implements ConstraintValidator<ValidEnum, String> {

    private Class<? extends Enum<?>> enumClass;
    private boolean ignoreCase;

    @Override
    public void initialize(ValidEnum annotation) {
        this.enumClass = annotation.enumClass();
        this.ignoreCase = annotation.ignoreCase();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isEmpty()) {
            return true; // 다른 검증 어노테이션이 처리
        }

        return Arrays.stream(enumClass.getEnumConstants())
                .anyMatch(e -> {
                    String name = e.name();
                    return ignoreCase ? name.equalsIgnoreCase(value) : name.equals(value);
                });
    }
}

 

 

  • initialize: 어노테이션에서 지정된 enumClass와 ignoreCase 값을 초기화.
  • isValid: 필드 값이 유효한 enum 값인지 체크합니다.

5. Category enum과 PostRequest DTO 예시

public enum Category {
    NOTICE("공지사항"),
    FREE("자유 게시판"),
    QNA("질문 게시판");

    private final String description;

    @JsonCreator
    public static Category fromString(String value) {
        try {
            return Category.valueOf(value.toUpperCase()); // 대소문자 무시
        } catch (IllegalArgumentException e) {
            throw new InvalidRequestException("유효하지 않은 Category입니다.");
        }
    }

    @JsonValue
    public String getDescription() {
        return description;
    }
}

 

  • fromString 메서드에서 대소문자 무시유효하지 않은 값 처리를 합니다.
  • @JsonCreator를 사용하여 Jackson에서 JSON 값을 Enum으로 매핑합니다.

PostRequest DTO

public class PostRequest {

    @NotBlank
    private String title;

    @NotBlank
    private String content;

    @NotNull
    @ValidEnum(enumClass = Category.class, message = "유효하지 않은 카테고리입니다. 유효한 카테고리 값은 NOTICE, FREE, QNA 입니다.")
    private Category category;

    // Getter, Setter
}

 

 category는 Category enum 타입으로 받으며, 유효성 검증을 위해 @ValidEnum을 사용합니다.

 

6. Jackson 설정 (대소문자 무시)

@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer enumCaseInsensitive() {
        return builder -> builder.featuresToEnable(
                DeserializationFeature.ACCEPT_CASE_INSENSITIVE_ENUMS
        );
    }
}

이 설정을 통해 대소문자 구분 없이 enum 값을 매핑할 수 있습니다. 예를 들어 "category": "notice"가 "category": "NOTICE"로 변환됩니다

 

7. Controller에서의 사용 예시

@PostMapping("/posts")
public ResponseEntity<PostResponse> createPost(@Valid @RequestBody PostRequest postRequest) {
    PostResponse response = postService.createPost(postRequest);
    return ResponseEntity.ok(response);
}

 

@Valid 어노테이션으로 유효성 검증을 트리거하고, 유효하지 않으면 400 Bad Request와 함께 커스텀 에러 메시지가 반환됩니다.

 

8. 최종 정리

  • @ValidEnum 어노테이션을 사용하여 enum 값 검증을 처리하고, 유효하지 않은 값이 입력되면 커스텀 예외상세한 에러 메시지를 반환합니다.
  • Jackson 설정을 통해 대소문자 무시를 적용하고, DTO에서 Enum 값을 직접 받도록 설정할 수 있습니다.
  • @JsonCreator@JsonValue를 활용해 JSON과 Enum의 매핑을 커스터마이즈할 수 있습니다.

오늘 배운 내용은 유효성 검증Jackson을 활용한 Enum 처리에 관한 중요한 개념들을 다룬 것으로, 실제 프로젝트에서 유용하게 사용할 수 있습니다. 🎉