Validation의 동작

  1. DispatcherServlet → RequestMappingHandlerAdapter
  2. 각 파라미터를 처리할 HandlerMethodArgumentResolver가 선택된다. 이때 Reflection을 통해 결정된다.
    • @RequestBody → RequestResponseBodyMethodProcessor
    • @ModelAttribute(기본) → ModelAttributeMethodProcessor
  3.  이 Resolver 들은 파라미터를 객체로 변환한 뒤 validateIfApplicable() 을 호출하며, 여기서 파라미터에 @Valid가 붙어 있는지 Reflection 으로 확인한다.
  4. 확인 후 Validator 구현체(spring-boot-starter-validation 의존성 받을 시 자동 생성 및 주입)
    1. 전달된 객체(또는 메서드 파라미터) → 그 안의 필드·게터·파라미터 …
    2. 거기에 붙어 있는 모든 어노테이션을 살펴본다.
    3. 그 어노테이션의 타입(클래스)이 @Constraint를 달고 있으면 즉시 “제약 조건”으로 간주하여 해당 ConstraintValidator 인스터스를 생성 후 실행한다.
      1. initialize(A annotation) 호출: 해당 어노테이션의 attribute 값을 내부 필드에 저장하는 단계
      2. 대상 값(value)을 매개변수로 isValid(value, context) 호출: 결과가 false이면 message 값을 담은 ConstraintViolation 객체를 생성
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Constraint(validatedBy = EnumMatchValidator.class)
public @interface EnumMatch {
    String message() default "Not Match Enum Value";

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

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

    String[] includes() default {};

    String[] excludes() default {};
}
  • @Constraint(validatedBy = ~): 실질적인 유효성 검사를 진행할 클래스 지정
  • 아래는 모든 제약(Constraint) 애노테이션이 반드시 제공해야 하는 기본 구성 요소로 생략하면 에러가 발생한다.
    • message(): 유효성 검사에서 false가 발생할 경우 반환할 메세지
    • payload(): 유효성 검사에서 사용할 부가 정보로 일반적으로 잘 쓰이지 않는다.
    • groups(): @Validated 어노테이션에서 지정한 인자와 동일한 것만 유효성 검사를 해주는 역할이다. 코드가 복잡해져서 자주 사용되지 않는다. 


class EnumMatchValidator implements ConstraintValidator<EnumMatch, Enum<?>> {

    private String[] includes;
    private String[] excludes;

    @Override
    public void initialize(EnumMatch constraintAnnotation) {
        includes = constraintAnnotation.includes();
        excludes = constraintAnnotation.excludes();
    }

    @Override
    public boolean isValid(Enum<?> value, ConstraintValidatorContext context) {
        if (includes.length == 0 && excludes.length == 0) {
            return true;
        }

        if (includes.length != 0) {
            for (String include : includes) {
                if (include.equals(value.name())) {
                    return true;
                }
            }

            return false;
        }

        for (String exclude : excludes) {
            if (exclude.equals(value.name())) {
                return false;
            }
        }

        return true;
    }
}
  • includes와 excludes 모두 비어있으면 무조건 유효성 검사를 통과시킨다.
  • includes가 존재할 때는 무조건 includes를 기반으로 유효성 검사를 진행한다. 즉, excludes가 존재해도 includes로만 유효성 검사를 진행한다.
  • 이후 excludes로 유효성 검사를 진행한다.
  • validation 어노테이션과 ConstraintValidator 구현 클래스는 밀접한 관련이 있으므로 package-private으로 한 파일 내에 정의해줬다.
    • 만약 다른 validation 어노테이션에서도 사용한다면 따로 정의하자.

'Spring Boot > validation' 카테고리의 다른 글

Validation 예외 처리  (0) 2024.10.30
Spring Boot - Validation  (1) 2024.07.22

+ Recent posts