본문 바로가기

Spring

[Spring] Spring MVC, RequestMappingHandlerAdapter의 요청 및 응답 처리 과정 : ArgumentResolver & ReturnValueHandler란?

 


 

RequestMappingHandlerAdapter의 요청 및 응답 처리 전체 구조

https://kimyongjun0129.tistory.com/185

 

[Spring] Spring MVC, RequestMappingHandlerAdapter의 요청 및 응답 처리 과정

목차RequestMappingHandlerAdapter 구조용어 정리전체 흐름 요약 RequestMappingHandlerAdapter 구조 용어 정리DispatcherServlet : Spring MVC의 핵심 프론트 컨트롤러클라이언트의 모든 요청을 가로채고, 적적할 컨트롤

kimyongjun0129.tistory.com

 

 


 

RequestMappingHandlerAdapter 구조

  • `RequestMappingHandlerAdapter`는 `ArgumentResolver`를 호출하여 Controller에 필요한 다양한 파라미터 값을 변환(바인딩)합니다.
  • `ReturnValueHandler`의 결과는 `RequestMappingHandlerAdapter`를 통해 최종적으로 뷰 렌더링 또는 HTTP 응답으로 이어집니다.

 


 

ArugmentResolver

정의 :

컨트롤러 메서드의 파라미터를 객체로 변환(바인딩)해주는 컴포넌트입니다.

 

 

파라미터 종류 :

  • @RequestParam
  • @RequestBody
  • @RequestParam
  • HttpServletRequest, Model, HttpEntity 

 

✨ 들어오는 Http 요청의 파라미터 타입이나 어노테이션 등에 에 따라 Resolver가 존재하면 호출됩니다.

 

 

ArgumentResolver 구현체 종류 : 

파라미터 또는 어노테이션 해당 Resolver 클래스 설명
`@RequestParam` RequestParamMethodArgumentResolver 쿼리 파라미터, 폼 데이터 등을 처리합니다.
@RequestParam("id")
`@RequestBody` RequestResponseBodyMethodProcessor HTTP 요청의 Body(JSON, XML 등)를 객체로
변환
`@PathVariable` PathVariableMethodArgumentResolver URL 경로 변수에 바인딩된 값을 처리
`@ModelAttribute` ModelAttributeMethodProcessor 폼 바인딩 객체 (e.g. UserForm userForm)
`HttpServletRequest` ServletRequestMethodArgumentResolver 서블릿 API 타입 (HttpServletRequest, HttpServletResponse)
`HttpServletResponse` ServletRequestMethodArgumentResolver 위와 동일
`HttpSession` ServletRequestMethodArgumentResolver 위와 동일
`Model` ModelMethodProcessor 컨트롤러 메서드에서 Model 주입
`HttpEntity<T>` HttpEntityMethodProcessor HTTP 요청의 전체 내용을 추상화한 객체
`RequestEntity<T>` HttpEntityMethodProcessor HttpEntity + 요청 정보 (URL, Header 등 포함)
  • 각 어노테이션과 파라미터에 대한 resolver 클래스 들은 `HandlerMethodArgumentResolver` 인터페이스를 구현하고 있습니다.

 

더보기

@RequestBody는 HttpMessageConverter가 변환해주지 않나? @RequestBody의 Resolver는 왜 존재할까?

 

 

❗결론부터 말하자면, 실제 변환은 `HttpMessageConverter`가 담당하는 것이 맞습니다.

그러면 ` RequestResponseBodyMethodProcessor`의 역할은 무엇일까요?

 

 

실제 동작 순서

1. Spring은 컨트롤러 메서드 파라미터를 분석하고, `HandlerMethodArgumentResolver` 구현체들이 저장된 리스트를 순회하면서, `supportsParameter()`를 호출하여 검증합니다.

for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
    if (resolver.supportsParameter(parameter)) {
        return resolver.resolveArgument(...);  // 여기서 파라미터 값을 만들어냄
    }
}

 

 

2. `@RequestBody`에 대한 resolver가 맞는지 검증합니다.

@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestBody.class);
}
  • `MethodParameter parameter` 안에, 해당 컨트롤러 메서드의 파라미터 정보 전체가 들어있습니다.
  • `RequestResponseBodyMethodProcessor`의 `supportsParameter()`가 `true`를 반환합니다.

 

 

3. 그 후 `RequestResponseBodyMethodProcessor`은 `resolveArgument()` 내부에서 다음 코드를 실행합니다.

@Override
public Object resolveArgument(...) {
    ...
    return readWithMessageConverters(request, methodParam, paramType);
}
  • 등록된 `HttpMessageConverter` 리스트를 순회합니다.
  • `canRead(...)`로 해당 타입을 처리할 수 있는 Converter 찾습니다.
  • 찾으면 `read(...)` 호출해서 Java 객체로 변환합니다.

 

 

결론

  • @RequestBody 자체는 Resolver + Converter 협업으로 작동합니다.
  • RequestResponseBodyMethodProcessor는 "연결자" 역할
    • 어떤 파라미터에 @RequestBody가 붙어있는지 감지하고,
    • 적절한 HttpMessageConverter를 선택해서 변환을 실행합니다.
  • HttpMessageConverter는 "실제 파싱/직렬화 담당자"입니다.
더보기

 

✨위에서 처럼 각 어노테이션과 파라미터에 대한 resolver들은 `HandlerMethodArgumentResolver`를 구현하고 있습니다.

 

 

예시 :

@GetMapping("/user")
public String example(
    @RequestParam("id") String id,               // RequestParamMethodArgumentResolver
    @RequestBody User user,                      // RequestResponseBodyMethodProcessor
    HttpServletRequest request,                  // ServletRequestMethodArgumentResolver
    Model model,                                 // ModelMethodProcessor
    HttpEntity<String> httpEntity                // HttpEntityMethodProcessor
) {
    ...
}

 

 


 

 

HandlerMethodArgumentResolver

  • `ArgumentResolver`의 실제 이름
    • resolver 클래스들은 이 인터페이스를 구현한 형태입니다.
    • 이 인터페이스를 구현하여, 커스텀하게 파라미터를 만들 수 있습니다. (확장)
  • `supportsParameter(MethodParameter parameter);`
    • 컨트롤러가 필요로하는 메서드의 파라미터를 지원하는지 여부를 검사합니다.
  • 지원한다면 `resolveArgument()` 메서드를 통해 Object(객체)로 만들어줍니다.
    • 만들어진 Object(객체)가 Controller 호출 시 메서드의 파라미터로 전달됩니다.
  • `supportsParameter()`를 사용하는 다양한 `ArgumentResolver` 구현체

 


 

 

실제 동작 순서

1. Spring은 컨트롤러 메서드 파라미터를 분석하고, `HandlerMethodArgumentResolver` 구현체들이 저장된 리스트를 순회하면서, `supportsParameter()`를 호출하여 검증합니다.

for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
    if (resolver.supportsParameter(parameter)) {
        return resolver.resolveArgument(...);  // 여기서 파라미터 값을 만들어냄
    }
}

 

 

2. 어노테이션 또는 파라미터에 대한 resolver가 맞는지 검증합니다.

@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(RequestBody.class);
}
  • `MethodParameter parameter` 안에, 해당 컨트롤러 메서드의 파라미터 정보 전체가 들어있습니다.
  • 특정 Resolver의 `supportsParameter()`가 ` parameter` 정보를 통해 확인한 후, 맞으면 `true`를 반환합니다.

 

 

3. true가 반환 된다면, 그 후 특정 Resolver는 `resolveArgument()` 내부에서 다음 코드를 실행합니다.

public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter parameter);

    @Nullable
    Object resolveArgument(
        MethodParameter parameter,
        @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest,
        @Nullable WebDataBinderFactory binderFactory
    ) throws Exception;
}
  • 실제로 파라미터 값을 생성하여 반환하는 역할을 합니다. 
    • 파라미터를 어떻게 만들어야 할지 구체적인 로직이 들어갑니다.
파라미터 설명
`MethodParameter parameter` 처리 중인 파라미터 (타입, 어노테이션 등 포함)
`ModelAndViewContainer mavContainer` Model이나 View 이름을 담고 있는 컨테이너. 주로 `@ModelAttribute`나 `Model` 파라미터가 있을 때 사용됩니다.
`NativeWebRequest webRequest` `HttpServletRequest`와 같은 웹 요청 객체를 래핑한 객체
`WebDataBinderFactory binderFactory` 바인딩과 검증을 위한 `WebDataBinder`를 생성해주는 팩토리. `@Valid`, `@InitBinder` 등을 처리할 때 사용합니다.

 

 


 

ReturnValueHandler

정의 :

컨트롤러 메서드의 리턴값을 HTTP 응답으로 변환해주는 컴포넌트입니다.
컨트롤러가 반환한 값을 Model, View, JSON, HTTP 응답 본문 등으로 변환합니다.

 

 

✨ 리턴 타입, 어노테이션 등에 따라 알맞은 Handler가 선택되어 호출됩니다.

 

 

ReturnValueHandler 구현체 종류 : 

리턴 타입 또는 어노테이션 해당 Handler 클래스 설명
`@ResponseBody` RequestResponseBodyMethodProcessor 객체를 JSON 또는 XML로 직렬화하여 HTTP 응답 body에 작성
`ResponseEntity<T>` HttpEntityMethodProcessor 상태 코드, 헤더, body 모두 직접 설정하여 응답 가능
`HttpEntity<T>` HttpEntityMethodProcessor 위와 동일하지만 상태 코드는 기본값 사용
`ModelAndView` ModelAndViewMethodReturnValueHandler 명시적으로 Model과 View 이름을 포함한 객체를 리턴
`String` (View 이름) ViewNameMethodReturnValueHandler 리턴된 문자열을 View 이름으로 간주 (JSP, Thymeleaf 등)
`Model` / `Map` ModelMethodProcessor Model 객체 자체를 반환 (주로 forward 용)
`@ModelAttribute` (리턴값에 사용) ModelAttributeMethodProcessor 객체를 Model에 추가하고 View로 전달
`void` (리턴 없음) VoidMethodReturnValueHandler 뷰 이름을 명시하지 않으면 요청 URL로부터 View 추정
`StreamingResponseBody`, `ResponseBodyEmitter`, `SseEmitter` 등 StreamingResponseBodyReturnValueHandler 외 비동기 스트리밍 방식으로 HTTP 응답 전송
  • 모든 Handler 클래스들은 `HandlerMethodReturnValueHandler` 인터페이스를 구현하고 있습니다.

 

 

예시 :

@GetMapping("/user")
@ResponseBody
public User getUser() {
    return new User("Tom");
}
  • 리턴 타입 : `User`, 어노테이션 : `@ResponseBody`
  • 처리 Handler : ` RequestResponseBodyMethodProcessor`

 

 

HandlerMethodReturnValueHandler

  • `ReturnValueHandler`의 실제 이름
    • resolver 클래스들은 이 인터페이스를 구현한 형태입니다.
    • 이 인터페이스를 구현하여, 커스텀하게 파라미터를 만들 수 있습니다. (확장)
  • 다양한 ReturnValueHander 구현체

 

 

동작 방식

1. Spring은 컨트롤러 메서드의 리턴 타입을 분석합니다.

for (HandlerMethodReturnValueHandler handler : returnValueHandlers) {
    if (handler.supportsReturnType(returnType)) {
        handler.handleReturnValue(...);  // 여기서 리턴값 처리
        break;
    }
}
  • `HandlerMethodReturnValueHandler` 구현체들이 저장된 리스트를 순회하면서
  • `supportsReturnType()`을 호출하여 이 핸들러가 해당 리턴값을 처리할 수 있는지 확인합니다.

 

2. 어노테이션 또는 리턴 타입에 따라 `supportsReturnType()`이 호출됩니다.

@Override
public boolean supportsReturnType(MethodParameter returnType) {
    return returnType.hasMethodAnnotation(ResponseBody.class);
}
  • `MethodParameter returnType` : 컨트롤러 메서드의 리턴 타입 정보를 담고 있습니다.
  • 조건이 맞으면 `true` 반환 → 해당 핸들러가 처리할 대상입니다.

 

3. `true`가 반환되면, 해당 핸들러의 `handleReturnValue()`가 호출됩니다.

public interface HandlerMethodReturnValueHandler {

    boolean supportsReturnType(MethodParameter returnType);

    void handleReturnValue(
        @Nullable Object returnValue,
        MethodParameter returnType,
        ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest
    ) throws Exception;
}
  • 이 메서드가 컨트롤러 메서드의 리턴값을 적절한 방식으로 HTTP 응답에 바인딩합니다.
  • 예: JSON 변환, View 이름 설정, Model에 데이터 추가 등
파라미터 타입 설명
`returnValue` `@Nullable Object` 컨트롤러 메서드가 반환한 실제 값입니다. 예: User, String, ResponseEntity, 등.
`returnType` `MethodParameter` 리턴값에 대한 메서드 파라미터 정보 객체입니다. 어노테이션(@ResponseBody, @ModelAttribute)이나 제네릭 타입 등의 정보도 포함됩니다.
`mavContainer` `ModelAndViewContainer` 모델, 뷰 이름, 처리 완료 여부 등을 담고 있는 컨테이너입니다. View 렌더링을 제어하는 데 사용됩니다. 예: mavContainer.setViewName("home")
`webRequest` `NativeWebRequest` HTTP 요청 및 응답에 접근할 수 있는 추상화된 객체입니다. 내부적으로 HttpServletRequest, HttpServletResponse에 접근 가능하며, Spring Web 전체에서 공통적으로 사용됩니다.