1. FrontController 패턴?
사용자의 요청을 Servlet에게 전달하기 위해서는 web.xml
에 servlet을 등록하고 mapping하는 과정이 필요합니다. 하지만 수 많은 요청이 필요한 어플리케이션의 경우 계속해서 servlet을 등록하고 mapping하는 과정이 필요로하게 됩니다. web.xml을 별도로 관리해주어야 하는 불편함이 있습니다. 이 때문에 새로운 패턴이 생겨났는데요 그것이 바로 FrontController
패턴입니다. 아래의 그림을 보면 조금 더 수월하게 이해하실 수 있을것 같습니다.
1.1 기존의 Servlet
기존의 방식은 요청 url당 servlet을 생성하고 그에맞는 Controller에게 요청을 보내주는 코드를 각각 다 따로 작성해야 했습니다.
1.2 Front Controller 패턴 적용
FrontController 패턴을 적용하면 하나의 Servlet에서 모든 요청을 받아들여 적절한 Controller로 요청을 위임해줍니다. 물론 적절한 Controller로 위임시켜줄 때에는 또 다시 Command 패턴
이란게 필요합니다만, 여기서는 언급하지 않겠습니다.
장점
한곳에서 모든 사용자의 요청을 컨트롤할 수 있는것에는 어떤 장점이 있을까요? 기본적으로 사용자의 모든 요청에 대해 인코딩처리
, 에러 페이지 처리
, 공지
등에 대한 처리를 한곳에서 할 수 있을 것입니다.
2. Dispatcher Servlet
Spring에서는 위와 같은 Front Controller 패턴을 취하는 Servlet을 미리 만들어 두었습니다. 그것이 바로 Dispatcher Servlet입니다. 즉 모든 요청을 한곳에서 받아서 필요한 처리들을 한 뒤, 요청에 맞는 handler로 요청을 Dispatch하고, 해당 Handler의 실행 결과를 Http Response형태로 만드는 역할을 합니다.
2.1 구조
DispatcherServlet
은 앞선 포스팅에서 살펴본 ContextLoaderListener
에 의해 ServletContext
에 등록된 ApplicationContext
를 상속받아 WebApplicationContext
를 생성합니다. 위 그림의 Root WebApplicationContext
가 ContextLoaderListener에 의해 등록된 ApplicationContext이며 Servlet WebApplicationContext
가 그것을 상속받아 DispatcherServlet
에서 자동으로 만든 ApplicationContext입니다.
요약을 하자면 아래와 같습니다.
Root WebApplicationContext
ContextLoaderListener
에 의해 ServletContext
에 등록되는 ApplicationContext로 모든 Servlet이 사용가능합니다.
Servlet WebApplicationContext
DispatcherServlet
에서 Root WebApplicationContext
를 상속받아 만든 ApplicationContext 으로 해당 DispathcerServlet
안에서만 사용이 가능합니다.
2.2 구조의 이유
위의 그림을 자세히 보시면 Root WebApplicationContext에는 모든 Servlet에서 공용으로 사용가능하게 보이는 Services, Repositories
Bean이 등록되어 있는것을 볼 수 있습니다. 때문에, DispatcherServlet
이 여러개가 필요한 Application이 있을 수도 있기 때문에, Root WebApplicationContext
을 DispatcherServlet
에서 다시 상속을 받아 필요한 기능을 추가하여 사용하도록 구조를 만들어 두었다고 합니다.
3. DispatcherServlet 사용하기
3.1 web.xml 수정
xxxxxxxxxx
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>galid.com.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>galid.com.WebConfig</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
web.xml을 위와 같이 설정하면 됩니다. 우선 이전에 등록했던 helloServlet 설정을 제거합니다.
그 다음 DispathcerServlet에서도 Root WebApplicationContext를 상속받아 또 하나의 WebApplicationContext를 만들어 낸다고 말씀 드렸었습니다. 따라서 DispatcherServlet의 <init-param>
태그를 이용해 적절한 ApplicationContext Class를 지정해주고 contextConfigLocation을 알려주어야 합니다.
그 후 servlet을 등록하듯이 DispatcherServlet
을 등록하고 원하는 url로 매핑하면 됩니다. 위의 경우 /app/*
으로 설정하였으므로 /app으로 시작하는 모든 url을 위의 DispatcherServlet이 받아서 처리하게 됩니다.
3.2 Servlet Configuration Class 생성
public class WebConfig {
}
web.xml에 등록한 DispatcherServlet의 ApplicationContext의 Configuration Class를 생성해야합니다. AppConfig
와 마찬가지로 @Configuration, @ComponentScan
을 부여했습니다.
3.3 Controller 생성
public class HelloController {
HelloService helloService;
"/hello") (
public String hello(){
return "hello, " + helloService.getName();
}
}
간단한 Controller를 생성합니다. web.xml에 따로 등록하지 않아도 @RestController
에 의해 IoC Container에 bean으로 등록될 것이며, DispatcherServlet에 의해 요청이 이 Controller로 전달될 것입니다. 그러나 지금의 구조에는 약간의 문제가 있습니다.
<현재 IoC Container 상태>
위의 그림은 현재 IoC Container에 등록된 Bean들의 현황입니다. 앞서서 DispatcherServlet
의 구조의 이유를 알아봤을때 Root WebApplicationContext
에는 다른 모든 DispatcherServlet
에서 공통으로 사용할 수 있는 Service, Repository
와 같은 빈들을 등록하고, 그것을 상속받아 DispatcherServlet
에서 새로 생성하는 WebApplicationContext에서 Web과 관련된 Controller
와 같은 Bean들이 등록되어야 한다고 말씀 드렸었습니다.
3.4 WebApplicationContext 계층구조 만들기
계층 구조를 만드는 방법은 간단합니다. Root WebApplicationContext에는 Service, Repository
관련 Bean들만을 등록하고 그것을 상속받아 만들어지는 Servlet WebApplicationContext에 웹과 관련된 Controller
등의 Bean들을 등록해주면 됩니다.
AppConfig Class 수정
excludeFilters = .Filter(Controller.class)) (
public class AppConfig {}
첫째로 Root WebApplicationContext의 설정 파일 Class인 AppConfig
를 위와 같이 수정해야 합니다. 설명드리면 @Controller
어노테이션이 부여된 Bean들을 제외한 나머지 Bean들만을 등록하겠다는 뜻입니다.
WebConfig Class 수정
useDefaultFilters = false, includeFilters = .Filter(Controller.class)) (
public class WebConfig {
}
그 후 Servlet WebApplicationContext의 설정파일 Class인 WebConfig
를 수정하여 Controller
Bean들만을 등록하도록 해야합니다.
<계층구조 적용 후 IoC Container 상태>
위의 그림은 계층 구조를 적용한 후의 ApplicationContext에 등록된 Bean들의 상태입니다. Root WebApplicationContext에는 앞서 말했듯이 모든 DispatcherServlet에서 공통적으로 사용할 수 있는 기능을 가진 Bean들(Service,Repository)등이 등록되고, 그것을 상속 받은 Servlet WebApplicationContext에 웹과 관련된 Bean(Controller)들이 존재하는 것을 볼 수 있습니다.
결과 화면입니다.
4. DispatcherServlet의 IoC Container만을 이용하기
앞선 방법처럼 IoC Container를 계층 구조로 나누어 사용하는 방법도 있지만 그냥 사용하는 방법도 있습니다. 사실 요새는 하나의 DispatcherServlet
에서 모든 요청을 받아 처리하는 추세이기 때문에 앞선 방법처럼 계층을 나누는 경우가 드물기도 합니다.
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>galid.com.WebConfig</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
방법은 간단합니다. xml에서 앞서 등록했던 listener와 context-param
태그를 없애주고, WebConfig class의 @ComponentScan 어노테이션의 옵션만 제거하면 됩니다.