반응형
Bean validation에서 기본적으로 제공하는 기능 이상으로 유효성 검증을 해야한다면 커스텀 어노테이션을 만들어서 검증할 수 있다. 커스텀 어노테이션 이외에 검증하는 방법은 여러가지가 있지만, 나는 컨트롤러나 서비스 레이어에서 검증 메서드를 계속 호출하는 것이 메서드의 핵심 코드를 읽기 불편하게 만든다고 생각하기 때문에 DTO의 검증은 DTO 레이어에서 처리하는게 좋다고 생각했다.
나와 비슷한 생각이신 분들을 위해 커스텀 어노테이션과 검증 클래스로 분리해서 DTO 유효성을 검사한 방법을 공유한다.
회원가입 DTO 코드
@NoArgsConstructor
@AllArgsConstructor
@Getter
@ValidSignUpRequest // Dto에 대한 검증은 Dto에서 처리하자
public class SignUpRequest {
private String nickname;
private String password;
private String passwordConfirm;
}
커스텀 어노테이션 코드
@Constraint를 적용하면 반드시 message, groups, payload를 속성으로 가져야 한다고 docs에서 설명하고 있다. validatedBy 속성의 값으로 검증할 validator를 설정할 수 있다. 나는 SignUpRequestValidator 클래스를 만들었다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SignUpRequestValidator.class)
public @interface ValidSignUpRequest {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
검증 클래스
ConstraintValidator 인터페이스를 상속받아서 isValid에 원하는 검증 로직을 작성할 수 있다.
public class SignUpRequestValidator implements ConstraintValidator<ValidSignUpRequest, Object> {
@Override
public void initialize(ValidSignUpRequest constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
SignUpRequest request = (SignUpRequest) value;
boolean valid = true;
if (!validateNickname(request.getNickname())) {
setErrorMessage(SIGNUP_NICKNAME.getField(), SIGNUP_NICKNAME.getMessage(), context);
valid = false;
}
if (!validatePassword(request.getNickname(), request.getPassword())) {
setErrorMessage(SIGNUP_PASSWORD.getField(), SIGNUP_PASSWORD.getMessage(), context);
valid = false;
}
if (!validatePasswordConfirm(request.getPassword(), request.getPasswordConfirm())) {
setErrorMessage(SIGNUP_PASSWORD_CONFIRM.getField(), SIGNUP_PASSWORD_CONFIRM.getMessage(), context);
valid = false;
}
return valid;
}
private boolean validateNickname(String nickname) {
if (nickname.length() < 3) return false;
return true;
}
private boolean validatePassword(String nickname, String password) {
return true;
}
private boolean validatePasswordConfirm(String password, String passwordConfirm) {
return true;
}
private void setErrorMessage(String fieldName, String message, ConstraintValidatorContext context) {
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(fieldName)
.addConstraintViolation()
.disableDefaultConstraintViolation();
}
}
반응형
'Language Framework > Spring' 카테고리의 다른 글
[Spring] Spring Data JPA의 쿼리 메서드로 정렬 조건 여러 개 + 페이지네이션 조회하기 (0) | 2023.12.20 |
---|---|
[Spring] Spring boot와 redis로 회원가입 이메일 인증기능 만들기 (0) | 2023.12.18 |
[Spring boot] 외부 파일 저장소를 사용할 때 트랜잭션은 어떻게 관리해야 할까? (with. AWS S3) (2) | 2023.12.11 |
[Spring boot] controller layer 테스트 코드 작성하기 (0) | 2023.12.04 |
[Spring boot] Service layer 테스트 코드 작성하기 (0) | 2023.12.04 |