안녕하세요.
인프런 김영한 님의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션 4 검증 - Validation 리뷰입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 강의 - 인프런
웹 애플리케이션 개발에 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. MVC 2편에서는 MVC 1편의 핵심 원리와 구조 위에 실무 웹 개발에 필요한 모든 활용 기술들을 학습할 수 있
www.inflearn.com
Vaildator 분리 1
복잡한 검증로직을 별도로 분리하자
ItemValidator 생성
package hello.itemservice.web.validation;
import hello.itemservice.domain.item.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Slf4j
@Component
public class ItemValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Item.class.isAssignableFrom(clazz);
// item == clazz
// item -- subItem 자식클래스
}
@Override
public void validate(Object target, Errors errors) {
Item item = (Item) target;
// 아래의 bindingResult.rejectValue 같다 , 단순한기능만 제공한다.
// ValidationUtils.rejectIfEmptyOrWhitespace(bindingResult, "itemName", required);
// 검증 로적
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 (item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice < 10000) {
// bindingResult.addError(new ObjectError("item",new String[]{"totalPriceMin"},new Object[]{10000, resultPrice}, null));
errors.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
}
}
}
}
addItemV4를 복사해서 addItemV5 추가
private final ItemValidator itemValidator;
@PostMapping("/add")
public String addItemV5(@ModelAttribute Item item,BindingResult bindingResult, // 순서 중요
RedirectAttributes redirectAttributes,
Model model
) {
itemValidator.validate(item, bindingResult);
// 검증에 실패하면 다시 입력 폼으로 이동
if (bindingResult.hasErrors()) {
log.info("bindingResult error ==> {}", 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}";
}
ItemValidator` 를 스프링 빈으로 주입 받아서 직접 호출했다.
실행
실행해보면 기존과 완전히 동일하게 동작하는 것을 확인할 수 있다. 검증과 관련된 부분이 깔끔하게 분리되었다.
Vaildator 분리 2
스프링이 `Validator` 인터페이스를 별도로 제공하는 이유는 체계적으로 검증 기능을 도입하기 위해서다. 그런데 앞 에서는 검증기를 직접 불러서 사용했고, 이렇게 사용해도 된다. 그런데 `Validator` 인터페이스를 사용해서 검증기를 만들면 스프링의 추가적인 도움을 받을 수 있다.
WebDataBinder를 통해서 사용하기
`WebDataBinder` 는 스프링의 파라미터 바인딩의 역할을 해주고 검증 기능도 내부에 포함한다.
너무 깊게 알 필요는 없다.
ValidationItemControllerV2 에 추가
/**
* ValidationItemControllerV2 으로 실행되었을때 아래의 메소드들이 실행되면 실행될 메소드마다
* init를 실행한다.
* */
@InitBinder
public void init(WebDataBinder dataBinder) {
dataBinder.addValidators(itemValidator);
}
이렇게 `WebDataBinder` 에 검증기를 추가하면 해당 컨트롤러에서는 검증기를 자동으로 적용할 수 있다.
`@InitBinder` 해당 컨트롤러에만 영향을 준다. 글로벌 설정은 별도로 해야한다.
@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, // 순서 중요
RedirectAttributes redirectAttributes,
Model model
) {
// 검증에 실패하면 다시 입력 폼으로 이동
if (bindingResult.hasErrors()) {
log.info("bindingResult error ==> {}", 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}";
}
동작 방식
`@Validated` 는 검증기를 실행하라는 애노테이션이다.
이 애노테이션이 붙으면 앞서 `WebDataBinder` 에 등록한 검증기를 찾아서 실행한다. 그런데 여러 검증기를 등록한다 면 그 중에 어떤 검증기가 실행되어야 할지 구분이 필요하다. 이때 `supports()` 가 사용된다. 여기서는
`supports(Item.class)` 호출되고, 결과가 `true` 이므로 `ItemValidator` 의 `validate()` 가 호출된다.
global 적용
package hello.itemservice;
import hello.itemservice.web.validation.ItemValidator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class ItemServiceApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(ItemServiceApplication.class, args);
}
public ItemServiceApplication getInstance() {
return new ItemValidator();
}
}
참고
검증시 `@Validated` `@Valid` 둘다 사용가능하다.
`javax.validation.@Valid` 를 사용하려면 `build.gradle` 의존관계 추가가 필요하다. `implementation 'org.springframework.boot:spring-boot-starter-validation'` `@Validated` 는 스프링 전용 검증 애노테이션이고, `@Valid` 는 자바 표준 검증 애노테이션이다.
'강의&책 리뷰 > 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술' 카테고리의 다른 글
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 / 섹션5. 로그인 처리1 - 쿠키,세션(1) (2) | 2024.03.11 |
---|---|
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 / 섹션4. 검증2 - Bean Validation (2) (1) | 2024.02.18 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 / 섹션4. 검증2 - Bean Validation (1) (1) | 2024.02.13 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 / 섹션4. 검증1 - Validation (2) (1) | 2024.02.05 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 / 섹션4. 검증1 - Validation (1) (2) | 2024.02.01 |