[Spring] root-contex와 servlet-context
기술/Java & Spring

[Spring] root-contex와 servlet-context

반응형

✏️개요

  이번에 작성할 글의 주제는 root-context와 servlet-context에 관하여다. 이번 주제를 component-scan을 주제로 한 글에 작성할까 생각했지만, 분리해서 작성하는 게 주제에 좀 더 집중할 수 있다고 생각했다. 이 주제에 대해서는 입사 초기에 시간이 많을 때 스프링과 자바에 대해 공부하는 과정에서 한번 접하고 넘어갔었는데, 이번에 component-scan에 대해 알아볼 때 component-scan이 2개가 존재하는 이유에 대해 궁금하여 찾아보는 과정에서 다시 한번 접하게 되어 글로 정리해보려고 마음먹게 되었다.

 

❗ 본 게시글은 필자 개인적인 의견이므로 틀린 부분이 있을 수 있습니다. 댓글을 통해 지적해주시면 감사하겠습니다.

✏️root-context와 servlet-context

  스프링의 MVC패턴에서 일반적으로 사용자의 요청을 받아들이고 실제로 처리하는 컴포넌트에게 요청을 넘기는 것은  DispatcherServlet이 담당한다. 이러한 DispatcherServlet의 구조는 아래의 이미지와 같다.

DispatcherServlet의 계층구조

  • servlet context : 말 그대로 servlet에 대한 context 설정을 작성하며, 그림에도 나와있듯이 Controllers, HandlerMapping, ViewResolver와 같은 웹과 연관되어 있는 bean들을 정의하게 된다.
  • root context : 일반적으로 데이터 저장소(Repositories) 및 비즈니스 서비스(Services)와 같은 여러 요청에 대해서 공유해야 하는 bean들을 정의하게 된다.
  • servlet context에서 root context에 등록된 bean들에 대한 참조가 가능하지만, root context에서 servlet context에 등록된 bean들에 대한 참조는 불가능하다.
위의 root context에 대한 설명을 보면, 여러 요청에 대해서 공유해야 하는 bean을 정의한다고 언급했다. 여기서 주의해서 알고 있어야 하는 점은, 여러 요청이 클라이언트들의 개별 요청을 의미하는 것이 아닌 여러 DispatcherServlet으로부터의 요청이라는 점이다.

✏️component-scan이 2개인 이유

  이 글을 작성하게 된 이유는 개요에서 언급하였듯이, component-scan이 root context와 servlet context에 각각 설정되어 있는 부분이라고 했다. 이제 우리는 DispatcherServlet의 계층 구조에 대해 알기 때문에 이를 설명할 수 있다. 

 

  여러 서블릿이 공유하기 위한 service, repository bean은 root context의 설정 파일에서 생성해주고, servlet 마다 가지는 servlet context에는 Controller bean을 생성해주어야 한다. 그리고 이 bean을 생성하는 방법으로서 앞선 글에서 살펴본 것이 바로 component-scan이며 이것이 component-scan이 servelt context와 root context에 2개가 존재하는 이유이다. 

+) 단순히 component-scan의 base-package를 통한 패키지 스캔 방식으로 각 bean들을 생성해줄 경우, root context와 servlet context 양쪽 모두에 Controller, Service, Repository 등의 bean이 생성되어 불필요한 중복을 발생시킨다. 따라서 앞선 글에서 배운 component-scan의 include와 exclude를 활용하여 적절한 스캔 범위를 지정해 줄 필요가 있다. 

단, 이와 같은 방식은 DispatcherServlet의 계층 구조를 만족하기 위한 설정이며 DispatcherServlet을 1개만 사용하는 경우가 대다수이기 때문에 분리할 필요가 없다고는 한다. 그러나 Spirng 공식문서에서 설명하고 있는 DispatcherServlet의 계층 구조가 가지는 특징을 만족시키기 위해서는 component-scan을 2개 이용하여 분리해주는 것이 좋아 보인다.

  여기까지 root context와 servlet context를 통해 회사 프로젝트의 설정 파일에서 component-scan이 2개 존재하는 이유에 대해서 알아보았다. 찾아보는 과정에서 여러 글을 통해 필자의 회사와 마찬가지로 대다수의 회사에서 역시 이러한 방식으로 Controller, Service, Repository를 적절하게 분리하는 과정을 거친다는 사실을 알 수 있었다.

 

  마지막으로 회사의 프로젝트에서 어떠한 식으로 각 bean들을 분리해 놓았는지 살펴보며 글을 마치고자 한다. 

  • applicationContext-mvc.xml : servlet context 설정 파일로 component-scan을 이용해 Controller beas를 생성한다.
    • 여기에는 mvc:annotation-driven도 함께 설정되어 있는데, 이는 @Controller들에게 HandlerMapping과 HandlerAdapter를 위해 설정하는 것이다.
    • 회사에서는 component-scan을 aspectj타입으로 bean을 생성하기 때문에 Controller들이 @Component로서 bean이 생성된다. 따라서, Controller에는 @Cotroller 어노테이션을 별도로 붙여주어 HandlerMapping과 HandlerAdapter가 정상 동작할 수 있도록 해준다.
  • appicationContext : root context 설정 파일로 component-scan을 이용해 Service beans를 생성한다.
  • applicationContext-mybatis : mybatis의 설정 파일로 MapperScannerConfigurer 클래스의 basepackage로 @Repository에 해당하는 dao 클래스들을 bean으로 생성한다.

✏️마치며

  여기까지 component-scan과 root context, servlet context에 대해서 알아보았다.

 


추가 내용.

 

root context와 servlet context를 설정해주는 부분은 web.xml에 존재하는데, 각 파일들은 모두 contextConfigLocation파라미터의 value 값으로 설정된다. 이를 <servlet> 태그 안에 DispatcherServlet과 함께 사용하면, servlet context의 설정 파일임을 명시하는 것이고 <listenr> 태그 안에 ContextLoaderListener과 함께 사용하면, root context에 대한 설정 파일임을 명시하는 것이다.

 

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

servlet context의 설정 파일을 DispatcherServlet의 contextConfigLocation으로 지정해주고 있다.

 

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-config/applicationContext.xml</param-value>
</context-param>
 
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

root context의 설정 파일을 ContextLoaderListener의 contextConfigLocation으로 지정해주고 있다. 

 

  root context를 이용한 servlet context들 간의 관계는 아래의 이미지와 같다.

각 Servlet은 servlet context를 별개로 사용하며, root context는 각 servlet들이 공유할 수 있는 bean이 생성된다.

 

반응형