Enum Validate Custom Annotation

ᄋᄌᄒ·2024년 2월 17일

Spring_Project

목록 보기
2/11
post-thumbnail

📚 글 쓰기 전에

한창 프로젝트 진행중.. 신경 쓸게 많은데 벨로그 작성할것도 많아서 야금야금 작성중이다.(팀원들 모두 화이팅..)

📙 본문

📌 Problem

서버에 데이터를 보내는 request요청 api에는 종종 서버가 설정한 enum타입의 필드를 입력해야 하는 경우가 있다.
우리 프로젝트에도 여럿 있지만 그 중에 [팀 컬러]라는 필드가 존재하는데, 서버에서 설정한 컬러를 유저가 한정적으로 선택하여 사용할 수 있도록 유도한 것이다.
근데 이렇게 보낸 enum이 잘못된 데이터를 보낼 경우 비즈니스 로직까지, 아니 데이터베이스 입력 까지 수행되었다가 쿼리 오류가 발생한다. 이러한 것들은 valid를 통해 제한할 수 있는 것이 좋을 것이라 판단했다.

status


프론트에서는 단순히 string 타입이기 때문에 아무 값이나 입력할 수 있다.(물론, 회의를 통해 합의를 하기는 하지만 실수의 경우도 있으니까) 롤백은 서버에러, 즉 쿼리 에러가 발생하는 경우에 일어난 시점에서 발생되기 때문에 쓸데없이 쿼리까지 db에 넘어가는 것을 로그를 통해 확인할 수 있다.

📌 How

우리는 request할 때 비즈니스에서도 validate같은 로직을 사용하지만 단순히 값이 존재하냐, 정당한 값이냐, 최대 최소는 맞추었냐 등의 validate또한 비즈니스 로직이 돌아가기 전에도 확인하는 Bean Validation 방식을 사용한다.

Bean validation은 limit을 원하는 필드 위에 어노테이션을 붙여 원하는 limit설정을 해주고, controller에서 @Valid를 붙여 검증하는 방식이다.

📌 Resolve

@Documented
@Constraint(validatedBy = {ValidEnumValidator.class})
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidEnum {
    String message() default "enum not contains this field";

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

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

    Class<? extends Enum<?>> target();
}
@Component
public class ValidEnumValidator implements ConstraintValidator<ValidEnum, String> {
    private Set<String> enumValues;

    @Override
    public void initialize(ValidEnum targetEnum) {
        Class<? extends Enum> enumSelected = targetEnum.target();
        enumValues = (Set<String>) EnumSet.allOf(enumSelected).stream()
                .map(e -> ((Enum<? extends Enum<?>>) e).name()).collect(Collectors
                        .toSet());
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return enumValues.contains(value);
    }
}
public class TeamRequest {

    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @ToString
    public static class Post {
        //...생략
        @ValidEnum(target = TeamColor.class)
        private String teamColor;
        //...
    }

}

ValidEnum.annotation : 커스텀 어노테이션 인터페이스
ValidEnumValidator.class : 실제로 커스텀 어노테이션을 사용했을 때 검증 로직

@ValidEnum(target = {Enum.class})와 같이 필드위에 설정해주면 해당 enum에 사용자가 입력한 항목이 존재하는지 검증하게 된다.

📌 Result

request(error)

response(error)

request(right)

response(right)

📨 글을 마치면서

저 위에 보면 dto 관련 어노테이션들도 묶어버릴 수 있는 커스텀 어노테이션을 만들고 싶다.. 지금 최선은 @Data, @Noargu~, @Allargu~, @Builder이렇게 네개를 사용하는 방식밖에 없지 않을까 한다. (시도는 해봤는데 안됨)

0개의 댓글