Today I Learned
비밀번호 유효성 검증 방식 비교
회원가입 시 비밀번호에 대소문자, 숫자, 특수문자를 각각 1개 이상 포함하도록 검증해야했다.
1. @Pattern 방식 (정규식 기반)
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\\\d)(?=.*[!@#$%^&*()_+\\\\-=]).{8,20}$",
message = "비밀번호는 영문 대소문자, 숫자, 특수문자를 포함한 8~20자여야 합니다.")
private String password;
장점
빠르게 적용 가능
별도 클래스 없이 DTO에 바로 붙일 수 있음
단점
정규식이 복잡하고 가독성이 떨어짐
조건 추가/변경 시 유지보수가 어려움
테스트나 디버깅이 불편함
2. @ValidPassword 방식 (커스텀 어노테이션)
@ValidPassword
private String password;
구성
`@ValidPassword` 어노테이션 정의
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordValidator.class)
@Documented
public @interface ValidPassword {
String message() default "비밀번호는 영문 대소문자, 숫자, 특수문자를 포함해야 합니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
`PasswordValidator` 클래스에서 자바 코드로 검증 로직 작성
public class PasswordValidator implements ConstraintValidator<ValidPassword, String> {
private static final String PASSWORD_PATTERN =
"^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=]).{8,20}$";
private Pattern pattern;
@Override
public void initialize(ValidPassword constraintAnnotation) {
pattern = Pattern.compile(PASSWORD_PATTERN);
}
@Override
public boolean isValid(String password, ConstraintValidatorContext context) {
if (password == null) {
return false;
}
return pattern.matcher(password).matches();
}
}
장점
가독성 높고 유지보수에 유리
조건별 메시지 분기 가능
여러 DTO에서 재사용 가능
테스트 코드 작성이 쉬움
단점
클래스 2개 추가 필요
초기 설정 번거로움
결론
`@ValidPassword` 방식은 유지보수성과 확장성이 뛰어나며, 비밀번호 정책이 자주 바뀌거나 조건이 복잡한 경우에 유리하다.
이런 이유로 이번 프로젝트에서는 정규식 기반이 아닌 커스텀 어노테이션을 활용하여 방식을 적용했다.