인프런 김영한님의 스프링mvc2편을 보고 정리한 글입니다.
무엇을 배웠나요?
- 스프링에서는 검증 오류처리방법으로 BindingResult를 제공한다.
- 💡주의! BindingResult 파라미터의 위치는 @ModelAttribute 다음에 와야 한다
- 필드 오류 - FieldError
- if (!StringUtils.hasText(item.getItemName())) { bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수입니다.")); }
- FieldError 생성자 요약
- 필드에 오류가 있으면 FieldError 객체를 생성해서 bindingResult 에 담아두면 된다.
- objectName : @ModelAttribute이름
- field : 오류가 발생한 필드 이름
- defaultMessage : 오류 기본 메시지
- public FieldError(String objectName, String field, String defaultMessage) {}
- 글로벌 오류 - ObjectError
- bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));
- ObjectError 생성자 요약
- 특정 필드를 넘어서는 오류가 있으면 ObjectError객체를 생성해서 bindingResult에 담아두면 된다
- objectName: @ModelAttribute의 이름
- defaultMessage : 오류 기본 메시지
- public ObjectError(String objectName, String defaultMessage) {}
- 타임리프는 스프링의 BindingResult 를 활용해서 편리하게 검증 오류를 표현하는 기능을 제공한다.
- #fields : #fields 로 BindingResult 가 제공하는 검증 오류에 접근할 수 있다.
- th:errors : 해당 필드에 오류가 있는 경우에 태그를 출력한다. th:if 의 편의 버전이다.
- th:errorclass : th:field 에서 지정한 필드에 오류가 있으면 class 정보를 추가한다.
- 만약 @ModelAttribute에 바인딩시 타입 오류가 발생한다면
- if BindingResult 가 없으면 400 오류가 발생하면서 컨트롤러가 호출되지 않고, 오류 페이지로 이동한다.
- else BindingResult 가 있으면 오류 정보( FieldError)를 BindingResult에 담아서 컨트롤러를 정상 호출한다. → 스프링이 직접 FieldError나 objectError 를 생성해서 BindingResultResult에 넣어준다.
- BindingResult에 검증 오류를 적용하는 방법에는 3가지가 있다.
- @ModelAttribute 의 객체에 타입 오류 등으로 바인딩이 실패하는 경우 스프링 FieldError 생성해서 BindingResult 에 넣어준다.
- 개발자가 직접 넣어준다.
- Validator사용
- FieldError는 오류발생시 사용자 입력 값을 저장하는 기능을 제공한다.(rejectedvalue가 저장하는 공간)
- 만약 타입오류로 바인딩에 실패하면 스프링은 FieldError를 생성하면서 사용자가 입력한 값을 넣어둔다.
- 그리고 해당 오류를 BindingResult에 담아서 컨트롤러를 호출한다.
- 따라서 타입오류같은 바인딩은 실패시에도 사용자의 오류 메시지를 정상 출력할 수 있다.
➕타임리프의 사용자 입력 값 유지
- th:field=”*{price}”
- 타임리프의 th:field는 정상 상황에서는 도델 객체의 값을 사용하지만, 오류가 발생하면 FieldError에서 보관한 값으 사용해서 값을 출력한다.
궁금한 점은 무엇인가요?
더 필요하다고 생각한 것이 있나요?
코드
@PostMapping("/add")
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.getPrice() < 1000 || item.getPrice() > 1000000) {
bindingResult.addError(new FieldError("item", "price", "가격은 1,000 ~ 1,000,000 까지 허용합니다."));
}
if (item.getQuantity() == null || item.getQuantity() > 10000) {
bindingResult.addError(new FieldError("item", "quantity", "수량은 최대 9,999 까지 허용합니다."));
}
//특정 필드 예외가 아닌 전체 예외
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";
}
//성공 로직
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v2/items/{itemId}";
}
<form action="item.html" th:action th:object="${item}" method="post">
<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>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}"
th:errorclass="field-error" class="form-control"
placeholder="가격을 입력하세요">
<div class="field-error" th:errors="*{price}">
가격 오류
</div>
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{quantity}"
th:errorclass="field-error" class="form-control"
placeholder="수량을 입력하세요">
<div class="field-error" th:errors="*{quantity}">
수량 오류
</div>
</div>
'Spring' 카테고리의 다른 글
검증1 - Validation : Validator 분리1,2 (0) | 2022.08.26 |
---|---|
검증1 - Validation : 오류 코드와 메시지 처리1~6 (0) | 2022.08.26 |
검증1 - Validation (0) | 2022.08.26 |
메시지, 국제화 : 메시지, 국제화 소개, 스프링 메시지 설정및 사용 (0) | 2022.08.24 |
타임리프 - 스프링 통합과 폼 : 체크박스, 라디오버튼, 셀렉트 박스 (0) | 2022.08.24 |