전 페이지에서 우리는 Map 자료구조를 활용하여 Errors를 직접 만들었다. 스프링은 뷰에 생긴 에러를 컨트롤러에서 잡기위해 BindingResult로 제공한다!! ModelAttribute처럼 .addAttribute 자동으로 해줌!

아래 코드와 전 페이지의 내가 만든 코드를 비교해보자! 얼마나 편해졌나!!

		@PostMapping("/add")
    public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {

        //검증 로직
        if (!StringUtils.hasText(item.getItemName())) {
            bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수 입니다."));
        }
        if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
            bindingResult.addError(new FieldError("item", "price", "가격은 1,000 ~ 1,000,000 까지 허용합니다."));
        }
        if (item.getQuantity() == null || item.getQuantity() >= 9999) {
            bindingResult.addError(new FieldError("item", "quantity", "수량은 최대 9,999 까지 허용합니다."));
        }

        //특정 필드가 아닌 복합 룰 검증
        if (item.getPrice() != null && item.getQuantity() != null) {
            int resultPrice = item.getPrice() * item.getQuantity();
            if (resultPrice < 10000) {  // ObjectError가 globalError임 FieldError는 그의 자식..
                bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));
            }
        }

        //검증에 실패하면 다시 입력 폼으로
        if (bindingResult.hasErrors()) { // BindingResult는 @ModelAttribute처럼 자동으로 view에 값이 담겨 있음.
            log.info("errors={} ", bindingResult);
            return "validation/v2/addForm";
        }

        //성공 로직
        Item savedItem = itemRepository.save(item);
        redirectAttributes.addAttribute("itemId", savedItem.getId());
        redirectAttributes.addAttribute("status", true);
        return "redirect:/validation/v2/items/{itemId}";
    }

그럼 BindingResult에 대해서 자세하게 알아보자!

스프링이 제공하는 검증오류를 보관하는 객체이다. → 너무 중요하고 유익하다!! 왜냐? 아래의 상황을 보자.

@ModelAttribute에 바인딩 시 타입 오류가 발생하면?

  1. BindingResult가 없으면 → 400에러 발생, 컨트롤러 호출 되지않고 바로 오류페이지로 이동. 즉 뭐가 문제인 건지 프론트에게 전달해줄 수가 없음.
  2. BindingResult가 있으면 → 오류정보(FindingError)를 BindingResult에 담아서 컨트롤러를 정상호출한다.

BindingResult에 검증오류를 적용하는 3가지 방법

  1. @ModelAttribute의 객체에 타입오류 등으로 바인딩이 실패하는 경우에 스프링이 “FieldError” 생성해서 BindingResult에 넣어준다! (순수하게 코드상 객체의 타입 불일치시 스프링이 넣어줌!)
  2. 개발자가 직접 설정!! (맨위의 코드처럼 우리가 if문 걸어서 원하는 조건에 bindingResult.addError 하면 된다!)
  3. “Validator” 사용. (추후에 배움)