본문 바로가기
Spring/Exception handling

[예외처리] @Valid 예외처리에 사용되는 BindingResult 객체는 무엇일까?

by domo7304 2022. 7. 17.

이번 글에서는 @Valid 에 의해 발생되는 MethodArgumentNotValidException.class 에 속하는
'BindingResult' 에 대해 알아보고자 한다.

  1. 문제 상황, 찾아보게 된 계기 - MethodArgumentNotValidException 에 속하는 BindingResult 가 무엇인가? (이동)
  2. 학습 과정 - BindingResult 파고들기 (이동)
  3. 결론 - 예외처리 코드가 동작하는 원리에 대한 이해 (이동)

 

1. 문제상황 - MethodArgumentNotValidException 에 속하는 BindingResult 가 무엇인가?

 

더보기

 

Client 로부터의 입력 예외, business 로직 예외를 처리하기 위해 위 접은 글들을 참고하며 예외 처리 클래스를 작성하고 있었다. 정말 친절하게 설명을 잘 해주셔서 천천히 로직을 따라가고 있었는데, 갑자기 막히는 곳이 생겼다.

아래 코드에서 ExceptionResponse 는 공통 예외 응답을 위한 클래스, FieldException 은 예외가 발생한 필드, 값, 이유 를 포함하는 nested class 이다. (이해를 위해 모든 코드를 이미지로 올렸으니 create() 메소드만 잘 따라가면 된다.)

처음 보는 getBindingResult() 메소드...
정체를 알 수 없는 BindingResult...
BindingResult.getFieldErrors() 는 또 어디 구현되어 있는 메소드인지....

첫 번째 이미지에서 @ValidMethodArgumentNotValidException 을 발생시키므로,
MethodArgumentNotValidException e 를 파라미터로 받는 것 (line18) 까지는 이해가 되었다.

그런데 그 이후

  1. e.getBindingResult() 로 BindingResult 를 가져와서
  2. BindingResult 에서 FieldError 를 원소로 가지는 List 를 만들고
  3. 그 List 를 이용해서 field, RejectedValue, DefaultMessage 를 FieldException 에 매핑하는데

'BindingResult' 가 도저히 뭔지 알 수가 없었다.

위 글들 뿐만 아니라 다른 글들에서도 많이 볼 수 있는 형태의 코드였기 때문에 조금 더 구글링을 해보니 
BindingResult 라는 것이 어떤 필드를 포함하고 있고, 어떤 역할인지 나오기는 했지만, 정확히 어떤 흐름으로 저렇게 매핑이 될 수 있는지에 대한 포스팅은 찾을 수가 없어서 답답한 마음에 쓰게 되었다.

2. 학습 과정 - BindingResult 파고들기

디버깅을 해보자

위와 같이 중단점을 찍어서 디버깅모드로 살펴보았다. @Valid 에 걸리도록 예외를 낸 다음에 관찰해보니 일단 한 가지를 알 수 있었다.

  • MethodArgumentNotValidException 안에는 여러 필드가 있고, 그 중 bindingResult 안을 살펴보면 error 와 관련된 여러 값들이 담겨있다.

저렇게 갖고 들어온 BindingResult 가 ExceptionResponse.create(), FieldException.create() 를 거쳐 아래와 같이 List 를 하나 만들게 되는데

BindingResult.getFieldErrors() 구현 코드를 살펴보니

BindingResult 내부의 errors 의 개수만큼 반복하여 해당 객체를 FieldError 라는 형태로 List 에 담아 반환하고 있었다.

결과적으로 BindingResult.getFieldErrors() 를 통해 FieldError 로 이루어진 List 를 반환 받고, 각 원소를 돌면서 개발자가 작성한 FieldException 이라는 클래스의 각 필드에 매핑하는 작업을 수행한 것이다.

3. 결론 - 예외처리 코드가 동작하는 원리에 대한 이해

요약하자면,

  1. @Valid 는 MethodArgumentNotValidException 을 발생시킨다.
  2. MethodArgumentNotValidException 에는 여러 값들이 있으며, 그 중 BindingResult 라는 객체에 예외에 대한 좀 더 자세하고 많은 정보들이 담겨있다.
  3. BindingResult 객체 안에서 error 와 관련된 필요한 값을 가져오기 위해 BindingResult.getFieldErrors() 메소드를 이용하여 BindingResult 가 갖고 있는 errors 를 FieldError 라는 객체 형태로 반환받고
  4. FieldError 객체에서 필요한 값들을 사용자(개발자) 가 정의한 예외 객체에 매핑하여 사용한다.
    1. Field : 객체에서 예외가 발생한 field
    2. RejectedValue : 어떤 값으로 인해 예외가 발생하였는지
    3. DefaultMessage : 해당 예외가 발생했을 때 제공할 message 는 무엇인지

결과적으로 'exceptions' 배열에서 볼 수 있듯 예외에 해당하는 field, value, reason 이 매핑되어 나오는 것을 확인할 수 있다.

댓글