💡 코드가 보이지 않으시다면 드래그 혹은 오른쪽 아래 🌜 아이콘을 눌러 테마 색을 변경해주세요.
안녕하세요!
키크니 개발자 입니다. 🦒
단순히 HTTP통신을 할 때 request, response만 생각한채 개발을 해온 것 같아 Dispatcher Servlet에 대해서 이해해보고자 정리합니다.
Dispatcher Servlet(디스패처 서블릿) 이란?
Dispatcher는 "보내다"를 의미합니다. 그리고 Servlet은 웹 애플리케이션을 만들 때 필요한 인터페이스를 의미합니다.
실제로 디스패처 서블릿도 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller) 패턴이라고 정의할 수 있습니다.
이것을 보다 자세히 설명하자면 클라이언트로부터 어떠한 요청이 오면 WAS(Web Application server)의 Tomcat(톰캣)과 같은 서블릿 컨테이너(Servlet Container)가 요청을 받게 됩니다.
그리고 이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 됩니다.
그러면 디스패처 서블릿은 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아서 작업을 위임합니다.
프론트 컨트롤러(Front Controller), 서블릿 컨테이너(Servlet Container)란?
프론트 컨트롤러란?
주로 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아서 처리해주는 컨트롤러로써 MVC 구조에서 함께 사용되는 디자인패턴입니다.
서블릿 컨테이너란?
- 서블릿을 담아둔 바구니라고 생각하면 됩니다.
- 서블릿들의 생성, 실행, 파괴를 담당합니다. (서블릿 생명주기를 관리합니다.)
- 멀티쓰레드 지원 및 관리합니다.
- 선언적인 보안을 관리합니다.
- 서블릿 컨테이너는 client의 request를 받아주고 response 할 수 있게, 웹서버와 소켓을 만들어 통신합니다.
- 대표적으로는 무료 서비스인 Tomcat(톰캣)이 있습니다.
- 톰캣은 웹서버와 소켓을 만들어 통신하며 JSP(java server page)와 Servlet이 작동할 수 있는 환경을 제공합니다.
웹 서버란(Web Server)?
- 웹페이지를 사용자에게 전송하는 서버입니다.
- 일반적인 상황에서 사용자는 브라우저에 URL을 입력합니다.
- 웹서버는 데이터를 전송하기 위해 HTTP 프로토콜을 사용합니다. 그리고 사용자는 웹페이지를 얻게 됩니다.
- 웹서버가 하는 일은 웹페이지를 사용자에게 전송하는 것입니다.
웹서버와 서블릿 컨테이너는 어떻게 요청을 처리할까?
- 웹서버가 HTTP 요청을 받는다
- 웹서버는 요청을 서블릿 컨테이너로 전달합니다.
- 서블릿이 컨테이너에 없다면, 서블릿을 동적으로 검색하여 컨테이너의 주소 공간에 로드한다
- 컨테이너가 서블릿의 init() 메소드를 호출하면, 서블릿이 초기화된다
: 서블릿이 처음 로드됬을 때 한번만 호출 - 컨테이너가 서블릿의 service() 메소드를 호출하여 HTTP 요청을 처리한다.
(요청의 데이터를 읽고, 응답을 만들어낸다)
서블릿은 컨테이너 주소에 남아있고, 다른 HTTP 요청들을 처리할 수 있습니다. - 웹서버는 동적으로 생성된 결과를 올바른 위치에 반환한다.
Dispatcher Servlet(디스패처 서블릿)의 장점은?
Spring MVC는 디스패처 서블릿이 등장함에 따라 web.xml의 역할을 상당히 축소시켜주었습니다.
과거에는 모든 서블릿을 URL매핑을 위해 web.xml에 모두 등록해주어야 했지만,
디스패처 서블릿이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링 해주고,
공통 작업을 처리하면서 상당히 편리하게 이용할 수 있게 되었습니다.
컨트롤러를 구현해두기만 하면 디스패처 서블릿이 알아서 적합한 컨트롤러로 위임을 해주는 구조가 되었습니다.
Spring Web MVC에서의 Dispatcher Servlet(디스패처 서블릿) 동작과정
Spring Web MVC란?
Model View Controller의 약자이며, 어플리케이션을 구송하는 요소를 역할에 따라 세 가지 모듈로 나누어 구분한 패턴입니다.
Mode(모델)
- 어플리케이션의 데이터를 의미하며, 모든 데이터 정보를 가공하여 가지고 있는 컴포넌트 입니다.
- 사용자가 이용하려는 모든 데이터를 가지고 있어야 하며, View, Controller에 대해 어떠한 정보도 알 수 없어야 합니다.
- 변경이 일어나면 처리 방법을 구현해야 합니다.
View(뷰)
- 시각적인 UI요소를 지칭하는 용어입니다.
- 모델이 가지고 있는 데이터를 저장하면 안됩니다.
- 모델이나 컨트롤러에 대한 정보를 알면 안되며 단순히 표시를 해주는 역할을 가지고 있습니다.
- 변경이 일어나면 처리 방법을 구현해야 합니다.
Controller(컨트롤러)
- 모델과 뷰를 연결해주는 역할을 합니다.
- 모델 또는 뷰에 대한 정보를 알아야 합니다.
- 모델 또는 뷰의 변경을 인지하여 대처를 합니다.
Spring MVC의 동작순서
1. 핸들러 조회
클라이언트의 HTTP요청을 디스패처 서블릿(가장 먼저 요청을 받는 프론트 컨트롤러)이 받습니다.
즉, 서블릿 컨텍스트(웹 컨텍스트)에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게됩니다.
이를 그림으로 표현하면 아래의 그림과 같습니다.
(실제로는 Interceptor가 Controller로 요청을 위임하지 않으므로 아래의 그림은 처리 순서를 도식화 한 것으로만 이해합니다.)
그리고 요청에 따라 적절한 controller를 찾기 위해 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회합니다.
💡 Spring Framework에서 제공하는 HandlerMapping (실제로는 더 많습니다.)
- RequestMappingHandlerMapping : 애노테이션 기반의 컨트롤러인 @RequestMapping에서 사용
- BeanNameUrlHandlerMapping : 스프링 빈의 이름으로 핸들러를 찾는다.
2. 핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터를 조회합니다.
디스패처 서블릿은 컨트롤러 요청을 직접 위임하는 것이 아니라 HandlerAdapter를 통해 컨트롤러로 요청을 위임합니다.
이때 Adapter를 통해 컨트롤러를 호출하는 이유는 공통적인 전/후처리 과정이 필요하기 때문입니다.
대표적으로 요청 시에 @RequestParam, @RequestBody 등을 처리하기 위한 ArgumentResolver들과 응답 시에 ResponseEntity의 Body를 Json으로 직렬화하는 ReturnValueHander들의 어댑터를 통해 처리됩니다.
그래서 디스패처 서블릿은 대신 컨트롤러로 요청을 위임할 HandlerAdater 구현체인 RequestMappingHandlerAdapter를 찾습니다.
그리고 앞서 찾은 HandlerMethodExecutionChain이 갖는 인터셉터들을 모두 실행한 다음에 HandlerAdapter를 통해 컨트롤러의 메소드를 호출하도록 요청을 위임합니다.
💡 Spring Framework에서 제공하는 HandlerAdapter
- RequestMappingHandlerAdapter : 애노테이션 기반의 컨트롤러인 @RequestMapping에서 사용
- HttpRequestHandlerAdapter : HttpRequestHandler 처리
- SimpleControllerHandlerAdapter : Controller 인터페이스(애노테이션 X, 과거에 사용 처리)
3. 핸들러 어댑터 실행 : 핸들러 어댑터를 실행합니다.
요청을 처리할 대상 정보인 HandlerMethod 객체에는 컨트롤러 정보와 메소드 객체가 있으므로 리플렉션의 메소드 객체를 invoke 합니다.
사실 엄밀히 말해서는 HandlerMethod에 컨트롤러 빈 이름과 메소드, 빈 팩토리가 있어서 빈 팩토리에서 컨트롤러 빈을 찾는 등의 작업이 일어납니다.
리플렉션(Reflection)이란?
- 클래스의 구조를 분석하여 동적 로딩을 가능하게 하는 기능입니다.
- class, constructor, method, field 정보를 가져와서 객체를 생성하거나 메서드를 호출하거나 변수의 값을 변경할 수 있습니다.
- 작성 시점에는 어떠한 클래스를 사용해야 할지 모르지만 런타임 시점에서 클래스를 가져와서 실행해야 하는 경우 필요합니다.
- 대표적으로는 Spring 프레임워크의 어노테이션 같은 기능들이 리플렉션을 이용하여 프로그램 실행 도중 동적으로 클래스의 정보를 가져와서 사용합니다.
4. 핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행합니다.
이후에 핸들러(controller)는 service를 호출하고 비즈니스 로직들을 실행합니다.
5. ModelAndView 반환 : 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환합니다.
Model은 Controller가 처리한 결과이고, View는 그 결과를 담는 페이지라고 할 수 있습니다.
💡View가 아닌 ResponseEntity를 반환할 수 있습니다.
비즈니스 로직이 처리된 후에는 컨트롤러가 반환값을 반환합니다.
요즘 프론트엔드와 백엔드를 분리하고, MSA(MicroService Architecture)로 가고 있는 시대에서는 주로 ResponseEntity를 반환합니다. (디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환됩니다.)
6. viewResolver 호출 : 뷰 리졸버를 찾고 실행합니다.
보통은 Controller가 View를 String 타입으로 주기 때문에 View Resolver가 그 이름을 가지고 실제 View를 찾아줍니다.
7. View 반환 : 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환합니다.
그럼 Controller에서 만든 모델(데이터)을 Dispatcher Servlet이 View에 심어줍니다.
8. 뷰 렌더링 : 뷰를 통해서 뷰를 렌더링 합니다.
Dispatcher Servlet(디스패처 서블릿)에 의한 특수한 빈(Beans)
그럼 이쯤에서 Dispatcher Servlet(디스패처 서블릿)에 의해 특수한 빈으로 검출되는 것을 다시 정리해보겠습니다.
HandlerMapping
- 사전/사후 처리를 위한 인터셉터 목록과 함께 request를 매핑합니다.
- 매핑은 일부 기준에 기초하지만 핸들러매핑 구현체에 따라 세부사항이 달라집니다.
- 두 가지 주요 핸들러매핑 구현체에는 RequestMappingHandlerMapping(@RequestMaping을 지원합니다.)와 SimpleUrlHandlerMapping(URI 경로 패턴의 명시적 등록을 유지한다.)가 있습니다.
HandlerAdapter
- 핸들러가 실제로 호출되는 방식에 관계없이 요청에 매핑된 핸들러를 호출하도록 도와줍니다.
- 예를들어 어노테이션이 달린 컨트롤러를 호출하려면 어노테이션을 해석해야 합니다.
- HandlerAdapter의 주목적은 그러한 세부작업들로부터 DispatcherServlet을 자유롭게 하는 것입니다.
HandlerExceptionResolver
- 예외처리, 핸들러에 HTML 오류나 기타 대상들을 매핑하는 전략입니다.
ViewResolver
- 핸들러에서 리턴 된 논리적인 String 기반의 뷰 이름을 response에 뷰로 렌더링 하기 위한 실제 이름으로 해석합니다.
LocaleResolver, LocaleContextResolver
- 국제화 된 뷰를 제공하도록 클라이언트가 사용하고 있는 Locale과 그에 맞는 timezone을 해석합니다.
ThemeResolver
- 개인적인 레이아웃을 제공하기 위해 웹 어플리케이션이 사용가능한 테마를 제공합니다.
MultipartResolver
- 몇몇의 멀티파트 파싱 라이브러리를 사용해 multi-part request(파일업로드)을 파싱을 위한 추상체입니다.
FlashMapManager
- 일반적으로 리다이렉트를 통해 하나의 요청에서 다른 요청으로 어트리뷰트(attribute)를 전달하는데 사용할 수 있는 input과 output flashMap을 저장하고 검색합니다.
⭐️ 참고한 곳
https://velog.io/@hanblueblue/%EB%B2%88%EC%97%AD-Spring2-Spring-Web-MVC
https://mangkyu.tistory.com/18?category=761302
https://www.youtube.com/watch?v=2pBsXI01J6M
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
배워야 할 것이 더 많은 주니어 개발자입니다. 🐣
내용 전달보다는 정리를 목적으로 포스팅을 하고 있습니다.
잘못 된 내용이나 부족한 부분은 댓글로 주시면 감사드리겠습니다.
'개발공부 > SPRING' 카테고리의 다른 글
[Spring] 비동기 프로그래밍 & ThreadPoolExecutor - 실습 (0) | 2023.01.09 |
---|---|
[Spring] 비동기 프로그래밍 & ThreadPoolExecutor 생성자 - 개념 (0) | 2023.01.02 |
[Spring] Swagger API 연동하기 (0) | 2022.08.29 |
[Spring] 통합 테스트와 슬라이스 테스트 (0) | 2022.08.15 |
[Spring] @Transactional이 2개라고? 일단 그게 뭔데? (0) | 2022.07.20 |