오봉이와 함께하는 개발 블로그
스프링 MVC 2 - Validator 분리 본문
Validator 분리 1
컨트롤러에 있는 검증 로직을 별도로 분리하는 것이 유지보수 측면에서 더 좋을 것이다.
또 분리를 하면 검증 로직을 재사용 할 수도 있다.
ItemValidator
@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;
// errors는 BindingResult의 부모 클래스
// 검증 로직
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, 10000000}, null);
}
if(item.getQuantity() == null || item.getQuantity() > 9999) {
errors.rejectValue("quantity", "max", new Object[]{1000}, null);
}
// 특정 필드가 아닌 복합 룰 검증
if(item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if(resultPrice < 10000) {
errors.reject("totalPriceMin", new Object[]{10000, resultPrice}, null);
}
}
}
}
스프링을 검증을 체계적으로 제공하기 위해 인터페이스를 제공한다.
public interface Validator {
boolean supports(Class<?> clazz);
void validate(Object target, Errors errors);
}
supports() {}
: 해당 검증기를 지원하는 여부 확인validate(Object target, Errors errors)
: 검증 대상 객체(Item)와 BindingResult(Errors의 자식 클래스)
ValidationItemControllerV2 - addItemV5()
@Controller
@RequestMapping("/validation/v2/items")
@RequiredArgsConstructor
@Slf4j
public class ValidationItemControllerV2 {
private final ItemRepository itemRepository;
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("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}";
}
// 나머지 코드들
}
Validator 분리 2
스프링이 Validator
인터페이스를 별도로 제공하는 이유는 체계적으로 검증 기능을 도입하기 위해서다.
위에서는 검증기를 직접 불러서 사용했지만(물론 이렇게 사용해도 된다.) Validator
인터페이스를 사용해서 검증기를 만들면 스프링의 추가적인 도움을 받을 수 있다.
WebDataBinder를 통해서 사용하기WebDataBinder
는 스프링의 파라미터 바인딩의 역할을 해주고 검증 기능도 내부에 포함한다.
ValidationItemControllerV2
@InitBinder
public void init(WebDataBinder webDataBinder) {
log.info("init binder {}", webDataBinder);
webDataBinder.addValidators(itemValidator);
}
@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
// 검증에 실패하면 다시 입력 폼으로
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}";
}
WebDataBinder
에 검증기를 추가하면 해당 컨트롤러가 호출될 때 자동으로 검증기를 돌려줘 자동으로 적용되며 요청이 올 때 마다 새로 만들어지는 객체다.@InitBinder
는 해당 컨트롤러에만 영향을 주기 때문에 글로벌 설정은 별도로 해야한다.
addItemV6
에는 validator를 직접 호출하는 부분이 사라지고, 대신에 검증 대상 앞에 @Validated
가 붙었다.
동작 방식@Validated
는 검증기를 실행하라는 어노테이션이다.
이 어노테이션이 붙으면 WebDataBinder
에 등록한 검증기를 찾아서 실행한다.
그런데 여러 검증기를 등록한다면 그 중에 어떤 검증기가 실행되어야 할지 구분이 필요한데, 이 때 supports()
가 사용된다.
여기서는 supports(Item.class)
호출되고, 결과가 true
이므로 ItemValidator
의 validate()
가 호출된다.
글로벌 설정 - 모든 컨트롤러에 다 적용
@SpringBootApplication
public class ItemServiceApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(ItemServiceApplication.class, args);
}
@Override
public Validator getValidator() {
return new ItemValidator();
}
}
이렇게 글로벌 설정을 추가할 수 있다.
기존 컨트롤러의 @InitBinder
를 제거해도 글로벌 설정으로 정상 동작하는 것을 확인할 수 있다.
참고로 이런 글로벌 설정을 사용하는 경우는 드물다.
참고
검증시 @Validated
@Valid
둘 다 사용 가능하다.javax.validation.@Valid
를 사용하기 위해서는 build.gradle
의존관계 추가가 필요하다.implementation 'org.springframework.boot:spring-boot-starter-validation'
@Validated
는 스프링 전용 검증 어노테이션이고, @Valid
는 자바 표준 검증 어노테이션이다.
출처 : 인프런 김영한 지식공유자님 강의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
'BE > Spring' 카테고리의 다른 글
스프링 MVC 2 - Bean Validator 스프링 적용 (0) | 2022.08.24 |
---|---|
스프링 MVC 2 - Bean Validation 소개, 시작 (0) | 2022.08.24 |
스프링 MVC 2 - 오류 코드와 메시지 처리 part.3 (0) | 2022.08.23 |
스프링 MVC 2 - 오류 코드와 메시지 처리 part.2 (0) | 2022.08.23 |
스프링 MVC 2 - 오류 코드와 메시지 처리 part.1 (0) | 2022.08.23 |