[본 포스팅은 스프링 MVC 2편 백엔드 웹 개발 핵심 기술 편을 기반으로 작성하였습니다.]
사용자에게 서비스를 제공할 때 검증은 굉장히 중요한 부분이다.
클라이언트에서 값에 대한 검증이 이루어져야 하고 서버에서도 마찬가지로 검증이 이루어져야 한다.
(이유는 클라이언트에서 값을 조작하여 보낼 수 있기 때문이다.)
기존의 검증의 로직은 다음과 같다.
//검증 로직
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, 1000000}, null);
}
if (item.getQuantity() == null || item.getQuantity() >= 9999) {
errors.rejectValue("quantity", "max", new Object[]{9999}, null);
}
이러한 방식으로 if문을 통하여 값을 체크하고 rejectValue를 통해 errors.properties를 통해 오류 메시지를 전달하는 방식이다.
하지만 Bean Validation에서는 이렇게 체크해야 하는 것을 애노테이션으로 검증하면 어떨까?라는 기똥찬 생각을 가지신 분이 만드셨다.
적용해보자
build.gradle에
implementation 'org.springframework.boot:spring-boot-starter-validation'
이것을 추가해주고 코끼리 눌러주고 설치를 해준다.
그리고 Item.java라는 데이터 클래스에 적용을 해보면
기존 코드
item.java
@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;
}
}
변경된 코드
item.java
@Data
public class Item {
private Long id;
@NotBlank(message = "공백X") //빈값 + 공백만 있는 경우를 허용하지 않는다.
private String itemName;
@NotNull //null을 허용하지 않는다.
@Range(min = 1000, max = 1000000) //범위 안의 값이어야 한다.
private Integer price;
@NotNull
@Max(9999) //최대 9999까지만 허용한다.
private Integer quantity;
public Item() {
}
public Item(String itemName, Integer price, Integer quantity) {
this.itemName = itemName;
this.price = price;
this.quantity = quantity;
}
}
이렇게 애노테이션이 붙어져 변경된다.
사용해보자
Test코드를 통해 작성해보면
BeanValidationTest.java
@Test
void beanValidation() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Item item = new Item();
item.setItemName(" "); //공백
item.setPrice(0);
item.setQuantity(10000);
Set<ConstraintViolation<Item>> violations = validator.validate(item);
for (ConstraintViolation<Item> violation : violations) {
System.out.println("violation = " + violation);
System.out.println("violation = " + violation.getMessage());
}
}
실행 결과)
이렇게 출력되는 것을 볼 수 있다.
(메시지는 기본으로 제공하는 메시지이며 수정할 수 있다.)
하지만 이후 스프링과 통합하면 우리가 직접 이런 코드를 작성하지 않으므로 참고만 하면 된다.
그렇다면 스프링에서는 어떻게 사용할까?
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
//특정 필드가 아닌 복합 룰 검증
if (item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice < 10000) {
bindingResult.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
}
}
//검증에 실패하면 다시 입력 폼으로
if (bindingResult.hasErrors()) {
log.info("errors = {}", bindingResult);
return "validation/v3/addForm";
}
//성공 로직
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/validation/v3/items/{itemId}";
}
위 코드를 보면 스프링에서는 매개변수에 @Validated를 넣어주기만 하면 된다!
(@Valid로 넣어줘도 됨, 스프링 전용 검증 애노테이션(@Validation)을 사용할 것인지 자바 표준 검증 애노테이션(@Valid)를 사용할 것인지 차이)
내가 강의를 들으며 이해한 순서는
- Validated가 Item이라는 데이터 클래스에 간다.
- 애노테이션을 확인하고 검증을 한다.
- 검증된 결과가 BindingResult에 담긴다.
이렇게 실행이 된다.
강의자료의 검증 순서의 경우
- @ModelAttribute 각각의 필드에 타입 변환 시도
1-1. 성공하면 다음으로
1-2. 실패하면 typeMismatch로 FieldError 추가 - Validator 적용
예)
- itemName에 문자 'A' 입력 -> 타입 변환 성공 -> itemName 필드에 BeanValidation 적용
- price에 문자 'A' 입력 -> 'A'를 숫자 타입 변환 시도 실패 -> typeMismatch FieldError 추가(타임리프에서 오류메시지를 errors.properties에서 찾아서 해줌) -> price 필드는 BeanValidation 적용 X
errors.properties에
#Bean Validation 추가
NotBlank={0} 공백X
Range={0}, {2} ~ {1} 허용
Max={0}, 최대 {1}
추가 후 실행해보면
이렇게 검증 메시지를 출력해준다.
'JAVA > Spring' 카테고리의 다른 글
[Spring] 로그인에 필요한 쿠키, 세션 생성 및 사용법 (서블릿 HTTP 세션) (1) | 2022.09.20 |
---|---|
[Spring] Bean Validation의 한계, 해결하기 위한 방법 두 가지(groups, Form 나누기) (0) | 2022.09.19 |
[Spring] MVC 패턴의 개요 (feat. 사용 예시) (0) | 2022.09.07 |
[Spring] Bean 스코프의 종류와 사용법 (싱글톤, 프로토타입) (1) | 2022.09.02 |
[Spring] 롬복(Lombok) 설치 및 간단하게 사용 (feat. 최신 트렌드) (0) | 2022.09.02 |