스프링/4. 스프링 MVC-2

35. 검증로직 분리

sdafdq 2023. 9. 2. 16:05

사실 역할이 있는 컨트롤러에 떡하니 검증 로직이 있는것도 마음에 안들었다.

 

예를 들면 addUser면 addUser역할만 해야 하는데 검증로직이 상당한 양을 차지한다.

 

이번에는 그냥 따로 함수로 빼는 정도일 것 같지만, 나중에는 아예 Bean으로 분리할 것 같다.

 

 

 

@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,100000000}, null);
        }
        if(item.getQuantity() == null || item.getQuantity() > 9999){
            errors.rejectValue("quantity","max", new Object[]{9999}, null);
        }


//        복합에러
        if(item.getPrice() != null && item.getQuantity() != null){
            int result = item.getQuantity() * item.getPrice();
            if(result < 10000){
                errors.reject("totalPriceMin", new Object[]{10000, result}, null);
            }
        }

    }
}

따로 클래스로 분리했다. 빈으로 등록도 했다.

 

Validator는 스프링에서 제공하는 검증관련 인터페이스이다.

 

안에는 supports와 실제 검증 메서드가 있다.

 

사실 이번 같은 경우는 검증이 필요한 클래스가 Item 하나지만, 검증이 필요한 클래스(예를 들어 고객, 상품 등)가 여러개가 될 경우 supports가 필요할 것 같다.

 

이것도 어댑터 패턴 같다.

 

솔직히 그냥 supports 보이면 어댑터 패턴 같다.

 

@Slf4j
@Controller
@RequestMapping("/validation/v2/items")
@RequiredArgsConstructor
public class ValidationItemControllerV2 {
    private final ItemValidator itemValidator;
    
    
    @PostMapping("/add")
    public String addItemV5(@ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {
        //검증 오류 보관

        if(bindingResult.hasErrors()){
            log.info("errors = {}", bindingResult);
            return "/validation/v2/addForm";
        }

        if(itemValidator.supports(item.getClass())){
            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}";
    }
}

이제 컨트롤러의 클래스에 구현한 ItemValidator를 추가했다.

그럼 내가 ItemValidator는 @Component로 빈으로 등록해놨기 때문에, 

 

알아서 ItemValidator가 의존주입을 통해 여기 안으로 들어오게 될 것이다.

 

왜냐하면 @RequiredArgsConstructor 덕분에 자동으로 final한 것을 인자로 받는 생성자가 만들어졌기 때문.

 

if(itemValidator.supports(item.getClass())){
     itemValidator.validate(item, bindingResult);
}

 

이 부분도 사실 하나라 supports로 검사할 필요는 없다.

근데 그냥 해 봤다.

만약 검증해야 할 클래스가 여러 종류라면, 아마 

private final ItemValidator itemValidator; 이 부분을 ItemValidator가 아니라,

private final Validator[] validators;

 

이렇게 해서 리스트를 만들어, 루프를 돌며 확인하는 이 어댑터 패턴으로 만들어 졌을 것이다.

(여러 빈들을 어떻게 리스트로 만들지? 하긴 이것도 그냥 스프링이 저렇게 해 두면 Validator 추가시켜 주려나?)

 

 

여튼 그냥 

itemValidator.validate(item, bindingResult); 해서 로직 진행시키면 끝난다.

Errors는 BindingResult의 부모이다.

 

 

쩝...

그래도 뭔가 왜 우리가 그 컨트롤러 거치기 전에? 

왜 그 빈 생성하기 직전이랑 빈 소멸되기 전에 할 메소드 등록할 수 있는 것 처럼

그런 거 그냥 등록해서 자동으로 하게 해주는.. 그냥 인자들 확인해서 BindingResult 있으면 아 이거 검증관리 필요한 거구나 해서 그 BindingResult 앞에 있는 객체 가지고 와서 검증 로직 실행시키는 그런 거 있지 않으려나?? 있을 거 같은데.

 

 

'스프링 > 4. 스프링 MVC-2' 카테고리의 다른 글

37. 빈 검증  (0) 2023.09.02
36. 검증로직 분리 2  (0) 2023.09.02
34. 오류메시지 4  (0) 2023.09.02
33. 오류메시지 3  (0) 2023.09.02
32. 오류 메시지 2  (0) 2023.09.02