본 글은 김영한 님의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 강의를 토대로 작성하였습니다.
이전 포스트들에서 파라미터를 통해 클라이언트로부터 넘어온 데이터를 받고, 객체로 자동 변환해주고, 반대로 응답을 보낼 때도 객체를 리턴하거나 문자열을 리턴하면 알아서 HTTP Message에 담겨 나가는 기능에 대해 알아보았다. 이는 스프링 MVC 에서 해당 로직들을 처리해주기 때문에 가능한 것인데, 그렇다면 실제로 어떤 코드들이 동작하여 이러한 복잡한 과정들을 처리해줄까?
다음은 SpringMVC 의 구조이다.
결국 우리가 파라미터에 있는 값을 활용하는 곳은 핸들러(컨트롤러)이다. 따라서 핸들러를 호출하기 전 파라미터에 들어올 값들이 이미 세팅이 되어야 한다는 의미이고, 그렇다면 핸들러 어댑터 부분에서 필요한 파라미터 객체들을 생성할 것이라는 것을 알 수 있다.
특히 어노테이션 기반의 컨트롤러 즉 @RequestMapping을 처리하는 핸들러 어댑터인 RequestMappingHandlerAdapter는 우리가 단계별로 요청 받는 방법에 대해 알아봤듯이, HttpServletRequest, Model과 어노테이션 기반인 RequestParam, @ModelAttribute등 굉장히 유연한 대응을 보여주고 있는데, 이 같은 일이 가능한 이유는 핸들러 어댑터가 ArgumentResolver라는 것을 이용하기 때문이다!
다음은 핸들러 어댑터가 핸들러를 호출하는 사이의 과정이다.
핸들러를 실행하기 전 파라미터에 들어갈 객체들을 세팅해야 하기 때문에, 먼저 ArgumentResolver를 호출하여 처리 가능한 리졸버를 호출하여 생성된 객체를 핸들러의 메소드에 파라미터로 넣어주며 호출을 해야한다.
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory
binderFactory) throws Exception;
}
실제 ArgumentResolver 인터페이스이다. 코드를 보면 supportsParameter 함수를 이용해 해당 파라미터를 지원하는지 여부를 체크하고 resolverArgument함수를 호출하여 파라미터로 넘겨줄 객체를 생성하여 리턴하는 것을 볼 수 있다.
이와 마찬가지로, 응답을 보낼 때도 그냥 문자열만 반환해도 view이름을 찾거나 @ResponseBody가 붙어있으면 그 자체를 HTTP 메시지 바디에 담아 내보내거나 ModelAndView를 반환하는 등 자동으로 이루어지는 부분이 많았다. 이 또한 ReturnValueHandler가 내부적으로 동작하여 리턴 값들을 자동으로 인식하여 적절한 로직을 실행하는 것이다.(ArgumentResolver랑 요청이냐 응답이냐만 다르지 원리는 거의 동일하다.)
그렇다면 이전에 종종 보였던 HTTP 메시지 컨버터는 어디서 호출되는 것일까.
그림에서 볼 수 있듯이, @RequestBody, HttpEntity 등 ArgumentResolver가 HTTP 메시지 바디를 직접 파싱해야할 때 HTTP 메시지 컨버터를 호출하여 사용한다. 이 메시지 컨버터 또한 JSON, XML 등 많은 구현체가 존재한다. 물론 응답의 경우도 마찬가지다