Java에서 커스텀 유효성 검증 어노테이션 만들기
일반적으로 @Email
, @Pattern
, @NotBlank
등의 표준 유효성 검증 어노테이션을 사용하지만, 프로젝트 내부에서 DTO를 만들어줄 때 전부 동일한 유효성 검사를 반복해서 하다보니 한 군데서 모아서 관리할 수 없을까 하다가 찾아보게 되었습니다.
1. 커스텀 유효성 검증 어노테이션 만들기
먼저, @ValidEmail
과 @ValidPassword
라는 커스텀 어노테이션을 만들어 보겠습니다. 이 어노테이션을 사용하면 이메일 형식과 비밀번호 규칙을 한 번에 검증할 수 있습니다.
UserDTO 클래스:
public class UserDTO {
@Documented
@Constraint(validatedBy = {UserValidator.EmailValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Size(max = 255)
@Schema(description = "회원 이메일", example = "test1234@naver.com")
public @interface ValidEmail {
String message() default "유효하지 않은 이메일 형식입니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Documented
@Constraint(validatedBy = {UserValidator.PasswordValidator.class})
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Schema(description = "회원 비밀번호", example = "Test1234test!")
public @interface ValidPassword {
String message() default "유효하지 않은 비밀번호 형식입니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Schema(description = "회원가입 요청")
public record RegisterRequest(
@NotBlank(message = "이름은 비어 있을 수 없습니다.")
@Size(min = 2, max = 6)
@Schema(description = "회원 이름", example = "홍길동")
String name,
@ValidEmail
String email,
@ValidPassword
String rawPassword
) {}
public record RegisterResponse(
UUID id,
@ValidEmail
String email,
String name,
String studyGoal
) {}
@Schema(description = "로그인 요청")
public record LoginRequest(
@ValidEmail
String email,
@ValidPassword
String rawPassword
) {}
}
위 코드에서는 @ValidEmail
과 @ValidPassword
라는 커스텀 어노테이션을 정의하고, 회원가입 및 로그인 시 해당 어노테이션을 사용하여 필드 검증을 쉽게 할 수 있습니다.
2. 검증 로직 구현:
UserValidator
클래스 안에 실제 검증 로직을 정의합니다. 이메일과 비밀번호는 각각의 정규식을 기반으로 검증할 수 있습니다.
UserValidator 클래스:
public class UserValidator {
private static final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
private static final String PASSWORD_REGEX = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d@$!%*#?&]{10,20}$";
// 이메일 검증
public static class EmailValidator implements ConstraintValidator<UserDTO.ValidEmail, String> {
@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return email != null && email.matches(EMAIL_REGEX);
}
}
// 비밀번호 검증
public static class PasswordValidator implements ConstraintValidator<UserDTO.ValidPassword, String> {
@Override
public boolean isValid(String password, ConstraintValidatorContext context) {
return password != null && password.matches(PASSWORD_REGEX);
}
}
}
UserValidator
클래스는 내부 클래스로 EmailValidator
와 PasswordValidator
를 정의하고 있습니다. 각 클래스는 정규식을 이용해 이메일과 비밀번호의 유효성을 검증합니다.
3. 커스텀 유효성 검증의 장점
- 재사용성: 한 번 정의한 커스텀 어노테이션은 여러 DTO나 엔티티에서 재사용할 수 있습니다.
- 가독성: 검증 로직이 각 필드에 직접 포함되지 않고 커스텀 어노테이션으로 분리되므로, 코드의 가독성이 크게 향상됩니다.
- 유지보수성: 이메일 형식이나 비밀번호 규칙이 변경될 경우, 커스텀 어노테이션만 수정하면 모든 검증 로직이 자동으로 적용됩니다.
- 비즈니스 로직 분리: 검증 로직이 비즈니스 로직과 분리되어 코드를 더 깔끔하고 관리하기 쉽게 만듭니다.