스프링이 제공하는 BindingResult를 이용하여 검증 오류 처리를 한다.
BindingResult 파라미터는 @ModelAttribute 뒤에 와야한다.
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
//검증 로직
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, null, null, "상품 이름은 필수입니다."));
}
//검증에 실패하면 다시 입력 폼으로
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}";
}
objectName 오류가 발생한 객체 이름
field 오류 필드
rejectValue 사용자가 입력한 값 (거절된 값)
bindingFalure 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값
codes 메시지 코드
arguments 메시지에 사용하는 인자
defaultMessage 기본 오류 메시지
public FieldError(String objectName, String field, @Nullable Object rejectedValue, boolean bindingFailure, @Nullable String[] codes, @Nullable Object[] arguments, @Nullable String defaultMessage)
<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>
messages.properties를 사용해도 되지만 오류 메시지 구분을 위해 errors.properties라는 파일로 관리해본다.
//application.properties
spring.messages.basename=messages,errors
//errors.properties
required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.
totalPriceMin=가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}
이제 errors에 등록한 메시지를 활동하여 코드 변경한다.
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, new String[]{"required.item.itemName"}, null, null));
}
BindingResult가 제공하는 rejectValue(), reject()를 사용하면 FieldError, ObjectError를 직접 생성하지 않고, 깔끔하게 검증 오류를 다룰 수 있다.
rejectValue(), reject()는 내부에서 MessageCodeResolver를 사용한다. 여기에서 메시지 코드를 생성한다.
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.rejectValue("itemName", "required");
}
if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null);
}
field 오류 필드명
errorCode 오류 코드
errorArgs 오류 메시지에서 {0}을 치환하기 위한 값
defaultMessage 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지
void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
이제 복잡한 검증 로직을 ItemValidator클래스를 만들어 별도로 분리해본다.
import hello.itemservice.domain.item.Item;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@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;
if (!StringUtils.hasText(item.getItemName())) {
errors.rejectValue("itemName", "required");
}
if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
errors.rejectValue("price", "range", new Object[]{1000, 10000000}, null);
}
}
}
WebDataBinder는 스프링 파라미터 바인딩의 역할을 해주고 검증 기능도 내부에 포함된다.
이렇게 WebDataBinder에 검증기를 추가하면 해당 컨트롤러에서는 검증기를 자동으로 적용할 수 있다.
@InitBinder는 해당 컨트롤러에만 영향을 준다.
@InitBinder
public void init(WebDataBinder dataBinder) {
dataBinder.addValidators(itemValidator);
}
@PostMapping("/add")
public Object addItem(@RequestBody @Validated ItemSaveForm form, BindingResult bindingResult) {
log.info("API 컨트롤러 호출");
if (bindingResult.hasErrors()) {
log.info("검증 오류 발생 errors={}", bindingResult);
return bindingResult.getAllErrors();
}
log.info("성공 로직 실행");
return form;
}
🔗 강의 링크
'⚙️ 개발 > Spring' 카테고리의 다른 글
@RequiredArgsConstructor (0) | 2024.05.08 |
---|---|
@Autowired (0) | 2024.05.08 |
messages (0) | 2024.05.02 |
[Tymeleaf] 체크 박스, 셀렉트 박스 (0) | 2024.05.02 |
[Thymeleaf] object, field (0) | 2024.04.30 |