서블릿 컨테이너의 이해
2025. 4. 4. 20:29

다룰 내용

  • 웹 서버와 웹 어플리케이션 서버
  • WAS의 역할
  • 서블릿
  • 서블릿 컨테이너
  • 정리

웹 서버와 웹 어플리케이션 서버

서블릿 컨테이너를 학습하기 전에 웹 서버와 웹 어플리케이션 서버에 대한 전반적인 흐름에 대해서 이해 하는 것이 서블릿 컨테이너의 동작을 이해하는데 더 도움 될 것이라고 생각한다. 사용자에게 요청이 들어오면 웹 서버와 WAS를 거쳐 데이터베이스에서 사용자가 요청한 데이터를 가지고와서 사용자에게 응답을 내려준다. 서블릿 컨테이너는 WAS 내부에서 동작한다.

웹 서버는 정적인 페이지를 사용자에게 보여주고 웹 WAS는 웹 서버 + 프로그램을 실행하는 역할을 수행한다. 웹 서버와 웹 어플리케이션 서버에 대한 내용도 깊게 다루면 여러가지 기술이 있겠지만 서블릿 컨테이너에 대한 글이기 때문에 건너뛰겠다.

웨https://sigridjin.medium.com/servletcontainer%EC%99%80-springcontainer%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%8B%A4%EB%A5%B8%EA%B0%80-626d27a80fe5

WAS의 역할

 

다음은 서버에서 처리해야하는 업무이다. 서버에 들어오는 각 요청마다 처리해야하는 비즈니스 로직은 다르겠지만 그 전까지 사용자로부터 요청을 받는 과정과 응답을 생성한 후에 응답을 사용자에게 전달하는 과정은 계속해서 반복된다.

서블릿

위에서 반복되는 요청들을 처리하는 것이 바로 서블릿이다. 현재는 거의 대부분의 웹 환경에서 http를 사용하기 때문에 http 프로토콜을 처리하는 servlet을 만들면 된다. 만약 다른 프로토콜을 사용한다면 그에 따른 새로운 servlet을 생성해야 한다. 서블릿은 싱글톤으로 생성되는데 이유는 모든 요청마다 서블릿 객체를 새로 생성해서 사용하는 것은 비효율적이기 떄문이다.

그렇다면 어떻게 한번에 여러 사용자의 요청을 받아서 처리하는걸까? 그 이유는 서블릿이 멀티 쓰레드 방식을 사용하기 때문이다. 각 요청에 하나의 쓰레드를 사용해서 요청을 처리한다. 이 멀티쓰레드를 이용한 방식에 대해서도 학습할 것이 많기 때문에 따로 하나의 글로 정리하겠다. 주의할 점은 멀티 쓰레드를 사용하기 때문에 상태를 공유하면 안되고 thread-safe하게 사용해야한다.

나는 지금까지 스프링을 사용하면서 서블릿을 구현한 적이 없는데 어떻게 통신하고 있던걸까?

Spring Web MVC에서 HttpServlet을 내부적으로 지원하기 때문에 따로 구현하지 않더라도 자동으로 적용된다. 

spring-boot-starter-web 라이브러리 내부에 spring-webmvc 내부에 Dispatcher Servlet이 있다.

스프링 MVC에서는 기본적으로 HttpServlet만 지원하는데 WebSocket같은 프로토콜은 어떻게 처리하는걸까?

스프링은 기본적으로 HTTP로 처리하는데 웹 소켓은 처음에 HTTP로 시작한 다음 업그레이드 요청을 통해 프로토콜을 웹소켓으로 전환한다.

서블릿과 디스패처 서블릿은 다른걸까?

HttpServlet를 상속받아서 만든 구현체가 DispatcherServlet이다.

서블릿 컨테이너

서블릿 컨테이너는 여러개의 서블릿을 관리하는 역할을 한다. 서블릿의 생명주기를 관리해주고 요청에 따라 해당 요청을 처리하는 서블릿으로 전달한다. 사용자의 HTTP 요청이 들어오면, 서블릿 컨테이너가 요청 URL에 맞는 서블릿 인스턴스를 찾는다. 서블릿 인스턴스가 존재하지 않으면, 서블릿 클래스를 메모리에 로드하고 인스턴스를 생성한 후, init() 메서드를 호출하여 초기한다.

이후 요청이 들어오면 service() 메서드를 호출하여 요청을 처리하고, 서버가 종료되거나 서블릿이 내려갈 때 destroy() 메서드를 호출하여 서블릿 인스턴스를 정리한다.

서블릿 컨테이너가 서블릿에게 Request를 전달 하는 방법

서블릿 컨테이너는 들어온 Http 요청을 서블릿에 전달해야한다. 서블릿컨테이너는 먼저 Socket을 통해 사용자에게 요청을 받는다. 이때 받은 요청을 Request 객체로 만든다. 하지만 이 객체를 그대로 서블릿에 전달하면 서블릿이 내부 상태를 건드릴 수 있기 때문에 일관성 or 보안 문제가 생길 수 있다. 그래서 Tomcat은 Request를 감싼 RequestFacade를 만들어서 서블릿에 전달한다. (ReqeustFacade는 HttpServletRequest의 구현체이다.) 서블릿은 HttpServletRequest의 GET/POST 여부에 따라 doGet() 이나 doPost()를 실행하게 되며, 동적 페이지 생성 후, HttpServletResponse 객체에 응답을 보낸다. 

 

서블릿컨테이너는 각 요청마다 쓰레드를 연결한다. 쓰레드 풀이라는 곳에서 쓰레드를 가져와서 사용하고 반납하는데 자세한 이야기는 멀티 쓰레드를 다루는 글에서 이야기 하겠다.

톰캣(서블릿 컨테이너)는 하나의 프로그램이기 때문에 하나의 JVM이 붙는다. 각각의 Servlet 클래스는 JVM의 Class Loader에 의해 로딩된다. Java Servlet container는 서블릿 생성자를 호출하고, 각각의 서블릿 생성자는 어떠한 매개변수도 받지 않는다.

서블릿은 요청이 처음 들어올때 생성되는데 디스패처 서블릿도 첫 요청이 들어오면 생성되는걸까?

아니다. 기본적으로 서블릿은 해당 url의 요청이 들어왔을때 서블릿을 생성하는데 디스패처 서블릿은 서버가 시작될 때 만들어진다.

이게 가능한 이유는 loadOnStartup이라는 설정 때문이다. loadOnStartUp을 1로 설정한다면 서버가 시작될때 서블릿이 생성된다. 기본 설정은 -1이다.

@WebServlet(name = "helloServlet", urlPatterns = "/hello", loadOnStartup = 1)
public class HelloServlet extends HttpServlet {
    // ...
}

만약 내가 만든 서블릿을 서버가 시작될때 생성하고 싶다면 이렇게 설정하면 되고 숫자가 작은 순서대로 초기화 순서가 정해진다.

'Spring' 카테고리의 다른 글

주문상태 동시성 처리  (0) 2025.05.05
스레드풀 전략  (0) 2025.05.02
엔티티 DTO 변환 위치  (1) 2025.01.10
스프링 시큐리티의 구조와 대체 방안  (2) 2025.01.02
스프링 부트에서 예외와 처리 방법  (2) 2024.12.30