[스프링/Spring] 스프링 MVC? DispatcherServlet 이해하기

kindof

·

2021. 11. 7. 00:47

0. 들어가면서

이전에 작성했던 포스팅에서는 스프링 MVC 프레임워크의 기초가 되는 FrontController(프론트 컨트롤러) 패턴에 대해 공부했습니다.

 

프론트 컨트롤러는 통해 개별 컨트롤러가 공통적으로 수행해야 하는 로직을 없애줄 수 있었고, 개별 컨트롤러가 일일이 서블릿의 request, response를 핸들링할 필요가 없게 만들어주었습니다.

 

그리고 이번 포스팅에서 자세히 다뤄볼 DispatcherServlet은 이러한 FrontController 패턴을 그대로 받아들여 스프링 MVC 프레임워크에서 발전시킨 개념입니다.

 

 

1. 스프링 MVC의 구조

아래 그림은 스프링 MVC 구조를 보여줍니다.

스프링 MVC 구조

DispatcherServlet이 어떤 역할을 하는지 이해하기 위해서 위 구조에 대해 간략하게 설명을 해보려고 합니다.

 

1. 그림에서 볼 수 있다시피, 모든 클라이언트의 요청은 DispatcherServlet으로 모이게 됩니다.

 

2. DispatcherServlet은 클라이언트의 요청에 대해 핸들러 매핑, 핸들러 어댑터 조회를 하는데요.

스프링에서 RequestMappingHandlerMapping은 스프링 빈(Bean) 중에서 클래스 레벨에 @RequestMapping가 붙은 녀석을 매핑 정보로 인식 하게 됩니다. 그리고 RequestHandlerAdaper를 통해 지금 찾은 핸들러가 클라이언트의 요청을 수행할 수 있는 핸들러인지를 확인하는 것이죠.

 

3. 만약 클라이언트 요청에 해당하는 컨트롤러를 찾았다면, 컨트롤러에서는 @Service를 이용해 비즈니스 로직을 처리하거나 컨트롤러 안에서 작은 비즈니스 로직을 수행하고, View에 넘겨줄 정보를 모델(Model)에 담아서 돌려주게 됩니다.

 

4. 스프링 MVC로 개발을 해보신 분들은 알겠지만, 컨트롤러는 뷰(View)의 이름을 논리적인 스트링 형태로 리턴해주는데요. 따라서 스프링의 viewResolver를 통해 진짜 View 경로를 찾아갈 수 있도록 매핑을 해주게 됩니다. 예를 들어, 컨트롤러에서 return "index"라고만 해도 viewResolver를 거치면 "/index.html"이 찾아지는 것처럼 말이죠.

 

5. 이제 뷰에는 Model 객체가 함께 넘어가게 됩니다. 그래서 JSP나 Thymeleaf 같은 템플릿 엔진은 해당 모델을 받아서 동적인 웹페이지를 생성할 수 있게 되고, 꼭 이러한 템플릿 엔진이 아니어도 JSON 형태로 모델의 값을 매핑받아 페이지를 생성하게 되죠.

 

* 대략적인 과정을 설명해보았는데, 만약 1~5 과정이 이해가 안되신다면 위에서 링크로 올린 FrontController에 대해 정리한 포스팅을 꼭 참고해주세요.

 


 

자, 그러면 DispatcherServlet이 어떻게 생겼길래 위의 과정을 처리할 수 있는지 코드를 보도록 하겠습니다.

 

2. DispatcherServlet 코드 살펴보기

인텔리제이(Intellij)에서 DispatcherServlet.java 코드 안으로 들어가보면 아래와 같은 설명이 쭉 나와있습니다.

 

설명을 조금 읽어보시면, DispatcherServlet은 HTTP request controller를 위한 가장 중심에 있는 dispatcher라고 되어 있습니다. 여기서 Dispatcher라는 단어는 '급파하다'라는 뜻을 가지고 있기 때문에 클라이언트의 요청을 개별 컨트롤러를 찾아서 던져주는 느낌을 받을 수 있죠. 

DispatcherServlet - 1

그리고 좀 더 읽어보다보면  위에서 설명했던 HandlerMapping, HandlerAdapter에 대한 내용이 나와있고 그 아래로 ViewResolver에 대한 내용 등이 쭉 나와있습니다.

DispatcherServlet - 2

 

자, 그럼 구체적인 코드를 보겠습니다. 실제로 Dispatcher.java 소스 코드를 보면 1500줄이나 되기 때문에 이걸 다 보는건 불가능해서 부분적으로 보겠습니다😓

 

우선 가장 기본적으로 servlet이 호출되면 아래와 같이 doServlce() 메서드가 호출됩니다. doService()의 코드를 쭉 읽다보면 아래에서 doDispatch() 메서드가 호출되는데요. 메서드 이름에서 알 수 있다시피, 실제로 dispatch를 실행하는 과정이 진행됩니다.

 

 

그러면 이제 doDispatch() 메서드로 넘어가서 하나하나 살펴보겠습니다.

 

[1] 핸들러 조회, 핸들러 어댑터 조회

가장 먼저 doDispatch()에서는 클라이언트의 요청에 해당하는 핸들러를 조회하고, 해당 핸들러를 받기 위한 어댑터를 조회합니다.

 

[2] 위에서 찾은 핸들러 어댑터의 handle() 메서드를 호출합니다. 이를 통해 ModelAndView를 반환하죠. 그리고 아래에서 processDispatchResult() 메서드를 호출하는데요. 

 

[3] 뷰 렌더링 호출

processDispatchResult() 메서드는 안에서 render() 메서드를 또 다시 호출합니다.

 

[4] 뷰 렌더링

render() 메서드 안에서는 뷰 리졸버(viewResolver)를 통해 뷰를 찾고, 해당 view를 반환받습니다. 그리고 마지막으로 뷰를 실제로 렌더링하죠.

 

DispatcherServlet.java 코드가 굉장히 길기 때문에 위에서 설명한 핵심 동작 원리가 어떤 코드들에 의해 실행되는지만 살펴봤습니다.

 

이 코드를 따라가보면 결국 핸들러 조회 → 핸들러 어댑터 조회 → 핸들러 어댑터 실행 → 핸들러 실행 → ModelAndView 반환 → ViewResolver 호출 → View 반환 → 뷰 렌더링의 순서대로 dispatcherServlet에 의해 동작하는 것을 볼 수 있죠.

 

이해가 되시나요?


 

이렇게 이번 포스팅에서는 FrontController 패턴을 기반으로 설계된 DispatcherServlet에 대해 공부해봤습니다. 스프링 MVC 프레임워크의 동작 원리를 이해하기 위해서 조금 디테일하게(?) 공부를 한 번 해봤는데요.

 

이 과정을 통해 저도 그냥 아무 생각없이 썼던 @Controller, @RequestMapping, Model 등이 어떻게 동작하는지 생각해볼 수 있게 되었고, 클라이언트의 요청부터 뷰 렌더링까지의 과정이 어떻게 되는지 이해할 수 있었던 것 같습니다.

 

감사합니다.

 

3. Reference