Spring/Spring MVC

스프링 MVC Validation - BindingResult, Field Error, Object Error

taey 2024. 10. 1. 03:07

BindingResult

스프링이 제공하는 검증 오류를 보관하는 객체, 검증 오류가 발생하면 여기에 보관한다.

 

예제 코드

@PostMapping("/add")
public String addItemV1(@ModelAttribute Item item, BindingResult bindingResult, 
RedirectAttributes redirectAttributes) {
    if (!StringUtils.hasText(item.getItemName())) {
		bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수입니다."));
	}
     
	if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() >1000000) {
		bindingResult.addError(new FieldError("item", "price", "가격은 1,000 ~ 1,000,000 까지 허용합니다."));
	}
     
	if (item.getQuantity() == null || item.getQuantity() >= 10000) {
		bindingResult.addError(new FieldError("item", "quantity", "수량은 최대 9,999 까지 허용합니다."));
	}
     
	//특정 필드 예외가 아닌 전체 예외
	if (item.getPrice() != null && item.getQuantity() != null) {
		int resultPrice = item.getPrice() * item.getQuantity();
		if (resultPrice < 10000) {
			bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));
     	}
     }
     
     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}";
}

 

BindingResult bindingResult 파라미터의 위치는 @ModelAttribute Item item 다음에 와야 한다.

 

필드 오류 - FieldError

if (!StringUtils.hasText(item.getItemName())) {
	bindingResult.addError(new FieldError("item", "itemName", "상품 이름은 필수입니다."));
}

 

FieldError 생성자 요약

public FieldError(String objectName, String field, String defaultMessage) {}

 

필드에 오류가 있으면 FieldError 객체를 생성해서 bindingResult에 담아두면 된다.

  • objectName : @ModelAttribute 이름
  • field : 오류가 발생한 필드 이름
  • defaultMessage : 오류 기본 메시지

 

글로벌 오류 - ObjectError

bindingResult.addError(new ObjectError("item", "가격 * 수량의 합은 10,000원 이상이어야 합니다. 현재 값 = " + resultPrice));

 

ObjectError 생성자 요약

public ObjectError(String objectName, String defaultMessage) {}

 

특정 필드를 넘어서는 오류가 있으면 ObjectError 객체를 생성해서 bindingResult에 담아두면 된다.

  • objectName : @ModelAttribute의 이름
  • defaultMessage : 오류 기본 메시지

BindingResult의 유무

BindingResult가 있으면 @ModelAttribute에 데이터 바인딩 시 오류가 발생해도 컨트롤러가 호출된다.

 

예) @ModelAttribute에 바인딩 시 타입 오류가 발생하면?

  • BindingResult가 없으면 → 400 오류가 발생하면서 컨트롤러가 호출되지 않고, 오류 페이지로 이동한다.
  • BindingResult가 있으면 → 오류 정보(FieldError)를 BindingResult에 담아서 컨트롤러를 정상 호출한다.

 

BindingResult에 검증 오류를 적용하는 3가지 방법

  • @ModelAttribute의 객체에 타입 오류 등으로 바인딩이 실패하는 경우 스프링이 FieldError 생성해서 BindingResult에 넣어준다.
  • 개발자가 직접 넣어준다.
  • Validator 사용

 

타입 오류 확인

주의

  • BindingResult는 검증할 대상 바로 다음에 와야 한다. 순서가 중요하다. 
  • BindingResult는 Model에 자동으로 포함된다.

 

BindingResult와 Errors

  • org.springframework.validation.Errors
  • org.springframework.validation.BindingResult

BindingResult는 인터페이스이고, Errors 인터페이스를 상속받고 있다. 실제 넘어오는 구현체는 BeanPropertyBindingResult라는 것인데, 둘 다 구현하고 있으므로 BindingResult 대신에 Errors를 사용해도 된다. Errors  인터페이스는 단순한 오류 저장과 조회 기능을 제공한다. BindingResult는 여기에 더해서 추가적인 기능들을 제공한다. addError()도 BindingResult가 제공하므로 주로 BindingResult를 많이 사용한다.

 


 

FieldError 생성자

FieldError는 두 가지 생성자를 제공한다.

public FieldError(String objectName, String field, String defaultMessage);

public FieldError(String objectName, String field, @Nullable Object rejectedValue,
boolean bindingFailure, @Nullable String[] codes, @Nullable Object[] arguments, @Nullable String defaultMessage)

 

파라미터 목록

  • objectName : 오류가 발생한 객체 이름
  • field : 오류 필드
  • rejectedValue : 사용자가 입력한 값 (거절된 값)
  • bindingFailure : 타입 오류 같은 바인딩 실패인지, 검증 실패인지 구분 값
  • codes : 메시지 코드
  • arguments : 메시지에서 사용하는 인자
  • defaultMessage : 기본 오류 메시지

ObjectError도 유사하게 두 가지 생성자를 제공한다

 

오류 발생 시 사용자 입력 값 유지

사용자의 입력 데이터가 컨트롤러의 @ModelAttribute에 바인딩 되는 시점에 오류가 발생하면 모델 객체에 사용자 입력 값을 유지하기 어렵다. 여기서 rejectedValue가 바로 오류 발생 시 사용자 입력 값을 저장하는 필드다.

bindingFailure는 타입 오류 같은 바인딩이 실패했는지 여부를 적어주면 된다. 

 

스프링의 바인딩 오류 처리

타입 오류로 바인딩에 실패하면 스프링은 FieldError를 생성하면서 사용자가 입력한 값을 넣어둔다. 그리고 해당 오류를 BindingResult에 담아서 컨트롤러를 호출한다. 따라서 타입 오류 같은 바인딩 실패 시에도 사용자의 오류 메시지를 정상 출력할 수 있다.

'Spring > Spring MVC' 카테고리의 다른 글

스프링 MVC - Validator 분리  (2) 2024.10.01
스프링 MVC 오류 코드와 메시지 처리  (1) 2024.10.01
스프링 메세지, 국제화  (3) 2024.10.01
스프링 MVC HTTP 메시지 컨버터  (2) 2024.09.30
스프링 MVC 응답  (0) 2024.09.30