Previous


validation.zip

이번 챕터를 학습하며 작성했던 Item 관련 프로젝트 혹시라도 이해가 잘 되지 않는다면 코드를 import하여 참고하자.

🤫 Bean Validation이란?


특정 구현체가 아닌 Bean Validation 2.0(JSR-380)이라는 기술 표준으로 여러 검증 애노테이션과 여러 인터페이스의 모음이다. (ex: JPA라는 표준 기술에 구현체로 하이버네이트가 있다.)

이러한 Bean Validation을 구현한 기술중 일반적으로 사용하는 구현체는 하이버네이트 Validator이다.

(이름이 하이버네이트지만 ORM과는 관련없다. )

즉, Bean Validation 를 활용하면 애노테이션 기반으로 우리가 이전에 구현해봤던 각종 구현로직들을 간단하게 적용할 수 있다.

우리가 이전에 사용했던 검증방식은 직접 Validator 인터페이스를 구현한 뒤 InitBinder로 구현한 검증기를 등록해서 사용하는 식이였다.

@Component
public class ItemValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Item.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        Item item = (Item) target;

        ValidationUtils.rejectIfEmpty(errors, "itemName", "required");

        if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1_000_000) {
            errors.rejectValue("price", "range", new Object[]{1000, 1_000_000}, null);
        }
        if (item.getQuantity() == null || item.getQuantity() >= 9999) {
            errors.rejectValue("quantity", "max", new Object[]{9999}, null);
        }
        //복합 룰 검증
        if (item.getPrice() != null && item.getQuantity() != null) {
            int resultPrice = item.getPrice() * item.getQuantity();
            if (resultPrice < 10000) {
                errors.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
            }
        }
    }
}
@Controller
@RequiredArgsConstructor
public class ValidationItemControllerV2 {
    private final ItemValidator itemValidator;

    @InitBinder
    public void init(WebDataBinder dataBinder){
        dataBinder.addValidators(itemValidator);
    }
		@PostMapping("/add")
    public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
        //검증 실패시 다시 입력 폼으로 이동해야 한다.
        if (bindingResult.hasErrors()) {
            log.info("errors = {}", bindingResult);
            return "validation/v2/addForm";
        }
				...
    }
}

이 방식도 처음보다는 많이 간결해진 코드지만, 이마저도 Bean Validation을 사용하면 훨씬 간단해진다. 다음은 간단하게 위 유효성 검증을 Bean Validation을 적용한 코드이다. 도메인에서 검증이 필요한 필드에 바로 적용을 해준다.

@Data
public class Item {
    private Long id;

    @NotBlank
    private String itemName;

    @NotNull
    @Range(min = 1000, max = 1_000_000)
    private Integer price;

    @NotNull
    @Max(9999)
    private Integer quantity;

    public Item() {
    }

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}

짧은 애노테이션 몇 가지로 기존의 작성한 방대한 검증 로직들이 대부분 대치된다.