본문 바로가기
교재 실습/자바 웹 프로그래밍 Next Step

5.3.2 서블릿 컨테이너, 서블릿

by Jint 2025. 4. 6.

앞의 HTTP 웹 서버 리팩토링 과정을 이해했다면 이미 서블릿 컨테이너와 서블릿이 어떻게 동작하는지 이해한 것이나 다를 바 없다.

앞의 동영상에서 톰캣 서버를 시작하는 WebServerLauncher 클래스를 살펴보면 다음과 같다.

 

- WebServerLauncher.java

public class WebServerLauncher {

    private static final Logger logger = LoggerFactory.getLogger(WebServerLauncher.class);

    public static void main(String[] args) throws Exception {
        String webappDirLocation = "webapp/";
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
        logger.info("configuring app with basedir : {}", new File("./" + webappDirLocation).getAbsolutePath());

        tomcat.start();
        tomcat.getServer().await();
    }

}

 

위 소스는 앞에서 구현한 HTTP 웹 서버의 WebServer 와 같은 역할로 톰캣 서버를 시작한다. 시작할 때 웹 자원(HTML, CSS, 자바스크립트)이 위치하는 디렉토리와 이 디렉토리 자원을 접근할 때의 경로(위 코드는 /로 설정하고 있음)를 설정하고 있다.

브라우저에 "Hello World!"를 출력하기 위해 구현한 서블릿 코드는 다음과 같다.

 

- HelloWorldServlet.java

import java.io.IOException;
import java.io.PrintWriter;

import java.servlet.ServletException;
import java.servlet.annotation.WebServlet;
import java.servlet.HttpServlet;
import java.servlet.HttpServletRequest;
import java.servlet.HttpServletResponse;

@WebServlet("/hello")
public class HelloWorldServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.print("Hello World!");
    }

}

 

서블릿 코드를 보는 순간 앞에서 본 Controller 인터페이스 구현체와 너무 비슷하다는 것을 알 수 있다. 서블릿은 앞에서 구현했던 Controller 와 정확히 같은 역할을 하며, 똑같은 방식으로 동작한다. doGet() 메서드의 인자로 전달하는 HttpServletRequest, HttpServletResponse 는 앞에서 구현한 HttpRequest, HttpResponse 와 같다. 더 정확히 연결하면  Controller 인터페이스는 서블릿의 Servlet 인터페이스, AbstractController 는 HttpServlet 과 같다.

서블릿 컨테이너는 서버를 시작할 때 클래스패스에 있는 클래스 중 HttpServlet 을 상속하는 클래스를 찾은 후 @WebServlet 어노테이션의 값을 읽어 요청 URL과 서블릿을 연결하는 Map을 생성한다. 즉, 앞에서 구현한 RequestMapping 의 Map 에 서블릿을 추가하고, 요청 URL에 대한 서블릿을 찾아 서비스하는 역할을 서블릿 컨테이너가 담당한다.

서블릿 컨테이너의 중요한 역할 중의 하나는 서블릿 클래스의 인스턴스 생성, 요청 URL과 서블릿 인스턴스 매핑, 클라이언트 요청에 해당하는 서블릿을 찾은 후 서블릿에 작업을 위임하는 역할을 한다. 이외에도 서블릿 컨테이너는 서블릿과 관련한 초기화(init)와 소멸(destroy) 작업도 담당한다. Servlet 인터페이스 소스코드를 보면 좀 더 명확히 이해할 수 있을 것이다.

 

- Servlet.java

package javax.servlet;

import java.io.IOException;

/**
 * Defines methods that all servlets must implement.
 */
public interface Servlet {
    
    /**
     * Called by the servlet container to indicate to a servlet that the servlet is being placed into service.
     * @param config a ServletConfig object containing the servlet's configuration and initialization parameters
     * @throws ServletException if an exception occurs that interrupts the servlet's normal operation
     */
    void init(ServletConfig config) throws ServletException;

    /**
     * Returns a ServletConfig object, which contains initialization and startup parameters for this servlet.
     * @return the ServletConfig object that initializes this servlet
     */
    ServletConfig getServletConfig();

    /**
     * Called by the servlet container to allow the servlet to respond to a request.
     * @param req the ServletRequest object that contains the client's request
     * @param res the ServletResponse object that contains the servlet's response
     * @throws ServletException if the request could not be handled
     * @throws IOException if an input or output error occurs while the servlet is handling the request
     */
    void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    /**
     * Returns a String containing information about the servlet, such as author, version, and copyright.
     * @return a String containing servlet information
     */
    String getServletInfo();

    /**
     * Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.
     */
    void destroy();

}

 

서블릿 컨테이너가 시작하고 종료할 때의 과정을 단계적으로 살펴본다.

 

· 서블릿 컨테이너 시작(@WebServlet 어노테이션 설정은 서블릿 3.0 버전에서 추가되었다. 이전 버전은 web.xml 파일 설정을 참고해 서블릿 클래스를 찾고, 요청 URL 매핑을 했다. 이 책은 web.xml에 대한 설정은 다루지 않으니 web.xml 설정을 알고 싶다면 다른 책을 참고)

· 클래스패스에 있는 Servlet 인터페이스를 구현하는 서블릿 클래스를 찾음

· @WebServlet 설정을 통해 요청 URL과 서블릿 매핑

· 서블릿 인스턴스 생성

· init() 메서드를 호출해 초기화

 

서블릿 컨테이너는 위 과정으로 서블릿 초기화를 완료한 후 클라이언트 요청이 있을 때까지 대기상태로 있다가 클라이언트 요청이 있을 경우 요청 URL에 해당하는 서블릿을 찾아 service() 메서드를 호출한다.

서비스를 하다 서블릿 컨테이너를 종료하면 서블릿 컨테이너가 관리하고 있는 모든 서블릿의 destroy() 메서드를 호출해 소멸 작업을 진행한다.

이와 같이 서블릿 생성, 초기화, 서비스, 소멸 과정을 거치는 전체 과정을 서블릿의 생명주기(라이프사이클, life cycle)라 한다. 따라서 서블릿 컨테이너는 서블릿의 생명주기를 관리한다고 이야기한다. 물론 이외에도 멀티쓰레딩 지원, 설정 파일을 활용한 보안관리, JSP 지원 등의 작업을 지원함으로써 개발자가 중요한 비즈니스 로직 구현에 집중할 수 있도록 한다.

앞으로 자바 웹 애플리케이션을 개발하면서 컨테이너라는 용어를 접할 기회가 있다. 각 컨테이너마다 다른 기능을 지원할 수 있지만 기본적으로 생명주기를 관리하는 기능을 제공한다. 예를 들어 지금은 거의 사용되지 않는 EJB(Enterprise Java Bean) 컨테이너는 EJB에 대한 생명주기 관리, 스프링 프레임워크에 포함되어 있는 빈(Bean) 컨테이너는 빈에 대한 생명주기를 관리하는 기능을 제공한다고 생각할 수 있다.

컨테이너가 관리하는 객체의 인스턴스는 개발자가 직접 생성하는 인스턴스가 아니다. 개발자가 직접 인스턴스를 생성한다면 개발자가 원하는 메서드를 호출해 초기화나 소멸과 같은 작업을 진행하면 된다. 하지만 컨테이너에 의해 인스턴스가 관리되기 때문에 초기화, 소멸과 같은 작업을 위한 메서드를 인터페이스 규약으로 만들어 놓고 확장할 수 있도록 지원하는 것이다. 2장의 JUnit도 @Before, @Test, @After와 같이 초기화, 테스트, 소멸 작업을 위해 확장할 부분을 제공했듯이 컨테이너 또한 같은 방식의 생명주기를 지원한다. 만약 새로운 컨테이너를 학습할 기회가 있다면 서블릿 생명주기와 같은 방식으로 구현되어 있는지 확인해본다. 대부분 같은 방식으로 동작하기 때문에 새로운 컨테이너라도 좀 더 빠르게 학습할 수 있다. 이것이 양파 껍질의 하나를 더 벗기면서 더 깊이 있는 학습을 하는 이유다.

서블릿에서 알아야 할 중요한 부분 중의 하나는 서블릿 컨테이너가 생성하는 서블릿 인스턴스의 개수다. 서블릿 컨테이너는 멀티스레드로 동작한다. 즉, 동시에 여러 명의 클라이언트가 접속할 수 있도록 지원한다. 그렇다면 서블릿 인스턴스는 몇 개나 생성될까? 새로운 스레드가 생성될 때마다 새로운 서블릿 인스턴스를 생성할까? 이에 대한 해답은 HTTP 웹 서버 실습에서 구현한 RequestMapping 의 Map을 보면 된다. RequestMapping 의 Map은 static 키워드로 구현되어 있어 서버가 시작할 때 한 번 초기화되면 더 이상 초기화하지 않고 계속해서 재사용한다(자바 관련 온라인 문서나 책을 참고해 static 키워드가 어떤 역할을 하는지 학습할 것을 추천). 서블릿도 같다. 서블릿도 서블릿 컨테이너가 시작할 때 한 번 생성되면 모든 스레드가 같은 인스턴스를 재사용한다. 멀티스레드가 인스턴스 하나를 공유하면서 발생하는 문제와 이에 대한 해결 방법은 이후에 다시 살펴본다.



참고도서 : https://roadbook.co.kr/169

 

[신간안내] 자바 웹 프로그래밍 Next Step

● 저자: 박재성 ● 페이지: 480 ● 판형: 사륙배변형(172*225) ● 도수: 1도 ● 정가: 30,000원 ● 발행일: 2016년 9월 19일 ● ISBN: 978-89-97924-24-0 93000 [강컴] [교보] [반디] [알라딘] [예스24] [인터파크] [샘

roadbook.co.kr

댓글