@PostMapping("/add")
public String addItemV2(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
/**
* bindingFailure=false : 클라이언트에서 넘어오는 매개변수와 item의 바인딩이 실패해도 binding 실패라고 보지 않겠다.
* 검증 로직에 걸렸을 떄에 FieldError를 생성하겠다는 뜻!!
*/
// 검증 로직
if(!StringUtils.hasText(item.getItemName()))
bindingResult.addError(new FieldError("item","itemName",item.getItemName(),
false,null,null,"상품명은 필수값입니다"));
if(item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000)
bindingResult.addError(new FieldError("item","price",item.getPrice(),
true,null,null,"가격은 1000원 이상 1000000이하 입니다."));
if(item.getQuantity() == null || item.getQuantity() >= 9999)
bindingResult.addError(new FieldError("item","quantity",item.getQuantity(),
true,null,null,"수량은 9,999개 이하입니다"));
if(item.getPrice()!=null && item.getQuantity() != null){
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice < 10000){
bindingResult.addError(new ObjectError("item",null,null,"가격*수량의 합은 10,000원 이어야 한다. 현재 값 ="+resultPrice + "원"));
}
// 이하 생략
}
@Data
public class Item {
private Long id;
private String itemName;
private Integer price;
private Integer quantity;
public Item() {
}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
스프링은 Request HTTP 메시지로 넘어온 데이터를 바인딩하기도 하지만 [타입 오류] 체크도 같이 해준다.
이때 스프링은 내부적으로 자동으로 아래와 같은 FieldError 객체를 생성한다.
new FieldError("item","price",item.getPrice(),
//bindingFailure 인수 = true로 설정됨
true,null,null,"가격은 1000원 이상 1000000이하 입니다.")
고로, 예를 들어 사용자가 price="aaaa"를 입력해서 타입 오류가 나면 컨트롤러 호출 이전 단계에서 자동으로 해당 오류
를 담아서 컨트롤러에 반환하여(정확히는 NumberException과 같은 타입 오류 예외 메시지를 담아 준다)
컨트롤러를 실행한다.
만약 컨트롤로 실행 시 Price에 대한 [검증 로직 오류]가 발생하면 이건 [타입 오류]에 의한 바인딩 실패(bindingFailure)
가 아니므로, 새로운 Field 객체를 아래와 같이 생성하여 bindinFailure 인수에 false로 설정을 해주면 된다.
(즉, 타입 오류와 검증 로직 오류를 철저히 분리하고 있다고 생각하면 된다)
if(item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000)
bindingResult.addError(new FieldError("item","price",item.getPrice(),
false,null,null,"가격은 1000원 이상 1000000이하 입니다."));
이때 타임리프는 th:field=&{item.price}에 대해 타입 오류와 검증 오류, 이 2가지 오류가 있다고 판단하여 렌더링한다.
(th:field는 해당 필드에 오류가 발생하면, BindingResult 객체에서 데이터를 꺼내서 사용한다. 만약 정상 상황이면 당연히 Model 객체에 담긴 데이터를 꺼내 사용)
'Springあるある' 카테고리의 다른 글
도메인의 진짜 의미! (0) | 2025.01.15 |
---|---|
[@RequestParam,@ModelAttribute] VS @ResponeBody(Feat.HttpMessageConverter) (0) | 2025.01.15 |
input 태그 속에 th:field와 th:value가 동시에 있는 경우 (0) | 2025.01.10 |
@ModelAttribute의 새로운 사용법 (0) | 2025.01.10 |
@RequiredArgsConstructor vs @AllArgsConstructor (0) | 2025.01.10 |