Lined Notebook

[스프링 MVC - 백엔드 웹 개발 기술] 18. 요청 매핑 핸들러 어뎁터 구조 - RequestMappingHandlerAdapter

by ymkim

01. 요청 매핑 핸들러 어뎁터 구조

  • 이번 시간에는 요청 매핑 핸들러 어뎁터(RequestMappingHandlerAdapter) 구조에 대해서 알아본다
  • 이전에 HTTP 메시지 컨버터에 대해 배웠지만 아래 사진에서는 메시지 컨버터 관련한 부분이 보이지 않는다.
  • 이러한 HttpMessageConverter 는 아래 사진에서 어떠한 부분에 해당이 되는 것일까?
  • 이번 시간의 주요 포인트는 요청 매핑 핸들러 어댑터, 그리고 HTTP 메시지 컨버터가 어디에서 사용이 되는지 확인해보자

Spring MVC 구조

  • HTTP 메시지 컨버터는 스프링 MVC의 어느곳에서 사용되는 것일까?
  • 위 그림만 봤을때는 HTTP 메시지 컨버터가 사용되는 곳은 보이지 않는다
  • 비밀은 어노테이션 기반의 컨트롤러인 @RequestMapping을 처리하는 핸들러 어댑터인 요청 매핑 핸들러 어뎁터에 있다
  • 다음으로 요청 매핑 핸들러 어뎁터의 동작 방식에 대해서 살펴보자

01-1. 요청 매핑 핸들러 어뎁터(RequestMappingHandlerAdapter) 동작 방식

  1. 사용자 HTTP 요청 → DispathcerServlet → 핸들러 어댑터(RequestMappingHandlerAdapter)는 위와 같다
  2. 핸들러 어댑터는 ArgumentResolver에게 요청에 대한 처리 가능 여부를 묻는데 해당 부분은 아래에서 알아보자

01-2. ArgumentResolver(요청시)

 

Method Arguments :: Spring Framework

JDK 8’s java.util.Optional is supported as a method argument in combination with annotations that have a required attribute (for example, @RequestParam, @RequestHeader, and others) and is equivalent to required=false.

docs.spring.io

// 아래와 같은 API들이 있다고 가정해보자
// 여기서 다양한 매개변수 HttpServletRequest, HttpServletResponse, HttpEntity
// 들은 누가, 어떻게 주입 해주는 것인가?
// ----> 주입 해주는 녀석은 핸들러 어댑터 이지만, 처리는 ArgumentResolver가 담당한다

@GetMapping("/test1")
public ResponseEntity<?> test01(HttpServletRequest request, HtttpServletResponse response) {
    return ResponseEntity.ok("ok");
}

@PostMapping("/test2")
public ResponseEntity<?> test02(@RequestBody Member member) {
    return ResponseEntity.ok("ok");
}

@PostMapping("/test3")
public ResponseEntity<?> test03(HttpEntity httpEntity) {
    return ResponseEntity.ok("ok");
}

@PostMapping("/test4")
public ResponseEntity<?> test03(@ModelAttribute Member member) {
    return ResponseEntity.ok("ok");
}
  • 위와 같이 컨트롤러(API)들은 다양한 파라미터매개변수로 받아서 유연하게 사용이 가능하다
    • HttpServletRequest, HttpServletResponse
    • Model
    • @RequestParam, @RequestBody, @ModelAttribute
    • HttpEntity
  • 위와 같이 파라미터를 유연하게 처리할 수 있는 이유가 ArgumentResolver 덕분이다
  • 어노테이션 기반 컨트롤러를 처리하는 요청 매핑 핸들러ArgumentResolver 를 호출하여 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터 값(객체)을 생성해준다. 또한 이렇게 파라미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘긴다

스프링은 30개가 넘는 ArgumentResolver를 기본적으로 제공한다.
어떤 종류가 있는지 확인만 해보자.

01-3. HandlerMethodArgumentResolver(인터페이스)

HandlerMethodArgumentResolver

// HandlerMethodArgumentResolver는 기본적으로 인터페이스로 구성이 되어있다
// ㄴ 위 사진을 보면 HandlerMethodArgumentResolver 인터페이스를 구현하는 구현 클래스가 다수 존재함
// ㄴ HttpEntityMethodProcessor
// ㄴ ModelMethodProcessor.. 등등이 존재함

package org.springframework.web.method.support;

import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;

/**
 * Strategy interface for resolving method parameters into argument values in
 * the context of a given request.
 *
 * @author Arjen Poutsma
 * @since 3.1
 * @see HandlerMethodReturnValueHandler
 */
public interface HandlerMethodArgumentResolver {

	boolean supportsParameter(MethodParameter parameter);

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

}

HandlerMethodArgumentResolver의 동작방식은 아래와 같다

  • supportsParameter(MethodParameter parameter)
    • 해당 파라미터 처리가 가능한지 지원 여부 체크
  • resolveArgument(…중략)
    • 파라미터 처리가 가능한 경우, 위 함수가 호출 되고 실제 객체 생성 후 컨트롤러에게 객체 전달
    • HttpservletRequest, DTO 등등
  • HandlerMethodArgumentResolver는 인터페이스이기에 사용자가 직접 구현도 가능

01-4. ReturnValueHandler(응답시)

  • HandlerMethodReturnValueHandler를 줄여서 ReturnValueHandler 라 부른다
  • ArgumentResolver 와 비슷한데 ReturnValueHandler은 ‘응답 값’을 변환하고 처리한다
  • ex) 컨트롤러에서 String으로 뷰 이름으로 반환해도, 동작하는 이유가 ReturnValueHandler 덕분이다
    • ModelAndView
    • @ResponseBody
    • HttpEntity
    • String

02. HTTP 메시지 컨버터

02-1. HTTP 메시지 컨버터 위치

  • HTTP 메시지 컨버터ArgumentResolver, ReturnValueHandler 2곳에서 모두 사용된다
    • 요청 파라미터 검증 후 객체를 생성하는 경우와 객체를 응답하는 경우 둘다 사용
  • 요청의 경우(Request)
    • @RequestBody를 처리하는 ArgumentResolver가 존재
    • HttpEntity를 처리하는 ArgumentResolver가 존재
    • 해당 ArgumentResolver 들이 HTTP 메시지 컨버터를 사용하여 필요한 객체를 생성하는 것
  • 응답의 경우(Response)
    • @ResponseBody를 처리하는 ReturnValueHandler가 존재
    • HttpEntity를 처리하는 ReturnValueHandler가 존재
    • 그리고 여기에서 HTTP 메시지 컨버터를 호출해서 응답 결과를 만든다
  • 스프링 MVC에서 @RequestBody, @ResponseBody 어노테이션이 존재하면
    • RequestResponseBodyMethodProcessor(ArgumentResolver)
  • 스프링 MVC에서 HttpEntity 존재하면
    • HttpEntityMethodProcessor(ArgumentResolver)

02-2. 확장

스프링에서는 다음을 모두 인터페이스로 제공, 하여 필요하면 언제든 기능 확장이 가능하다

  • HandlerMethodArgumentResolver
  • HandlerMethodReturnValueHandler
  • HttpMessageConverter

02-3. 스프링은 대부분의 기능을 제공하기에 확장할 일이 많지 않음

  • 기능 확장은 WebMvcConfigure를 상속받아서 스프링 빈으로 등록하면 된다
  • 실제 자주 사용하지는 않으니 실제 기능 확장이 필요한 경우 사용한다
@Bean
public WebMvcConfigurer webMvcConfigurer() {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        WebMvcConfigurer.super.addInterceptors(registry);
    }

}

블로그의 정보

기록하고, 복기하고

ymkim

활동하기