V2.7 Validator 분리

알파로그·2023년 3월 27일
0

Spring MVC 활용 기술

목록 보기
41/42

✏️ V2 의 문제점

지금까지 구현한 V2 의 method 는
비지니스 로직보다 검증로직의 길이가 더 길어 유지보수성이 매우 떨어진다.


✏️ 검증 로직 분리

  • 검증만 담당하는 별도의 객체를 생성해 Controller 의 검증 로직을 분리시킬 수 있다.
  • @Component 로 Spring Bean 으로 등록
  • Validator 를 상속받아 상황에 맞게 검증 로직을 완성시켜줄 수 있다.
    • supports
      • validate method 가 객체를 검증할 수 있는지 판단하는 로직
    • validate
      • 실질적인 검증 로직이 작동되는 method
      • 검증하려는 객체와 BindingResult 을 변수로 받는다.
      • BindingResulterror 를 상속받았기 때문에 별도 케스팅 없이 형변환이 가능하다.
      • 🔗 형변환
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) {
        // 매개변수 Class 가 Item 과 같은 타입인지 확인하는 로직
        // isAssignableFrom 은 자식 Class 까지도 true 를 반환한다.
        return Item.class.isAssignableFrom(clazz);
    }

    //-- 실질적인 검증 로직이 작동되는 method --//
    @Override
    public void validate(Object target, Errors errors) {
        // 매개변수를 Item 으로 케스팅
        Item item = (Item) target;
        
        //-- 실질적 검증로직 --//
        // Errors 는 BindingResult 의 부모 클레스 이므로
        // BindingResult 도 매개변수로 받을 수 있다.
        if (!StringUtils.hasText(item.getItemName()))
            errors.rejectValue("itemName", "required");

        if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() > 100000)
            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)
                errors.reject("totalPriceMin",
                        new Object[]{10000, resultPrice}, null);
        }

    }
}

📍 Controller 계층 - 리팩토링

  • Spring Bean 으로 등록한 ItemValidator 을 DI 한다.
@Slf4j
@Controller
@RequestMapping("/validation/v2/items")
@RequiredArgsConstructor
public class ValidationItemControllerV2 {

    private final ItemRepository itemRepository;
    private final ItemValidator itemValidator;
  • 검증로직을 모두 삭제하고,
    validate 을 호출한다.
    - validate method 내부에서 검증에 실패할경우 RedirectAttributes 에 오류코드를 저장해준다.
//-- validation 분리 --//
@PostMapping("/add")
public String addItemV5(
        @ModelAttribute Item item,
        BindingResult bindingResult,
        RedirectAttributes redirectAttributes
) {
    // 검증 로직
    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}";
}
profile
잘못된 내용 PR 환영

0개의 댓글