BindingResult1
스프링이 제공하는 검증 오류 처리 방법인 BindingResult
에 대해 알아보자!
ValidationItemControllerV2
- addItemV1
BindingResult bindingResult
파라미터의 위치public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) { }
BindingResult bindingResult
파라미터의 위치는 @ModelAttribute Item item
다음에 와야 한다!
FieldError
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수입니다."));
}
FieldError
객체를 생성해서 bindingResult
에 담아두면 된다.public FieldError(String objectName, String field, String defaultMessage) {}
objectName
: @ModelAttribute
이름field
: 오류가 발생한 필드 이름defaultMessage
: 오류 기본 메시지ObjectError
bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));
ObjectError
객체를 생성해서 bindingResult
에 담아두면 된다.public ObjectError(String objectName, String defaultMessage) {}
objectName
: @ModelAttribute
의 이름defaultMessage
: 오류 기본 메시지타임리프는 스프링의 BindingResult
를 활용해서 편리하게 검증 오류를 표현하는 기능을 제공한다.
#fields
: #fields
로 BindingResult
가 제공하는 검증 오류에 접근할 수 있다.th:errors
: 해당 필드에 오류가 있는 경우에 태그를 출력한다. th:if
의 편의 버전!th:errorclass
: th:field
에서 지정한 필드에 오류가 있으면 class
정보를 추가한다.BindingResult2
BindingResult
가 있으면 @ModelAttribute
에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다!
예를 들어, 바인딩 시 타입 오류가 발생하면?
BindingResult
❌ → 400
오류 발생 → 컨트롤러 호출 ❌, 오류 페이지로 이동한다!BindingResult
⭕→ 오류 정보(FieldError
)를 BindingResult
에 담아서 컨트롤러를 정상 호출한다!BindingResult
에 검증 오류를 적용하는 3가지 방법@ModelAttribute
의 객체에 타입 오류 등으로 바인딩이 실패하는 경우, 스프링이 FieldError
생성해서 BindingResult
에 넣어준다.Validator
사용BindingResult
와 Errors
org.springframework.validation.Errors
org.springframework.validation.BindingResult
BindingResult
는 인터페이스이고, Errors
인터페이스를 상속받고 있다.BeanPropertyBindingResult
인데,BindingResult
대신에 Errors
를 사용해도 된다!Errors
인터페이스는 단순한 오류 저장과 조회 기능을 제공하며, BindingResult
는 여기에 더해서 추가적인 기능까지 제공한다. addError()
도 BindingResult
만 제공하므로 여기서는 BindingResult
를 사용해야 한다.BindingResult
를 많이 사용한다.FieldError, ObjectError
📌 목표
- 사용자 입력 오류 메시지가 화면에 표시되도록 하자.
FieldError
,ObjectError
에 대해서 더 자세히 알아보자.
ValidationItemControllerV2 - addItemV2
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)
objectName
: 오류가 발생한 객체 이름field
: 오류 필드rejectedValue
: 사용자가 입력한 값(거절된 값)bindingFailure
: 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값codes
: 메시지 코드arguments
: 메시지에서 사용하는 인자defaultMessage
: 기본 오류 메시지new FieldError("item", "price", item.getPrice(), false, null, null, "가격은 1,000 ~ 1,000,000 까지 허용합니다.")
FieldError
: 오류 발생시 사용자 입력 값을 저장하는 기능을 제공한다. rejectedValue
: 오류 발생시 사용자 입력 값을 저장하는 필드bindingFailure
: 타입 오류 같은 바인딩이 실패했는지 여부 저장.th:field="*{price}"
타임리프의 th:field
동작은 다음과 같다.
FieldError
에서 보관한 값을 사용해서 값을 출력한다.타입 오류로 바인딩에 실패하면 스프링은 FieldError
를 생성하면서 사용자가 입력한 값을 넣어둔다.
그리고 해당 오류를 BindingResult
에 담아서 컨트롤러를 호출한다.
→ 타입 오류 같은 바인딩 실패시에도 사용자의 오류 메시지를 정상 출력할 수 있다.
이제 오류가 발생해도 입력 값이 유지된다!
FieldError
, ObjectError
의 생성자는 errorCode
, arguments
를 제공한다.
이것은 오류 발생시 오류 코드로 메시지를 찾기 위해 사용된다!
error
메시지 파일 생성오류 메시지를 저장하기 위해 errors.properties
파일을 만들자!
required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.
totalPriceMin=가격 * 수량의 합은 {0}원 이상이어야 합니다. 현재 값 = {1}
이렇게 파일을 따로 빼두면, 하드코딩을 할 필요가 없어서 유지보수가 쉬워진다!
ValidationItemControllerV2 - addItemV3
new FieldError("item", "price", item.getPrice(), false, new String[]{"range.item.price"}, new Object[]{1000, 1000000}
codes
required.item.itemName
를 사용해서 메시지 코드를 지정한다.arguments
: Object[]{1000,1000000}
를 사용해서 코드의 {0}
, {1}
로 치환할 값을 전달한다.바로 위에서 배운 FieldError
, ObjectError
보다 더 편리한 방법에 대해 알아보자!
rejectValue()
, reject()
BindingResult
가 제공하는 rejectValue()
, reject()
를 사용하면
FieldError
, ObjectError
를 직접 생성하지 않고, 깔끔하게 검증 오류를 다룰 수 있다!
ValidationItemControllerV2
- addItemV4
rejectValue()
void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
field
: 오류 필드명errorCode
: 오류 코드 (messageResolver
를 위한 오류 코드)errorArgs
: 오류 메시지에서 {0}
을 치환하기 위한 값defaultMessage
: 오류 메시지를 찾을 수 없을 때 사용하는 기본 메시지FieldError()
를 직접 사용했을 때는,
range.item.price
와 같이 오류 코드를 모두 입력해야 했다.
rejectValue()
를 사용했을 때는,
range
처럼 간단하게 입력해도 오류 메시지를 잘 찾아서 출력해준다!