상품 등록시의 검증
Validation Flow
정상 로직
PRG : POST - Redirect - GET
비정상 로직
예) 상품명을 입력하지 않거나, 숫자 필드에 문자가 들어오는 경우
컨트롤러에서 검증이 실패하면 검증에 실패한 정보를 담아서 등록 폼을 다시 렌더링한다.
컨트롤러에서 모델에 잘못된 데이터까지 다시 담아서 등록 폼에 다시 전달해야 한다.
어떤 값이 잘못되어 있는지 다시 알려 주어야 한다.
StringUtils : 문자열 관련 기능을 강화한 클래스
Validation 방법
타입 오류 처리하기
타입 오류는 컨트롤러 접근 전에 500 - BAD REQUEST로 발생한다.
타입 오류가 발생해도 오류가 폼에 남아야 하지만 바인딩이 처리되지 못함
BindingResult
검증 오류를 보관하는 객체. BindingResult를 빼면 컨트롤러에서 튕겨 400 오류를 발생시킴
만약 ModelAttribute에 매핑 오류가 생기게 되면, 일단 BindingResult에 담긴다.
BindingResult에 검증오류를 적용하려면?
1. 타입 오류의 경우 FieldError를 생성해서 BindingResult에 넣어준다.
2. 개발자가 직접 넣어준다
3. Validator 사용
필드 오류
FieldError(String ObjectName[@ModelAttribute 이름], String field[오류 발생 필드 이름], String defaultMessage)
FieldError(String ObjectName[@ModelAttribute 이름], String field[오류 발생 필드 이름], @Nullable Object rejectedValue, boolean bingingFailture[바인딩 실패인지, 검증 실패인지], @Nullable String[] codes[메시지 코드], @Nullable Object[] arguments[메시지에서 사용하는 인자], String defaultMessage[기본 오류 메시지])
rejectedValue에서 오류 발생시 값을 저장하게 됨
글로벌 오류
ObjectError(String ObjectName[@ModelAttribute 이름], @Nullable String[] codes[메시지 코드], @Nullable Object[] arguments[메시지에서 사용하는 인자], String defaultMessage[기본 오류 메시지])
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
* BindingResult는 ModelAttribute 뒤에 와야 한다.
* Model에 담지 않아도 ModelAttribute에 함께 담긴다
타임리프와 함께 사용하기
#fields : #fields로 BindingResult에 접근
th:errors : 필드에 오류가 있는 경우에 태그 출력
th:errorclass : th:field 에서 지정한 필드에 오류가 있을 경우 class 정보를 추가
오류 메시지 처리
위의 FieldError, ObjectError의 인자(codes, arguments)를 활용한다
application.properties 수정하기
spring.messages.basename=messages,errors
messages.properties
required.item.itemName=상품 이름은 필수입니다.
range.item.price=가격은 {0} ~ {1} 까지 허용합니다.
max.item.quantity=수량은 최대 {0} 까지 허용합니다.
Controller 수정
bindingResult.addError(new FieldError("item", "itemName", item.getItemName(), false, new String[]{"required.item.itemName"}, null, null));
reject(), rejectValue()를 활용
BindingResult는 Target을 가지고 있다(위에서 ModelAttribute의 대상 객체)
bindingresult.getObjectName() | bindingResult.getTarget() 으로 확인가능
bindingResult.rejectValue("quantity", "max", new Object[]{9999}, null);
bindingResult.addError(new ObjectError("item", new String[]{"totalPriceMin.item"}, new Object[]{10000, resultPrice}, null));
bindingResult.reject("totalPriceMin", new Object[]{1000, resultPrice}, null);
rejectValue(errorcode, errorArgs, defaultMessage)
reject(field, errorcode, errorArgs, defaultmessage)
위와 같이 rejectValue, reject를 사용한다.
오류 메시지의 계층화
이미 정의된 메시지를 활용하기 위해서는
단순한 메시지 : 여러 곳에서 사용이 가능하다(범용성👍)
세밀한 메시지 : 작성이 어렵다(범용성👎) - 객체명 + 필드명 조합
세밀한 메시지가 "없다면" 단순한 메시지를 출력하도록 하자
예 ) 객체명+필드명 메시지 확인 -> 없다면 범용적인 메시지 출력
메시지의 설계는 구체적인 것부터 덜 구체적인 것 순서로 만들자
MessageCodeResolver 사용
Spring에서 제공하는 MessageCodesResolver는 메시지 코드들을 생성한다.
DefaultMessageCodesResolver가 기본 구현체이다.
code+objectname+field
code+field
code+field type
code
순으로 메시지가 생성된다
스프링이 직접 만든 오류 메시지 처리
스프링에서 타입 오류가 발생하면 typemismatch가 나타난다.
typeMismatch.java.lang.Integer=숫자를 입력해주세요.
typeMismatch=타입 오류입니다.
위와 같이 계층화된 메시지를 활용해 보편적인 메시지, 세부적인 메시지를 작성해 주자!
Validator 분리
로직이 복잡해질 때, Validator를 빈으로 등록해 사용한다
Spring이 제공하는 Validator를 implement 받아 구현하자
@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;
WebDataBinder를 통해서 사용한다.
Controller 내부에 @InitBinder를 사용하여 커스텀한 Validator를 넣어주자
private final ItemValidator itemValidator;
@InitBinder
public void init(WebDataBinder dataBinder){
dataBinder.addValidators(itemValidator);
}
위처럼 사용하면 init()이 선언된 컨트롤러가 호출될 때마다 validator를 생성해 준다.
@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
검증을 진행할 ModelAttribute의 앞에 @Validated 어노테이션을 붙여주어야 한다.
'Spring' 카테고리의 다른 글
MVC2 - 로그인(쿠키, 세션) (1) | 2022.10.03 |
---|---|
MVC2 - Bean Validation (0) | 2022.09.27 |
File->MultipartFile (0) | 2022.07.26 |
QueryDSL 사용 전 환경설정 (0) | 2022.07.12 |
Reactive Programming (0) | 2022.06.30 |