BindingResult는 스프링에서 제공해주는 에러관련 기능이다.
@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 result = item.getQuantity() * item.getPrice();
if(result < 10000){
bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재값 = \" + result"));
}
}
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}";
}
순서가 중요하다. 내가 다룰 객체 바로 뒤에 넣어야 한다. 이번 같은 경우는 Item 다음에 바로 넣었다.
쉽다.
BindingResult.addError(new Error(객체명, 객체의오류를내보낼필드명, 오류메시지 ))
뭐 당연히 인자로 받았기 때문에 자동으로 모델에 들어가고, 거기에다 스프링에서 제공해주는 기능이라 더더욱 자동으로 모델에 들어간다.
FieldError가 필드에 관한에러, (나중에 템플릿에 이 필드에 관해서 묶어줌)
ObjectError는 그냥 오브젝트 전체에 관련한 에러라고 보면 된다.
<form action="item.html" th:action th:object="${item}" method="post">
<div th:if="${#fields.globalErrors()}">
<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}" class="form-control" placeholder="이름을 입력하세요"
th:errorclass="field-error">
<div class="field-error" th:errors="*{itemName}">
<p >상품 이름 오류</p>
</div>
</div>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요"
th:errorclass="field-error">
<div class="field-error" th:errors="*{price}">
<p>가격 오류</p>
</div>
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요"
th:errorclass="field-error">
<div class="field-error" th:errors="*{quantity}">
<p>수량 오류</p>
</div>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">상품 등록</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/validation/v2/items}'|"
type="button" th:text="#{button.cancel}">취소</button>
</div>
</div>
</form>
<div th:if="${#fields.globalErrors()}">
bindingResult에 접근하려면 #fields 이걸로 접근하면 된다.
만약 bindingResult에 글로벌에러가 있다면
<input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요"
th:errorclass="field-error">
errorclass는 에러 있으면 저 클래스 추가.
참고로 이거 뭐 아래 errors="*{price}" 처럼 우리가 따로 필드명 명시하지도 않았는데 되는 이유는
저 th:field 가 에러까지 포함함. 그냥 th:errors="*{itemName}" 까지 했다고 생각하면 됨.
<div class="field-error" th:errors="*{price}">
th:errors 에러가 있으면 저 태그 출력, 아니면 안함.
여기서 오브젝트접근으로 쓸 수 있는 이유는 필드명 똑같이 했고 바인딩 했기 때문.
그리고 이 bindingResult,
Rest API에서도 Json으로 바꿔서 내려줄 수 있다고 함.
'스프링 > 4. 스프링 MVC-2' 카테고리의 다른 글
30. FieldError, ObjectError (0) | 2023.08.29 |
---|---|
29. BindingResult2 (0) | 2023.08.29 |
27. 검증 직접 구현 (0) | 2023.08.28 |
26. 검증 (0) | 2023.08.27 |
25. 메시지, 국제화 적용 (0) | 2023.08.27 |