JSR-303 Annotation Customizing

데이터를 저장하기 전에는 데이터에 대한 검증이 요구된다.
서비스에 정의한 값들이 매우 반복적이여서 검증하는 코드를 숨기고 명시적으로 보여줄 수 있는 것 같아서 JSR-303 어노테이션을 커스텀하는 방식을 사용해봤다.

우선 필드에서 검증할 어노테이션을 생성해준다. 이 때, message, groups, payload 메소드는 반드시 존재해야 한다.
그리고 values 라는 속성을 추가하여 주로 사용되는 값을 디폴트로 설정했다. 다른 값을 검증하려면 필드에 설정한 어노테이션에 values 속성을 설정해주면 된다.
@Constraint 어노테이션에는 직접 생성할 ConstraintValidator를 구현하는 Validator를 지정해줘야 한다.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = AllowedValueValidator.class)
public @interface AllowedValue {
  String[] values() default { “S”, “M”, “L" };
  String message() default “{com.validator.constraints.AllowedValue.message}";
  Class<?>[] groups() default {};
  Class<? extends Payload>[] payload() default {};
}

Validator를 생성하여 ConstraintValidator 인터페이스를 상속 받고, 인터페이스에 선언된 메소드를 구현해줘야 한다.
initialize 메소드에서는 초기화할 데이터가 필요한 경우 설정해주면 된다. 앞서 만든 어노테이션을 인자로 하여 디폴트로 설정한 값으로 초기화했다.
isValid 메소드에서는 실제 검증할 내용을 작성해주면 된다. 검증할 필드에 요청된 값이 values에 정의된 값 중에 존재하는지 확인했다.

public class AllowedValueValidator implements ConstraintValidator<AllowedValue, String> {
  private String[] values;
  @Override
  public void initialize(AllowedValue constraintAnnotation) {
    this.values = constraintAnnotation.values();
  }
  @Override
  public boolean isValid(String value, ConstraintValidatorContext context) {
    return ArrayUtils.contains(values, value);
  }
}

검증할 필드에 커스텀한 어노테이션을 표기한다.
특정 필드에서는 기본 검증 값이 아닌 “red”, “blue” 값을 검증하도록 표기했다.

public class Clothing {

  private String id;

  private String name;

  @AllowedValue
  private String topSize;

  @AllowedValue
  private String bottomSize;

  @AllowedValue(values = { "red", "blue" })
  private String color;
}

컨트롤러에서는 기존 처럼 모델에 @Valid와 BindingResult만 표기해준다.
해당 요청이 들어오면 모델 필드에 표기한 커스텀 어노테이션의 Validator에 작성한 isValid 메소드로 검증하게 된다.
조건에 맞지 않는 경우에는 bindingResult의 해당 필드에 대한 에러 내용이 담긴다.

@PostMapping("/clothing/update")
public void update(@Valid Clothing clothing, BindingResult bindingResult) {
  if (bindingResult.hasErrors()) {
    FieldError fieldError = bindingResult.getFieldError();
    throw new RuntimeException(fieldError.getDefaultMessage());
  }
  ...
  clothingManager.update(clothing);
}

매우 반복적인 값들에 대한 검증으로 괜찮다고 생각한다.
하지만 모델 필드에 values 에 표기할 값들이 많아지는 경우는 오히려 코드가 복잡해 보일 수도 있다.
검증하는 방법은 다양하니 상황에 적절하게 잘 사용하면 좋을 것 같다.

+ Recent posts