BackEnd/Spring Boot

Validator 분리

연향동큰손 2024. 7. 31. 15:25

 

기존의 검증 로직을 별도의 클래스로 분리하는 작업에 대해서 배웠다.

 

<기존 검증 로직>

@PostMapping("/add")
public String addItemV4(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
    log.info("objectName={}", bindingResult.getObjectName());
    log.info("target={}", bindingResult.getTarget());
    if (!StringUtils.hasText(item.getItemName())) {
        bindingResult.rejectValue("itemName", "required");
    }
    if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 1000000) {
        bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null);
    }
    if (item.getQuantity() == null || item.getQuantity() > 10000) {
        bindingResult.rejectValue("quantity", "max", new Object[]{9999}, null);
    }
    //특정 필드 예외가 아닌 전체 예외
    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/v2/addForm";
    }
    //성공 로직
    Item savedItem = itemRepository.save(item);
    redirectAttributes.addAttribute("itemId", savedItem.getId());
    redirectAttributes.addAttribute("status", true);
    return "redirect:/validation/v2/items/{itemId}";
}

 

<ItemValidator 클래스>

package hello.itemservice.web.validation;

import hello.itemservice.domain.item.Item;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;


@Component
public class ItemValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Item.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {

        Item item = (Item) target;

        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() > 10000) {
            errors.rejectValue("quantity", "max", new Object[]{9999}, 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);
            }
        }
    }
}

 

Validator 인터페이스 ==> 스프링에서 제공하는 검증 인터페이스

 

supports()  ==> 해당 검증기를 지원하는 여부 확인

 

validate(Object target, Errors errors) ==> 검증 대상 객체와 BindingResult

 

 itemValidator를 스프링 빈으로 주입 받아서 직접 호출 해야 한다.

private final ItemValidator itemValidator;

이렇게 검증 로직을 분리 해주면 Controller의 코드가 다음과 같이 매우 간단해지는 것을 확인할 수 있다.

@PostMapping("/add")
public String addItemV5(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {

    //검증로직이 ItemValidator로 넘어감
    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}";
}

 

 


검증기 자동 적용 방법 ==> WebDataBinder사용

 

Controller에 아래 코드 추가

@InitBinder
public void init(WebDataBinder dataBinder){
    dataBinder.addValidators(itemValidator);
}

 

WebDataBinder가 파라미터 바인딩의 역할을 해주고, 검증 기능도 내부에 포함한다.

위 코드를 통해 해당 컨트롤러는 검증기를 자동으로 적용할 수 있게된다.

 

 

Validated 어노테이션 추가

@PostMapping("/add")
public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes) {

    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}";
}

 

@Validated를 넣어주면 검증 대상이 된다. 따라서 검증이 필요한 곳에 @Validated를 넣어주면 된다.

 

 

실행 결과

'BackEnd > Spring Boot' 카테고리의 다른 글

BeanValidation-Form 전송 객체 분리  (1) 2024.08.05
Bean Validation  (0) 2024.08.01
검증 - 개선  (2) 2024.07.26
검증 - BindingResult  (0) 2024.07.26
검증(직접 처리)  (0) 2024.07.25