728x90
반응형
Validation
- 검증 서버에 검증로직을 추가해보자
- 한단계씩 코드를 개선해 보면서 어떻게 스프링에서 검증을 처리할수 있는지
알아본다. - 검증이 필요한 이유
- 검증을 하지 않으면 로직상 치명적인 에러를 놓치기 쉽다.
- 검증을 통해 사용자의 잘못된 값을 처리한다.
Map 으로 구현하기
- 검증이 잘못 된경우 에러를 맵에 담아 컨트롤러에서 처리를 한다.
- 모델에 에러 내용을 담아 화면으로 넘겨준다.
코드
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes
redirectAttributes, Model model) {
//Map을 이용하여 직접 에러검증하는 자료구조를 만든다.
Map<String, String> errors = new HashMap<>();
//검증 로직
if (!StringUtils.hasText(item.getItemName())) {
errors.put("itemName", "상품 이름은 필수입니다.");
}
//검증에 실패하면 다시 입력 폼으로
if (!errors.isEmpty()) {
model.addAttribute("errors", errors);
return "validation/v1/addForm";
}
//성공 로직
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v1/items/{itemId}";
}
화면
<div>
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}"
th:class="${errors?.containsKey('itemName')} ? 'form-control
field-error' : 'form-control'"
class="form-control" placeholder="이름을 입력하세요">
<div class="field-error" th:if="${errors?.containsKey('itemName')}"
th:text="${errors['itemName']}">
상품명 오류
</div>
</div>
참고사항
- errors?.containsKey('itemName')
- errors 가 null 일 경우 NullPointerException이 발생하는 대신 null을 반환한다.
문제점
- 중복처리가 많아 진다.
- 타입오류가 처리가 안된다
- 타입이 Integer일때 문자타입이 들어오는경우 등 - 사용자가 입력한 값이 사라진다.
- 사용자가 입력한 값을 별도로 저장후 보여줘야 뭐가 잘못되었는지 알수 있다,
BindingResult
- BindingResult 로 Map 을 대체하였다.
- 스프링이 제공하는 검증오류를 보관하는 객체이다.
- 검증오류 발생시 여기에 저장이 된다. - BindingResult bindingResult 파라미터의 위치는 @ModelAttribute Item item 다음에 와야 한다.
- BindingResult는 Model에 자동으로 포함이 된다.
사용이유
- BindingResult를 사용하게 되면 400 오류가 발생하지 않고 에러를 담아 컨트롤러를 정상호출한다.
- 그렇게 되면 오류페이지에 넘어가기 전에 처리를 할수 있다.
필드에러 처리
- FieldError 를 BindingResult 에 추가해주면된다.
- public FieldError(String objectName, String field, String defaultMessage) {}
- objectName: @ModelAttribute 이름
- field: 오류가 발생한 필드 이름
- defaultMessage: 오류 기본메세지
@PostMapping("/add")
public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.addError(new FieldError("item", "itemName", "상품 이름은
필수입니다.."));
}
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}";
}
글로벌 오류 처리
- 필드의 값을 넘어서는 오류가 있다면 글로벌로 처리해주자
- public ObjectError(String objectName, String defaultMessage) {}
- objectName: @ModelAttribute이름
- defaultMesssage: 오류기본 메세지
bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야
합니다. 현재 값 = " + resultPrice));
화면 에서 에러 처리
- #fields: BindingResult가 제공하는 오류에 접근 할수 있다.
- th:errors: 해당 필드에 오류가 있는 경우 태그를 출력한다.
- 오류가 없으면 해당 태그는 출력되지 않는다. - th:errorclass: 에러가 있다면 class 정보를 추가한다
<div th:if="${#fields.hasGlobalErrors()}">
<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}"
th:errorclass="field-error" class="form-control"
placeholder="이름을 입력하세요">
<div class="field-error" th:errors="*{itemName}">
상품명 오류
</div>
</div>
BindingResult 와 Errors
- BindingResult는 인터페이스이다, Errors 인터페이스를 상속받고있다.
- 실제 넘어오는 구현체는 BeanPropertyBindingResult 이다. - 둘다 구현이하고 있기 때문에 BindingResult 대신 Errors 사용해도 된다.
- 하지만 관례상 BindigResult를 많이 사용한다.
FieldError, ObjectError
- 위 예제 들에서는 사용자가 입력한 값이 날라가는 문제가 있다.
FiedlError 와 ObjectError를 사용하여 이를 해결해 보자 - 타임리프에서는 th:field가 오류발생을 감지 하여 해당값을 출력해주는 역할도 한다.
FieldError
- 입력데이터가 컨트롤러의 @ModelAttribute 에 바인딩되는 시점에 오류발생시
모델 객체에 사용자 입력 값을 유지하는것은 어렵다. - 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: 메시지에서 사용하는 인자.
- defaultMessage: 기본 오류 메시지
예제
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.addError(new FieldError("item", "itemName",
item.getItemName(), false, null, null, "상품 이름은 필수입니다."));
}
참고:
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
728x90
반응형
'Spring > spring mvc 2 스터디' 카테고리의 다른 글
[Spring] Validation: Validator (0) | 2022.02.02 |
---|---|
[Spring] Validation: 오류코드, 메시지 처리 (0) | 2022.02.01 |
[Spring] thymeleaf 예제 (0) | 2022.01.06 |
[Spring] thymeleaf 기본 문법 (0) | 2021.12.19 |
[Spring] thymeleaf 개요 (0) | 2021.12.16 |
댓글