프로젝트 진행중 spring validation 이외의 유효성 검증 작업이 필요하여 Custom Validator에 대해 찾아본 내용을 정리하려고 한다.
custom validator를 적용하려면 두 가지를 구현해야 한다.
먼저 커스텀 어노테이션 객체를 먼저 만들어보자.
package com.pitpat.pitterpatter.global.validation;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Documented
@Constraint(validatedBy = LocalDateValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidLocalDate {
String message() default "생일은 'YYYY-MM-DD' 형식이어야 합니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
※groups와 payload는 보통 우리는 잘 지정하지 않는다.
public class LocalDateValidator implements ConstraintValidator<ValidLocalDate, String> {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public void initialize(ValidLocalDate constraintAnnotation) {
}
@Override
public boolean isValid(String dateStr, ConstraintValidatorContext context) {
if (dateStr == null) {
return true; // null 값은 다른 검증에서 처리할 수 있음
}
try {
LocalDate.parse(dateStr, DATE_FORMATTER);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
}
ConstraintValidator<ValidLocalDate, String>
@Override
public void initialize(ValidLocalDate constraintAnnotation)
@Override
public boolean isValid(String dateStr, ConstraintValidatorContext context)
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChildRequestDTO {
private String profileImage;
@NotNull(message = "닉네임은 필수 값입니다.")
@Size(max = 10, message = "닉네임은 최대 10자까지 입력 가능합니다.")
private String nickname;
@NotNull(message = "성별은 필수 값입니다.")
private Gender gender;
@NotNull(message = "생일은 필수 값입니다.")
@ValidLocalDate
private String birth;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@ValidLocalDate
private String birth;

이렇게, 구현한 검증 로직을 통과하지 못한다면, 구현 validator에서 지정한 메세지가 출력되는 것을 확인할 수 있다 !!
처음 써보는 custom validator이지만 간단한 로직이라 쉽게 따라할 수 있었다.
단순히 마구잡이로 생성하지 말고, 이 또한 효율을 고민하고 custom validation을 관리한다면 조금 더 좋은 코드로 관리될 것 같다.