오봉이와 함께하는 개발 블로그

스프링 MVC 2 - Bean Validator 스프링 적용 본문

BE/Spring

스프링 MVC 2 - Bean Validator 스프링 적용

오봉봉이 2022. 8. 24. 18:17
728x90

Bean Validation - 스프링 적용

ValidationItemControllerV3 코드 수정

@Controller
@RequestMapping("/validation/v3/items")
@RequiredArgsConstructor
@Slf4j
public class ValidationItemControllerV3 {

    private final ItemRepository itemRepository;

    @GetMapping
    public String items(Model model) {
        List<Item> items = itemRepository.findAll();
        model.addAttribute("items", items);
        return "validation/v3/items";
    }

    @GetMapping("/{itemId}")
    public String item(@PathVariable long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "validation/v3/item";
    }

    @GetMapping("/add")
    public String addForm(Model model) {
        model.addAttribute("item", new Item());
        return "validation/v3/addForm";
    }

    @PostMapping("/add")
    public String addItem(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
        // 검증에 실패하면 다시 입력 폼으로
        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}";
    }

    @GetMapping("/{itemId}/edit")
    public String editForm(@PathVariable Long itemId, Model model) {
        Item item = itemRepository.findById(itemId);
        model.addAttribute("item", item);
        return "validation/v3/editForm";
    }

    @PostMapping("/{itemId}/edit")
    public String edit(@PathVariable Long itemId, @ModelAttribute Item item) {
        itemRepository.update(itemId, item);
        return "redirect:/validation/v3/items/{itemId}";
    }

}

코드를 변경하고 실행하면 정상적으로 동작하는 것을 확인할 수 있다.

스프링 MVC는 어떻게 Bean Validator를 사용할까

스프링 부트가 spring-boot-starter-validation 라이브러리를 넣으면 자동으로 Bean Validator를 인지하고 스프링에 통합한다.

스프링 부트는 자동으로 글로벌 Validator로 등록한다.

LocalValidatorFactoryBean을 글로벌 Validator로 등록한다.
이 Validator는 @NotNull같은 어노테이션을 보고 검증을 수행한다.
글로벌 Validator가 적용되어 있기 때문에, @Valid, @Validated만 적용하면 된다.
검증 오류가 발생하면 FieldError, ObjectError를 생성해서 BindingResult에 담아준다.

주의
글로벌 Validator를 직접 등록하면 스프링 부트는 Bean Validator를 글로벌 Validator로 등록하지 않기 때문에 어노테이션 기반의 빈 검증기가 동작하지 않는다.

참고

검증시 @Validated @Valid 둘다 사용가능하다.
javax.validation.@Valid를 사용하기 위해서 build.gradle의존관계 추가가 필요하다.
(implementation 'org.springframework.boot:spring-boot-starter-validation')

@Validated는 스프링 전용 검증 애노테이션이고, @Valid는 자바 표준 검증 애노테이션이다. 둘중 아무거나 사용해도 동일하게 작동하지만, @Validated는 내부에 groups라는 기능을 포함하고 있다.

검증 순서

  1. @ModelAttribute 각각의 필드에 타입 변환 시도
    1. 성공하면 다음
    2. 실패하면 typeMismatch로 FieldError추가
  2. Validator 적용

바인딩에 성공한 필드만 Bean Validation 적용
BeanValidator는 바인딩(타입 변환)에 실패한 필드는 BeanValidator를 적용하지 않는다.
모델 객체에 바인딩 받는 값이 정상으로 들어와야 검증도 의미가 있기 때문이다.

예시

  1. 성공
    1. itemName에 문자 "A"입력
    2. 타입 변환 성공
    3. BeanValidation 적용
  2. 실패
    1. price에 문자 "A"입력
    2. 문자를 숫자 타입 변환 시도 실패
    3. typeMismatch FieldError 추가
    4. price 필드는 BeanValidation 적용 X
출처 : 인프런 김영한 지식공유자님 강의 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
728x90
Comments