[Spring] dispatcherServlet이란

스프링 프레임워크의 MVC에서 주로 사용되는 DispatcherServlet에 대한 정리를 하였다.

 

1. DispatcherServlet의 정의

스프링 프레임워크에서 HTTP 요청을 처리하고 적절한 핸들러(컨트롤러)에게 전달하는 핵심 컴포넌트.

다시 말하면, 클라이언트의 요청을 적절한 컨트롤러에게 라우팅하고
☞ 컨트롤러의 실행 결과를 적절한 형태의 응답으로 변환하여 클라이언트에게 전달하는 역할을 한다.  

 

2. DispatcherServlet 필요성

1. 중앙 집중적인 요청 처리
DispatcherServlet을 통해 모든 웹 요청이 중앙에서 관리되고 분배되어 코드를 구성할 수 있다.

2. 코드 모듈화
여러 컨트롤러로 분리하여 모듈화된 코드를 작성할 수 있다.

3. 유연한 View 처리
다양한 View Resolver를 이용하여 다양한 View를 지원할 수 있다.

 

3. DispatcherServlet 구성요소

DispatcherServlet에서는 아래의 구성 요소들을 이용하여 웹 어플리케이션의 요청 처리를 담당하고,
설정은 XML 또는 Java Config를 통해 이루어진다.

 

- Handler Mapping

클라이언트의 요청이 어떤 컨트롤러에 의해 처리될지를 결정하는 역할을 한다.
Spring은 여러 가지 Handler Mapping 전략을 제공한다. 몇 가지 주요 기능은 아래와 같다.

● BeanNameUrlHandlerMapping: 빈의 이름과 URL을 매핑하여 컨트롤러를 찾는다.
● DefaultAnnotationHandlerMapping: @ReqeustMapping을 사용한 메서드와 URL을 매핑하여 컨트롤러 찾는다.
● RequestMappingHandlerMapping: @RequestMapping을 사용한 메서드와 URL을 매핑하여 컨트롤러 찾는다.

 

- Handler Adapter

실제 컨트롤러(Handler)의 실행을 담당한다.
각 컨트롤러는 특정한 메서드 시그니처를 가지고 있는데, Handler Adapter를 통해 각 시그니처 정보를 읽고 호출하는 역할을 한다. 몇 가지 주요 기능은 아래와 같다.

● SimpleControllerHandlerAdapter: Controller 인터페이스를 구현한 컨트롤러에 대한 어댑터이다.
● HttpRequestHandlerAdapter: HttpRequestHandler 인터페이스를 구현한 컨트롤러에 대한 어댑터이다.
● RequestMappingHandlerAdapter: @RequestMapping을 사용하여 메서드를 처리하는 어댑터이다.

 

- Handler Interceptor

컨트롤러의 실행 전후에 추가적인 작업을 수행한다.
주로 로깅, 인증, 권한 검사 등을처리하는데 사용한다.

● preHandle(): 컨트롤러가 호출되기 전에 실행된다.
● postHandle(): 핸들러가 실행은 완료되었지만, 아직 View가 생성되기 이전에 호출된다. 
● afterCompletion(): 모든 View에서 최종 결과를 생성하는 일을 포함한 모든 작업이 완료된 후에 실행된다.

단, 모든 메서드는 preHandle의 리턴값이 true일때 실행되고
인터셉터가 여러개 적용되있는 경우는 preHandle는 역순 호출된다.

 

- View Resolver

컨트롤러가 반환한 뷰 이름을 실제 뷰 객체로 변환하는 역할을 한다.

● InternalResourceViewResolver: JSP와 같은 내부 리소스를 처리하는 뷰 리졸버이다.
● FreeMarkerViewResolver, Velocity ViewResolver 등: 다양한 템플릿 엔진을 사용하는 경우, 해당 엔진에 맞는 뷰 리졸버를 설정할 수 있다.

 

- Multipart Resolver

Multipart Resolver는 멀티파트 파일 업로드를 처리하는데 사용한다.

● CommonsMultipartResolver: Apache Commons FileUpload를 기반의 Multipart 리졸버이다.

 

- Locale Resolver

클라이언트의 로케일을 해석하는 역할을 한다.

● CookieLocaleResolver: 쿠키를 사용하여 로케일을 설정하는 리졸버이다.

 

- Theme Resolver

웹 어플리케이션의 테마를 결정하는데 사용한다.

FixedThemeResolver: 고정된 테마를 사용하는 리졸버이다.

 

- Content Negotiation Resolver

클라이언트 요청의 Accept 헤더를 기반으로 어떤 미디어 타입을 사용할 건지를 결정한다.

● ContentNegotiating ViewResolver: 여러 미디어 타입에 대한 뷰 리졸버를 관리하고 선택하는 리졸버이다.

 

- FlashMapManager

리다이렉트 후에도 데이터를 유지하기 위한 일시적인 데이터 저장소를 제공한다.

● SessionFlashMapManager: 세션을 사용하여 FlashMap을 관리하는 매니저이다.

 

- Exception Resolver

예외가 발생했을 때 어떻게 처리할지를 결정한다.

● SimpleMappingExceptionResolver: 예외와 뷰 이름을 매핑하여 예외 처리를 담당하는 리졸버이다.

 

4. DispatcherServlet 동작 과정

1. 먼저 클라이언트의 요청을 수신한다
     - 클라이언트가 HTTP 요청을 보내고, DispatcherServlet이 해당 요청을 받는다. 

☞ 2. Handler Mapping (= 요청을 매핑한다)
    - Handler Mapping은 URL을 통해 요청을 어떤 컨트롤러에서 처리할지 매핑을 결정한다.

☞ 3. Handler Execution (= 요청을 수행한다)
    - 선택한 컨트롤러가 실행되어 비즈니스 로직을 수행하고 모델을 생성하는 등의 요청을 처리한다.

☞ 4. View Resolution (= 뷰를 얻는다)
    - View Resolver를 사용한 컨트롤러의 실행 결과를 통해, View 객체나 이름을 얻는다.

☞ 5. Render View (= 뷰를 반환한다)
    - 찾은 View를 이용하여 응답을 생성하고, 클라이언트에게 반환한다.


5. DispatcherServlet 관련 파일

DispatcherServlet는 아래의 두 가지 방법들을 통해 등록된다.
방법1:  Server 3.0+에서는 Java Config 클래스
방법2:  웹 어플리케이션의 web.xml 파일

 

6. DispatcherServlet 사용 예제

- 방법1:  Java Config 기반 설정

Java Config클래스에서 DispatcherServlet를 설정한다.

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("com.example.config");

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

 

- 방법2: XML 기반 설정 (web.xml에서 설정)

먼저 web.xml에서 dispatcherServlet를 등록하고 설정한다. 

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

그리고 dispatcher-servlet.xml에서 DispatcherServlet을 설정하는 XML파일을 작성한다.

<context:component-scan base-package="com.example.controller" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>

 

- 호출과 응답 부문

먼저 DispatcherServlet은 "/hello"라는 URL로 들어오는 요청을 ExampleController에서 처리한다.

@Controller
public class ExampleController {

    @RequestMapping("/hello")
    public ModelAndView hello() {
        ModelAndView modelAndView = new ModelAndView("hello");
        modelAndView.addObject("message", "Hello, Spring MVC!");
        return modelAndView;
    }
}

그리고 "hello.jsp"라는 View파일(WEB-INF/views/hello.jsp)을 통해 응답을 생성한다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello Page</title>
</head>
<body>
    <h2>${message}</h2>
</body>
</html>