개발 기록

> [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (2) HandlerMethodArgumentResolver : Request Value Binding 본문

DIVE

> [Spring] Spring 은 요청을 어떻게 처리 해주고 있는 걸까? (2) HandlerMethodArgumentResolver : Request Value Binding

1z 2023. 12. 7. 16:54

 

 

 

 

1 HandlerMethodArgumentResovler

컨트롤러 메서드에서 특정 조건에 맞는 파라미터가 있을 때 요청에 들어온 값을 이용해 원하는 객체를 만들어 바인딩해준다. 

 

☞ HandlerMethodArgumentResolver 확장(=custom) 해서 사용하는 경우

매개변수로 사용되는 인자에 대해 공통적으로 처리해야할 로직 등이 있을 경우, 중복 코드를 줄이고 공통 기능으로 추출하여 사용할 수 있다.

ex. 특정 클래스나 특정 어노테이션등의 요청 파라미터를 수정해야하거나, 또는 클래스의 파라미터를 조작 혹은 공통적으로 써야하는 파라미터들을 바인딩 해줘야 하는 경우.

 

 

(1) 요청 간략 흐름 

 

스프링의 디스패처 서블릿은 컨트롤러로 요청을 전달하고

InvocableHandlerMethod  내부 로직 중 getMethodArgumentValues() 메서드 실행으로 ArgumentResolver 가 동작하여 컨트롤러에서 필요로 하는 객체를 만들고 값을 바인딩하여 전달한다.  스프링이 제공하는 다음과 같은 어노테이션들은 모두 ArgumentResolver로 동작한다.

☞  @RequestParam: 쿼리 파라미터 값 바인딩

  @ModelAttribute: 쿼리 파라미터 및 폼 데이터 바인딩

@CookieValue: 쿠키값 바인딩

@RequestHeader: 헤더값 바인딩

@RequestBody: 바디값 바인딩

https://velog.io/@e1psycongr00/Spring-ArgumentResolver-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0

 

*

 

 

 

(2) 내부 살펴보기 - method 

  • supportsParameter() 
    • 주어진 메소드의 파라미터가 이 Argument Resolver에서 지원하는 타입인지 검사한다. 지원한다면 true 를, 그렇지 않다면 false 를 반환한다.
  • resolveArgument()
    • 이 메소드의 반환값이 대상이 되는 핸들러 메소드의 파라미터에 바인딩된다.
public interface HandlerMethodArgumentResolver {
	boolean supportsParameter(MethodParameter parameter);

	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

 

(3) Class 관계도 

 

하나하나 알아보자.

 type name 설명
Interface HandlerMethodArgumentResolver  핸들러 메소드 호출에서 반환된 값을 처리
Interface HandlerMethodReturnValueHandler  요청의 메소드 매개변수를 인수 값으로 해석
Class AbstractMessageConverterMethodArgumentResolver  요청 본문을 읽어 메서드 인수 값을 확인
(HttpMessageConverts 사용)
Class AbstractMessageConverterMethodProcessor  응답에 기록하여 메서드 반환 값을 처리하는 기능 
Class RequestResponseBodyMethodProcessor  @RequestBody 메서드의 인자/반환 값 처리

 

 

 

3. 구현체  

 

(1) AbstractMessageConvertMethodArgumentResolver  

 

▶1. readWithMessageConverters ()   

: 주어진 HttpInputMessage 를  읽고 HTTPMessageConvert  를 이용하여  요청값을 매개변수 타입의 인수 값으로 데이터바인딩 한다. 

- supports: 해당 RequestBodyAdvice를 적용할지 여부를 결정함

- beforeBodyRead: body를 읽어 객체로 변환되기 전에 호출됨

- afterBodyRead: body를 읽어 객체로 변환된 후에 호출됨

- handleEmptyBody: body가 비어있을때 호출됨

AbstractMessageConverterMethodArgumentResolver. readWithMessageConverters

 

 

(2) AbstractNamedValueMethodArgumentResolver  

 

: 메서드 인수를 확인하기 위한 추상 기본 클래스이다.

 

@RequestParam, @PathVarible은 모두 NamedValue 기반으로 동작하고 이들의 ArgumentResolver는 모두 AbstractNamedValueMethodArgumentResolver 테플릿을 상속하여 구현한다. 서브클래스는 다음을 수행하는 방법을 정의한다.

 

- 메소드 매개변수에 대한 명명된 값 정보 얻기

- 이름을 인수 값으로 확인

- 인수 값이 필요한 경우 누락된 인수 값 처리

- 선택적으로 해결된 값을 처리.

 

※ 만약 ArgumentResolver를 직접 구현해서 추가해야 한다면,  해당 템플릿 클래스를 상속하여 구현하면 Spring에 호환성이 좋고 안전하게 구현할 수 있다.

 

로직은 다음과 같은 순서로 진행한다

1. getNamedValueInfo() : createNameValueInfo() 메서드를 호출하여 NamedValueInfo(name, required, defaultValue) 를 가져온다.

 

RequestParamMethodArgumentResolver.class

 

 

2. resolveName() : 주어진 매개변수 유형과 값 이름을 인수 값으로 해석 하고 인자에 필요한 데이터를 바인딩한다.

3. 2번 과정에서 가져온 인자 값이 null이라면 NamedValue의 속성에 따라 로직을 처리한다

 

 

 

(3) RequestResponseBodyMethodProcessor

 

@RequestBody는 ArgumentResolver의 구현체인 RequestResponseBodyMethodProcessor에 의해 처리된다. json 형태의 메세지는 객체로 변환되어 컨트롤러로 전달된다.

 

☞  @RequestBody 주석이 달린 메서드 인수를 확인 하고 요청 또는 응답의 본문을 읽고 써서 @ResponseBody 주석이 달린  메서드의 반환 값을 처리한다.. (HttpMessageConverter 사용)
☞   @RequestBody 주석에 유효성 검사하는 주석이 추가된 경우 메서드 인수도 유효성 검사 대상이 된다.

- 유효성 검사에 실패하는 경우  HTTP 400 error 발생 (공식문서 번역)

 

 

 

 

 

참고

https://mangkyu.tistory.com/250

https://velog.io/@e1psycongr00/Spring-ArgumentResolver-%EB%B6%84%EC%84%9D%ED%95%98%EA%B8%B0

https://cl8d.tistory.com/11