HJW's IT Blog

서블릿과 Spring DispatcherServlet: 웹 애플리케이션의 핵심 이해하기 본문

SPRING

서블릿과 Spring DispatcherServlet: 웹 애플리케이션의 핵심 이해하기

kiki1875 2024. 11. 18. 17:33

Servlet

서블릿이란, JAVA를 기반으로 하는 웹 프로그래밍 기술 중 하나로, Dynamic Web Page 를 만들 때 사용되는 자바 기반의 웹 어플리케이션 프로그래밍 기술이다.

웹상에는 다양한 req 와 res 가 있는데, 이러한 요청과 응답을 일일이 처리하게 되면 많은 시간과 노력이 소요 된다. 이때 서블릿이 사용된다. 서블릿이란 복잡한 요청과 응답을 간단한 메서드 호출 만으로 체계적으로 다룰 수 있도록 해주는 기술이다.

개발자가 소스를 작성하게 되면, 컴파일 되어 서블릿 컨테이너에 등록되고, 클라이언트와 소통할 때 컨테이너에 등록된 서블릿을사용하게 된다. 즉, 클라이언트는 서블릿 컨테이너에 요청을 보내고, 서블릿 컨테이너가 처리한 후, HTTP 형식으로 응답을 하는 것이다.

서블릿 컨테이너

위에서 요청이 들어오면, 컨테이너에 등록된 서블릿에게 요청을 위임하는 구조라 설명했다. 그렇다면 이때 어떻게 올바른 서블릿에게 요청이 전달되는 것일까?

이 역할을 서블릿 컨테이너가 담당한다. 클라이언트의 요청이 들어오게 되면, 컨테이너는 HttpServletRequest, HttpServletResponse 를 생성하여, 응답을 반환한다.

HttpServletRequest

클라이언트에서 서버로 전송된 HTTP 요청을 표현하는 객체이다. 클라이언트가 보낸 요청 데이터를 읽고, 요청의 메타데이터를 확인할 수 있다. method 식별, URI, Query String, Header, Parameter, Cookie 등에 접근할 수 있다.

HttpServletResponse

서버에서 클라이언트로 전송되는 HTTP 응답 객체이다. 응답 상태 코드, 헤터, 콘텐츠, 쿠키 등을 설정할 수 있다.

서블릿 작성 예시

public class myServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init method 호출!");
    }
    
    @Override
    public void destroy() {
        System.out.println("destroy method 호출!");
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        System.out.println("doGet service method 호출!");		
    }
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        System.out.println("doPost service method 호출!");		
    }
	
}

서블릿 컨테이너의 주요 기능은 다음과 같다.

생명 주기 관리

  • 서블릿 컨테이너가 시작되면, 컨테이너는 명시된 web.xml 혹은 어노테이션을 통해 서블렛 클래스를 찾아 로드한다. 이때, 서블릿의 상세 설정에 따라 로드 될 수도 안될 수도 있다. 로드된 서블릿 클래스를 인스턴스화 하고, init() 메서드를 호출하여 초기화 작업을 수행한다.
  • 클라이언트의 요청이 들어오면, service() 메서드를 호출한다. 위 예시의 doGet 혹은 doPost 가 service 에 해당한다.
  • 컨테이너가 종료되거나, 서블릿이 더이상 필요 없을 경우, destroy() 메서드를 통해 삭제하며, garbage collertor 를 통해 메모리에서 제거한다.

통신 지원

  • 서블릿 컨테이너는 당연하지만, 서버와 클라이언트 간의 통신을 지원한다. 이로 인해 개발자는 WebSocket 프로그래밍 과 같은 복잡성을 신경쓰지 않아도 된다.
  • 서블릿 컨테이너는 특정 포트를 열고 클라이언트의 요청을 기다리는데, 요청이 들어올 경우, 소켓을 생성하고 연결을 설정한다.

멀티 스레딩 관리

  • 서블릿 컨테이너는 기본적으로 멀티 스레딩 환경에서 동작하도록 설계되어 있다. 즉, 여러 클라이언트의 요청을 동시에 처리할 수 있다.
  • 클라이언트의 요청이 들어올 때마다, 컨테이너는 새로운 스레드를 생성 / 기존의 재사용을 한다. 각 요청은 독립적인 스레드에서 처리되며, Thread Pool 을 통해 효율적인 스레트 관리를 한다.
    • 미리 일정 수의 thread 생성
    • Request시, 미리 만들어둔 thread 사용
    • 사용이 끝나면 pool에 반환
  • 서블릿 인스턴스는 싱글톤으로 관리되어 여러 스레드에서 공유된다.
  • 서블릿이 상태를 공유하는 경우, 동기화 문제가 발생할 수 있다.
  • 동시성 문제 : 서블릿 컨테이너는 각 서블릿 클래스에 대해 단 하나의 인스턴스만 생성하기에, race condition (여러 스레드가 공유 자원을 수정하려 할 때), 혹은 데이터 불일치 같은 결과가 발생할 수 있다.
    • 상태가 없는 서블릿을 설계하거나, 한번에 하나의 스레드만 특정 코드 블럭을 실행하도록 제한, ThreadLocal을 사용 등의 방안이 있다.

선언적인 보안 관리

  • 서블릿 컨테이너는 보안 기능을 지원하는데, 이러한 보안관리는 XML 파일에 서술되기에, 보안이슈로 소스를 수정하더라도, 자바 소스 코드를 다시 컴파일 하지 않아도 된다.
  • 주요 기능으로 Authorization, Authentication , SSL/TLS 등이 있다.

Spring 에서의 Sublet

DispatcherServlet

DispatcherServlet 은 Spring MVC 에서 Front Controller 로 작동하는 서블릿이다. DispatcherServlet은 모든 HTTP 요청을 가로체어, 다른 구성요소로 전달하는 역할을 한다. 즉 Request에 대한 라우팅을 담당하는 서블릿 이다.

이 서블릿은 HandlerMapping 을 통해 요청을 처리한 컨트롤러를 찾고, HandlerAdapter를 통해 요청을 위임한다. 컨트롤러가 비즈니스 로직을 처리한 후라면, ViewResolver를 통해 렌더링을 한다.

다음은 공식 문서에서의 DispatcherServlet 에 대한 설명이다.

DispatcherServlet 은 다른 모든 서블릿과 마찬가지로, 서블릿 명세에 따라 선언되며, 매핑되어야 한다. Java Configuration 혹은 web.xml 을 통해 이루어져야 하며, Spring 설정을 통해 다음과 같은 위임 컴포넌트들을 찾아 동작한다/.

  • Request Mapping
  • View Resolver
  • Exception Handling

DispatcherServlet 설정 예제

public class MyWebApplicationInitializer implements WebApplicationInitializer {

	@Override
	public void onStartup(ServletContext servletContext) {

		// Spring 웹 애플리케이션 설정 로드
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		context.register(AppConfig.class);

		// DispatcherServlet 생성 및 등록
		DispatcherServlet servlet = new DispatcherServlet(context);
		ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
		registration.setLoadOnStartup(1); // 애플리케이션 시작 시 로드
		registration.addMapping("/app/*"); // "/app/*" URL 매핑
	}
}

Spring Applicaiton 이 시작할 때, 서블릿 컨테이너가 이 DispatcherServlet을 자동으로 감지하여 동작한다.

상세 설명

  1. WebApplicationInitializer 인터페이스:
    • onStartup 메서드를 구현하여 서블릿 컨텍스트(ServletContext)에 DispatcherServlet을 등록한다.
    • 서블릿 컨테이너가 애플리케이션을 시작할 때 호출된다.
  2. AnnotationConfigWebApplicationContext:
    • Java 기반 설정을 사용하는 Spring 애플리케이션 컨텍스트를 초기화한다.
    • AppConfig.class는 Spring 설정을 정의한 클래스이다.
  3. DispatcherServlet 생성 및 등록:
    • DispatcherServlet 객체를 생성하고 Spring 컨텍스트와 연결한다.
    • ServletContext.addServlet을 통해 서블릿 컨테이너에 등록한다.
    • setLoadOnStartup(1): 애플리케이션 시작 시 해당 서블릿을 초기화한다.
    • addMapping("/app/*"): 해당 서블릿이 처리할 URL 패턴을 설정한다.

web.xml 예시

<web-app>

	<!-- Spring ContextLoaderListener 등록 -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Spring 설정 파일 위치 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/app-context.xml</param-value>
	</context-param>

	<!-- DispatcherServlet 등록 -->
	<servlet>
		<servlet-name>app</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value></param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- URL 매핑 -->
	<servlet-mapping>
		<servlet-name>app</servlet-name>
		<url-pattern>/app/*</url-pattern>
	</servlet-mapping>

</web-app>

상세 설명

  1. ContextLoaderListener:
    • Spring 애플리케이션 컨텍스트를 초기화하고 애플리케이션 전반에 공유할 수 있도록 설정한다.
    • contextConfigLocation 파라미터로 Spring 설정 파일 위치를 지정한다.
  2. DispatcherServlet 등록:
    • <servlet-class>로 DispatcherServlet을 정의한다.
    • contextConfigLocation 초기화 매개변수로 추가 설정 파일 위치를 지정할 수 있다.
    • <load-on-startup>: 서블릿이 애플리케이션 시작 시 초기화되도록 설정한다.
  3. URL 매핑:
    • <servlet-mapping>에서 특정 URL 패턴을 지정하여 DispatcherServlet이 해당 패턴의 요청을 처리하도록 설정한다.

Spring 에서

Spring boot은 기본적으로 Auto-Configuration 을 사용하기에, 명시적으로 정의하지 않아도 되는데, 동작 과정은 다음과 같다.

  • @SpringBootApplication 어노테이션이 붙은 Main 클래스가 DispatcherServlet 을 초기화 한다. (spring-boot-starter-web) 을 사용중이라면 자동으로 동작한다.

'SPRING' 카테고리의 다른 글

Spring Bean 과 의존관계 설정 두가지 방법  (0) 2024.11.25
Spring 웹 개발 기초  (1) 2024.11.25
IoC Container 과 Bean에 대한 정리  (0) 2024.11.25
[SPRING BOOT] MVC Pattern  (0) 2024.05.28
[SPRING BOOT] IOC, DC, BEAN 에 대해  (0) 2024.05.28