Validator 분리

JeongHoHyun·2025년 2월 11일

Spring MVC

목록 보기
11/21

Validator 분리

컨트롤러에서 검증 로직이 차지하는 부분은 매우 크다. 이런경우 별도의 클래스로 역할을 분리하는것이 좋다.
이렇게 분리하면 검증 로직을 재사용할 수도 있다.

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<?> aClass) {
        return Item.class.isAssignableFrom(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
        Item item = (Item) o;

        // 검증 로직
        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() >= 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);
            }
        }
    }
}
  • 스프링은 검증을 체계적으로 제공하기 위해 Validator인터페이스를 제공한다.
public interface Validator {
boolean supports(Class<?> clazz);
void validate(Object target, Errors errors);
}
  • supports() {} : 해당 검증기를 지원하는 여부 확인
  • validate(Object target, Errors errors) : 검증 대상 객체와 BindingResult

WebDataBinder (@InitBinder)

@InitBinder
    public void init(WebDataBinder binder) {
        binder.addValidators(itemValidator);
    }
  • WebDataBinder는 스프링의 파라미터 바인딩의 역할을 해주고 검증 기능도 내부에 포함한다.
  • 위와 같이 검증기를 추가하면 해당 컨트롤러에서는 검증기를 자동으로 적용할 수 있다.
  • @InitBinder -> 해당 컨트롤러에만 영향을 준다. 글로벌 설정은 별도로 해야한다.
@PostMapping("/add")
    public String addItemV6(@Validated @ModelAttribute Item item, BindingResult bindingResult, RedirectAttributes redirectAttributes, Model model) {

        // 검증에 실패하면 다시 입력 폼으로
        if (bindingResult.hasErrors()) {
            log.error("errors = {}", bindingResult);
            // bindingResult 는 model에 담지 않아도 뷰로 넘어간다.
            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는 검증기를 실행하라는 애노테이션이다.
  • 이 애노테이션이 붙으면 WebDataBinder에 등록한 검증기를 찾아서 실행한다.
  • 여러 검증기를 등록한다면 supports()가 호출되고, 이결과가 true인 검증기의 validate()가 호출된다.

Validator 글로벌 설정 (모든 컨트롤러 적용)

@SpringBootApplication
public class ItemServiceApplication implements WebMvcConfigurer {

	public static void main(String[] args) {
		SpringApplication.run(ItemServiceApplication.class, args);
	}
    
	@Override
	public Validator getValidator() {
		return new ItemValidator();
	}
}
profile
Java Back-End 2022.11.01 💻~ing

0개의 댓글