이번 시리즈에서는 스프링 부트로 프로젝트를 처음부터 생성 및 구축해보도록 하겠습니다.
# 목차
1. 스프링 부트 프로젝트 생성 & 실행 - EP01
2. Controller 생성 & API 호출 (1) - EP02
3. Controller 생성 & API 호출 (2) - EP03
DispatcherServlet
이전글에 이어 다음으로 살펴볼 클래스는 DispatcherServlet 입니다.
앞단에서 HTTP request 에 대한 여러 Filter 처리가 완료된 후에는
DispatcherServlet 이 호출되어 실제로 해당 request 에 mapping 된 method 를 호출하게 됩니다.
위 Debugger Stack 상으로는 HttpServlet 이라고 표기되어 있지만.
DispatcherServlet 의 상위 클래스 상속구조가 다음과 같기 때문에.
HttpServlet / FrameworkServlet 클래스 내부 메서드가 호출될 수 있는 구조입니다.
위에서 각 Servlet 클래스에 대한 자세한 설명은 생략하겠습니다.
간단히 GenreicServlet 은 일반적인 Servlet 을 통칭하고..
HttpServlet 은 HTTP 전용으로 사용하기 위한 GenricServlet 이며.
FrameworkServlet 은 Spring 전용으로 사용하기 위한 HttpServlet 으로만 이해하고 넘어가겠습니다..
이 중 DispatcherServlet 은 HTTP request 에 대한 중앙 집중 처리를 전담해주는 친구로..
그림으로 간단히 정리하면 다음과 같이
HTTP request 가 들어오면 Servlet Container 가 관리하는 여러 Servlet 중
해당 요청에 맞는 Servlet (일반적으로 Controller 를 통칭..) 을 찾아서 request 를 처리하도록 요청합니다.
조금 더 자세히 살펴보면..
HttpServlet
먼저 HttpServlet 의 service 메서드를 통해 ServletRequest / ServletResponse 객체가 (사용자 요청)
HttpServletRequset / HttpServletResponse 객체로 변환됩니다.
ServletRequest / ServletReponse 와 HttpServletRequest / HttpServletResponse 의 차이점은 다음과 같습니다.
요약하자면 ServletRequest / ServletResponse 는 Protocol 독립적인 interface 이며
HttpServletRequest / HttpServletResponse 는 HTTP Protocol 을 위해 사용되는 interface 입니다.
즉, 다음과 같이 웹 브라우저로 요청한 /hello request 는 HTTP Protocol 을 사용하므로.
이를 처리하기위해 ServletRequest / ServletResponse 를
HttpServletRequest / HttpServletResponse 로 변환하는 것으로 이해하면 될 것 같습니다.
FrameworkServlet
다음으로는 FrameworkServlet 의 service 메서드가 호출되는데
PATCH 요청의 경우에만 super 클래스인 HttpServlet 의 service 가 아닌
FrameworkServlet 클래스의 service 메서드를 호출합니다.
이는.. PATCH 메서드가 비교적 최근에 추가되었기 때문에.
HttpServlet 의 service 메서드에는 PATCH 요청에 대한 처리를 하도록 설계되어 있지 않기 때문인 것으로 생각됩니다. (아마도..?)
실제로 HttpServlet 의 service 메서드를 살펴보면 다음과 같이 PATCH 요청에 대해서만
어떻게 처리할 것인지에 대해 구현이 된 내용이 없는 것을 알 수 있습니다. (GET / POST / DELETE / PUT 등 은 존재..)
결론적으로.. HttpServlet 에 정의되어 있지 않은 PATCH 요청이나..
아니면.. 정의되어 있는 GET / POST / PUT / DELETE / OPTIONS / TRACE 등의 요청이던지 간에..
FrameworkServlet 의 processRequset 메서드를 호출해 request 에 대한 처리를 위임(delegate) 하게 됩니다.
RequestHanlderMapping
이후 DispatcherServlet 에서 getHandler 메서드를 호출해
HTTP request 를 처리하기 위해 어떠한 컨트롤러 & 메서드(Handler)를 사용할 것인지를 determine 하게 되는데.
이 때 프로젝트 별 어떠한 Mapping 전략을 사용하고 있느냐에 따라
아래와 같이 Hanlder 를 찾아주는 역할을 하는 HanlderMapping 구현체가 달라지게 됩니다.
현재 제 프로젝트에서는 다음과 같이 RequestMapping 전략을 사용하고 있기 때문에.
(@GetMapping 은 @RequestMapping(method = RequestMethod.GET) 와 동일)
아래의 getHanlder 메서드의 내부에서 HttpRequestMappingHandler 구현체를 사용해
HTTP request 에 대한 MappingHandler 를 찾게 되고..
결과적으로 return 받은 MappingHanlder 는
다음과 같이 어떤 Contoller 에 어떤 Method 를 수행해야 되는지에 대한 정보가 담겨 있게 됩니다.
간단히, HanlderMethod 란 HTTP request 를 처리하기 위한 Controller & Method 에 대한 정보라고 이해하면 될 것 같습니다.
RequestHandlerMappingAdapter
자 이제.. 오랜 여정 끝에.. HTTP request 를 처리하기 위한 Controller 와 Method 정보를 알아냈으니..
이제 해당 Method 를 실행한 뒤 결과값을 return 해주면 되는데.
이 때 에도 직접 해당 요청을 수행하는 것 이 아닌.
Adpater 패턴을 적용한 RequestMappingHanlderAdpater 를 호출해 작업을 수행하게 됩니다.. 🤦🏻♂️
Adpater 패턴을 사용한 이유는 아마도..
어떠한 Mapping 전략을 사용하던지 간에..
동일하게 시스템이 동작할 수 있도록 구현되어 있는 것 같습니다.
ServletInvocableHanlderMethod
RequestMappingHanlderAdpater가 호출되면 내부적으로는
RequestMappingHanlderMapping 이 찾아준 HanlderMethod 를
ServletInvocableHandlerMethod 로 Wrapping 한 뒤 해당 HanlderMethod 를 invoke (수행) 합니다.
ServletInvocableHandlerMethod 는 다음과 같은 상속 구조를 가지고 있는데.
해당 클래스의 설명을 보면 아마도?..
HandlerMethodReturnValueHandler 를 사용해 return value 를 handle 하고
method-level 로 reponse status 를 설정하기 위해 사용하는 것 같습니다.
ServletInvocableHandlerMethod 는 결론적으로 doInvoke 메서드를 통해
HanlderMethod (요청 HTTP request 를 처리하기 위한 Controller 의 Method) 를 수행하게 되는데
Reflection
이때 Method 를 수행하기 위해 내부적으로는 Reflection 을 사용하게됩니다.
Reflection 는 간단히.. 객체를 통해 클래스의 정보를 분석해 내는 방법이라고만 이해하고
넘어가도록 하겠습니다. (아직 잘 모르는 분야 입니다. ㅠㅠ)
ServletInvocableHanlderMethod
다음으로.. 진짜 진짜 최종으로..
드디어 HelloController Stack 이 호출되게 되고..
GET /hello 요청과 mapping 되는 hello() 메서드가 수행이 되게 됩니다..
자.. 이렇게 간단한게 /hello 요청이 호출되는 과정에 대해 알아보았습니다. 😭
다음글에서는 위 요청에 대한 결과값이 어떻게 return 되는지에 대해 알아보도록 하겠습니다.
결론
여러 Filter 처리가 완료된 HTTP request 에 대해 DispatcherServlet 이
해당 request 를 처리하기 위한 Servlet 을 찾아 작업을 요청하기 위해..
1. ServletRequset / ServletResponse 를 HTTP 요청 규격에 맞도록
HttpServletRequest / HttpServletResponse 로 변환하고 (HttpServlet)
2. HttpServlet 규격에는 PATCH 메서드가 구현되어 있지 않으므로..
PATCH 요청일 경우에만 FrameworkServlet 의 service 메서드를 직접 호출하고,
나머지 요청들은 HttpServlet 에 구현되어 있는 service 메서드를 호출해 요청을 처리하며
3. 프로젝트의 Mapping 전략별 MappingHanlder 를 사용해 (현 프로젝트 기준 RequsetMappignHandler)
해당 HTTP request 를 처리하기 위한 HandlerMethod (Controller & Method 정보) 를 알아내고.
4. 알아낸 HandlerMethod (Controller & Method 정보) 를 수행하기 위해
Adapter 패턴을 적용한 HanlderMappingAdapter 를 호출해 작업을 위임하며.
5. 이 때 HanlderMappingAdapter 는 해당 작업을 수행하기 위해 내부적으로 Reflection
(객체를 통해 클래스의 정보를 분석해 내는 방법) 을 사용하게 된다.
로 이해할 수 있습니다.
'Spring > Project' 카테고리의 다른 글
[Project] Controller 생성 & API 호출 (1) - EP02 (0) | 2021.06.27 |
---|---|
[Project] 스프링 부트 프로젝트 생성 & 실행 - EP01 (0) | 2021.06.20 |