둘셋 개발!

[Spring] BindResult 객체 본문

SPRING

[Spring] BindResult 객체

23 2023. 8. 8. 00:49

BindResult는 Controller 메소드에서 @ModelAttribute에서 객체 바인딩 시 타입 검증에 실패하면 에러정보를 담는 인터페이스이다.

 

 

사용예시를 보면,

 

Item.java

import lombok.Data;

@Data
public class Item {

    private Long id;
    private String itemName;
    private Integer price;
    private Integer quantity;

    public Item() {
    }

    public Item(String itemName, Integer price, Integer quantity) {
        this.itemName = itemName;
        this.price = price;
        this.quantity = quantity;
    }
}

 

Controller.java

@PostMapping("/add")
public String addItem3(@ModelAttribute Item item, BindingResult bindingResult, Model model) {
    List<FieldError> fieldErrors = bindingResult.getFieldErrors();
    for (FieldError error:fieldErrors) {
        log.info("error={}", error);
    }
    /// ...
}

⭐️⭐️ BindingResult는 @ModelAttribute 바로 뒤에 나와야 한다.

 

 

http 요청 

content-type: multipart/form-data
message body: itemName=iteam1&price=abc&quantity=3

 

결과 (로그)

error=Field error in object 'item' on field 'price': rejected value [abc];
codes [typeMismatch.item.price,typeMismatch.price,typeMismatch.java.lang.Integer,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [item.price,price];
arguments [];
default message [price]];
default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Integer' for property 'price'; 
nested exception is java.lang.NumberFormatException: For input string: "abc"]

로그를 보면 어떤 객체(Item)의 어떤 필드(price)가 어떠한 이유(typeMismatich)로 오류가 발생했는지 알 수 있고

어떤 값을 바인딩 하려고 시도했는지도 볼 수 있다.

 

이렇게 나올 수 있는 이유는 스프링이 @ModelAttribute의 객체에 타입 오류 등으로 바인딩에 실패했을 경우

FieldError를 생성해서 BindingResult에 넣어줬기 때문이다. 

 

 

 

여기서 FieldError을 잠깐 살펴볼까?

public class FieldError extends ObjectError {

	// 생성자 1
	public FieldError(String objectName, String field, String defaultMessage) {
		this(objectName, field, null, false, null, null, defaultMessage);
	}


	// 생성자 2
	public FieldError(String objectName, String field, @Nullable Object rejectedValue, boolean bindingFailure,
			@Nullable String[] codes, @Nullable Object[] arguments, @Nullable String defaultMessage) {

		super(objectName, codes, arguments, defaultMessage);
		Assert.notNull(field, "Field must not be null");
		this.field = field;
		this.rejectedValue = rejectedValue;
		this.bindingFailure = bindingFailure;
	} 
}

- objectName:  @ModelAttribute 이름

- field: 오류가 발생한 필드 이름

- defaultMessage: 오류 기본 메세지

- rejectedValue: 거절된 값

- bindingFailure: 타입 오류 실패인지, 검증 실패인지

- codes: 메시지 코드

- arguments: 메세지에서 사용하는 인자 

 

그리고 이렇게 bindingResult가 만들어지면 자동으로 Model에 담겨져서 View로 넘어간다.

이것도 확인해볼까?

 

 

 

Controller.java

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

    if (bindingResult.hasErrors()) {
        Object result = model.getAttribute("org.springframework.validation.BindingResult.item");
        log.info("result={}", result);
    }

    // ...

}

 

결과 (로그)

result=org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'item' on field 'price': arguments ....(생략)

BeanPropertyBindingResult라는 객체가 model에 담겨있는 것을 확인할 수 있다.

BeanPropertyBindingResult는 인터페이스인 BindingResult를 구현한 구현체이다. 

(이미지 출처: https://fordevelop.tistory.com/115)

 


ref

https://docs.spring.io/spring-framework/reference/web/webflux/controller/ann-methods/modelattrib-method-args.html#page-title