Untitled

고객이 잘못된 값을 입력하면 서버 검증 로직이 실패하면서 고객에게 다시 상품 등록 폼을 보이고, 잘못 입력한 부분을 친절하게 알려주어야 한다.

컨트롤러에서 오류를 검증하고 렌더링 시 해당되는 에러 메시지를 띄우도록 한다.

BindingResult

BindingResult 가 있으면 오류 정보( FieldError )를 BindingResult 에 담아서 컨트롤러를 정상 호출한다.

@PostMapping("/add")    //BindingResult는 ModelAttribute 다음에 위치해야 한다.
    public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {

        //검증 로직
        if(!StringUtils.hasText(item.getItemName())){
            bindingResult.addError(new FieldError("item","itemName","상품 이름은 필수입니다."));
        }

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

        //검증에 실패하면 다시 입력 폼으로
        if(bindingResult.hasErrors()){
            log.info("errors = {} ", bindingResult);
            return "validation/v2/addForm";
        }

        //성공 로직
}

bindingResult를 이용하여 오류 검증.

<div th:if="${#fields.hasGlobalErrors()}">
  <p class="field-error" th:each="err: ${#fields.globalErrors()}" th:text="${err}">글로벌 오류 메시지</p>
</div>
<div>
  <label for="itemName" th:text="#{label.item.itemName}">상품명</label>
  <input type="text" id="itemName" th:field="*{itemName}"
    th:errorclass="field-error" class="form-control" placeholder="이름을 입력하세요">
  <div class="field-error" th:errors="*{itemName}">
    상품명 오류
  </div>
</div>

#fields : #fields 로 BindingResult 가 제공하는 검증 오류에 접근할 수 있다.

th:errors : 해당 필드에 오류가 있는 경우에 태그를 출력한다. th:if 의 편의 버전이다.

th:errorclass : th:field 에서 지정한 필드에 오류가 있으면 class 정보를 추가한다.

FieldError 생성자

파라미터 목록

objectName : 오류가 발생한 객체 이름 field : 오류 필드 rejectedValue : 사용자가 입력한 값(거절된 값) 보관 bindingFailure : 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값 codes : 메시지 코드 arguments : 메시지에서 사용하는 인자 defaultMessage : 기본 오류 메시지

FieldError 는 두 가지 생성자를 제공한다.

public FieldError(String objectName, String field, String defaultMessage);
public FieldError(String objectName, String field, @Nullable Object
rejectedValue, boolean bindingFailure, @Nullable String[] codes, 
@Nullable Object[] arguments, @Nullable String defaultMessage);

오류 코드와 메시지 처리

MessageCodesResolver

메시지를 더 범용성 있게 사용하도록 도와준다.

#Level1
totalPriceMin.item=상품의 가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}

#Level2 - 생략
totalPriceMin=전체 가격은 {0}원 이상이어야 합니다. 현재 값 = {1}

범용성에 따라 메시지를 나누어 놓고 bindingResult.reject("totalPriceMin",new Object[]{10000,resultPrice},null); 를 호출하여 메시지들을 문자열 배열로 받는다.

@InitBinder
public void init(WebDataBinder dataBinder) {
 log.info("init binder {}", dataBinder);
 dataBinder.addValidators(itemValidator);
}

컨트롤러가 실행될 때 검증기 초기화. 글로벌 설정은 별도로 함.

@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, 
RedirectAttributes redirectAttributes) {
		
}

: @Validated 애노테이션을 추가하여 모델 타입에 맞는 검증기 호출.