BindingResult
@PostMapping("/add")
public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult,
RedirectAttributes redirectAttributes)
다음과 같이 검증할 객체 뒤에 파라미터를 위치하여 사용한다.
객체를 `@ModelAttribute` 에 바인딩하는 과정에서 어떠한 검증과정을 거칠 때 오류가 나면 그 오류를 `BindingResult` 에 저장한다.
그리고 `FieldError`, `ObjectError` 생성자를 제공하는데 필드 또는 객체 자체에 오류가 났을 때 그 정보를 객체로 생성하여 `addError` 를 통해 `bindingResult` 에 값을 담을 수 있다.
`FieldError`, `ObjectError`의 생성자 파라미터 목록을 보면 대충 어떤 생성자인지 알 수 있다.
- objectName: 오류가 발생한 객체 이름
- field: 오류 필드
- rejectedValue: 사용자가 입력한 값
- bindingFailure: 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분
- codes: 메세지 코드
- arguments: 메세지에서 사용하는 인자
- defaultMessage: 기본 오류 메세지
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.addError(new FieldError("item",
"itemName",
item.getItemName(),
false,
new String[]{"required.item.itemName"},
null,
null));
}
다음과 같이 사용할 수 있는데 딱 봐도 생성자에 필드가 너무 많아 코드가 번잡하다. 이걸 깔끔하게 줄여주는 메서드도 제공되는데
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.rejectValue("itemName", "required");
}
rejectValue() 메서드로 다음과 같이 코드를 줄일 수 있다. 생각해보면 BindingResult는 어떤 객체를 대상으로 검증하는지 target을 이밎 알고 있어서 그에 관한 정보는 없어도 되기 때문에 고대 개발자들이 다음은 메서드를 제공해 줄일 수 있게 만든 것이다. (사실 이것 또한 이제는 유물과 같은 존재가 된 것 같다.)
rejectValue() 의 파라미터
- field: 오류 필드명
- errorCode: 오류 코드
- errorArgs: 오류 메세지에서 {값} 을 치환하기 위한 값
- defaultMessage: 기본 메세지
bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null)
위처럼 쓸 수 있다는 것을 알아두자.
Bean Validation
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, 10000}, null);
}
if (item.getQuantity() == null || item.getQuantity() >= 10000) {
errors.rejectValue("quantity", "max", new Object[]{9999}, null);
}
위처럼 검증을 하려면 필드 하나하나에 대해서 조건문을 걸어 로직을 다 짜줘야하는데 상당히 번거롭다.
그래서 Bean Validation 이라는 필드에 에노테이션 형태로 코드를 추가해 훨씬 간편하게 검증을 할 수 있다. 한번 비교를 해보자
public class Item {
@NotBlank
private String itemName;
@NotNull
@Range(min = 1000, max = 1000000)
private Integer price;
@NotNull
@Max(9999)
private Integer quantity;
}
사실 너무 직관적이라 별다른 설명이 필요없다. 이렇게 적어주면 위처럼 IF문으로 조건 작성할 필요가 없는 것이다. 그럼 컨트롤러의 메서드에선 이걸 어떻게 사용하는지 보자.
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute Item item, BindingResult
bindingResult, RedirectAttributes redirectAttributes) {
}
그냥 객체 앞에 @Vaild 또는 @Validated 만 넣어주면 끝이다. 이렇게만 해주면 스프링 부트에서 알아서 Validator 를 등록 해줘서 적용된다. 여기서 검증 오류가 발생하면 아까 봤던 FieldError, ObjectError 를 생성해서 BindingResult 에 담아준다.
참고로 @Validated 는 스프링 전용 검증 에노테이션, @Vaild 는 자바 표준 검증 에노테이션인데 @Validated 는 groups 기능이 포함되어있다. groups 는 컨트롤러에 객체를 받는 여러 메서드들이 있을 것이다. 근데 이걸 각각 다르게 검증받고 싶을 때 사용하는 것인데 실무에선 이것은 잘 사용하지 않고 각자 Dto 객체를 만들어서 관리한다고 한다.
'개발 > 내일배움캠프 TIL' 카테고리의 다른 글
Spring 뉴스피드 프로젝트 미니 발제 KPT 회고 (0) | 2024.12.27 |
---|---|
[TIL #37] JPA를 이용한 일정 과제를 하면서 배운 내용 + 트러블 슈팅 기록 (0) | 2024.12.18 |
[TIL #35] 빈 생명주기 콜백 (0) | 2024.12.06 |
[TIL #33] 컴포넌트 스캔 탐색 위치와 기본 스캔 대상 (0) | 2024.12.04 |
[TIL #32] Spring 일정 관리 앱 과제 트러블 슈팅 + 디버그로 원인 찾기 (0) | 2024.12.03 |