서블릿(Servlet)의 생명주기

서버란·2024년 11월 3일

웹 어플리케이션

목록 보기
2/4

서블릿(Servlet)의 생명주기는 서블릿이 생성되고, 클라이언트 요청을 처리한 후, 종료될 때까지의 일련의 과정을 말합니다. 서블릿은 Java 기반의 웹 애플리케이션에서 서버 측에서 실행되며, 클라이언트의 요청에 대해 동적인 응답을 생성하는 역할을 합니다. 서블릿의 생명주기는 Servlet 인터페이스에 정의된 메서드들에 의해 관리되며, 크게 4단계로 나눌 수 있습니다:

1. 서블릿 초기화 (init)

서블릿 컨테이너가 서블릿을 처음 생성할 때 호출됩니다. 서블릿 객체는 처음에 메모리에 적재되고, 필요한 초기 설정을 수행하는 단계입니다. 이 단계에서는 서블릿이 초기화 파라미터를 받아서 초기화를 하거나, 데이터베이스 연결을 설정하는 등의 작업을 할 수 있습니다.

init() 메서드가 사용되며, 서블릿이 생성될 때 한 번만 호출됩니다.
예시:

public void init(ServletConfig config) throws ServletException {
    // 초기화 작업
    System.out.println("Servlet 초기화");
}

2. 요청 처리 (service)

클라이언트가 HTTP 요청을 할 때마다 호출되는 메서드입니다. 서블릿 컨테이너는 클라이언트의 요청을 받아서 적절한 서블릿의 service() 메서드를 호출합니다. 이 메서드는 HttpServletRequest와 HttpServletResponse 객체를 인자로 받아, 요청을 처리하고 응답을 반환하는 역할을 합니다.
service() 메서드는 요청 메서드(예: GET, POST)에 따라 적절한 메서드를 호출하도록 되어 있습니다.
예시:

public void service(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
    String method = request.getMethod();
    if (method.equals("GET")) {
        doGet(request, response);
    } else if (method.equals("POST")) {
        doPost(request, response);
    }
}

3. 서블릿 종료 (destroy)

서블릿 컨테이너가 서블릿을 메모리에서 제거하기 전에 호출됩니다. 이는 서버가 종료되거나, 서블릿이 더 이상 필요하지 않을 때 실행됩니다. 이 단계에서 서블릿은 메모리 해제, 리소스 정리(예: 데이터베이스 연결 해제)를 수행합니다.
destroy() 메서드는 서블릿이 종료될 때 한 번 호출됩니다.
예시:

public void destroy() {
    // 리소스 정리 작업
    System.out.println("Servlet 종료");
}

4. 서블릿 객체의 쓰레딩 및 재사용

서블릿은 일반적으로 싱글톤으로 생성되며, 여러 클라이언트 요청을 동시에 처리하기 위해 여러 쓰레드가 생성되어 동작합니다. 한 번 로드된 서블릿 객체는 컨테이너가 제거할 때까지 메모리에서 유지되며, 매 요청마다 init()을 반복하지 않고 service() 메서드만 호출됩니다. 이 때문에 서블릿은 상태를 저장하는 멤버 변수를 신중하게 사용해야 합니다.

서블릿 생명주기 정리

  • 서블릿 객체가 생성되고 init() 메서드를 호출하여 초기화.
  • 클라이언트 요청이 있을 때마다 service() 메서드가 호출되어 요청 처리.
  • 서버 종료 또는 서블릿 제거 시 destroy() 메서드가 호출되어 리소스 정리.
  • 서블릿의 생명주기는 주로 웹 서버나 애플리케이션 서버(예: Tomcat)에 의해 관리됩니다.

Q1: 서블릿의 멀티스레딩 문제를 해결하기 위해 어떤 방법을 사용할 수 있을까요?

서블릿은 멀티스레드 환경에서 실행되기 때문에 멤버 변수의 상태 관리에 주의해야 합니다. 멀티스레딩 문제를 해결하는 몇 가지 방법을 소개합니다:

  • 지역 변수(Local Variables) 사용
    멤버 변수 대신 지역 변수를 사용하면 각 요청에 대해 독립적으로 관리되므로 스레드 간의 충돌이 발생하지 않습니다.

  • 동기화(Synchronization)
    멤버 변수를 반드시 사용해야 하는 경우, synchronized 키워드를 사용하여 특정 메서드나 블록을 동기화할 수 있습니다. 하지만, 동기화는 성능 저하를 일으킬 수 있기 때문에 최소화하는 것이 좋습니다.

public synchronized void someMethod() {
    // 동기화 처리
}
  • SingleThreadModel 인터페이스 (Deprecated)
    예전에는 SingleThreadModel 인터페이스를 구현하면 각 요청에 대해 새로운 서블릿 인스턴스를 생성했지만, 성능 문제로 현재는 더 이상 권장되지 않으며, 최신 서블릿 API에서는 제거되었습니다.

  • ThreadLocal 사용
    각 스레드가 고유하게 사용할 수 있는 변수를 ThreadLocal을 통해 설정할 수 있습니다. 이를 통해 멀티스레드 환경에서도 안전하게 데이터를 관리할 수 있습니다.

private static ThreadLocal<String> threadLocalVar = new ThreadLocal<>();

Q2: 서블릿의 init() 메서드에서 초기화할 수 있는 자원은 어떤 것들이 있을까요?

init() 메서드는 서블릿이 처음 생성될 때 호출되므로, 서블릿이 실행되는 동안 사용될 자원들을 초기화하는 데 사용됩니다. 여기서 초기화할 수 있는 주요 자원은 다음과 같습니다:

  • 데이터베이스 연결 설정 (Connection Pool)
    데이터베이스와의 연결을 미리 설정하여 이후 요청에서 효율적으로 데이터베이스에 접근할 수 있도록 할 수 있습니다.

  • 외부 파일 로드 (Configuration Files)
    서블릿이 동작하는 데 필요한 설정 파일(예: web.xml, properties 파일 등)을 로드하여 내부적으로 사용할 준비를 합니다.

  • 서블릿의 초기화 매개변수 (Init Parameters)
    web.xml 파일에서 정의된 서블릿 초기화 매개변수들을 읽어들여, 서블릿의 설정 값으로 사용합니다. 예를 들어, 특정 API 키나 기본 URL 등을 설정할 수 있습니다.

  • 서버 자원 접근 설정
    파일 시스템 경로나 다른 서블릿 혹은 필터에 접근할 때 필요한 경로나 리소스를 미리 설정할 수 있습니다.

Q3: service() 메서드 내부에서 GET 요청과 POST 요청을 구분하는 다른 방법은 무엇이 있을까요?

일반적으로 service() 메서드에서 request.getMethod()를 호출하여 GET과 POST 요청을 구분하는데, 아래와 같은 다른 방법도 사용할 수 있습니다:

  • 별도 메서드를 직접 호출
    서블릿을 작성할 때 doGet()과 doPost()를 직접 구현하면, service() 메서드를 명시적으로 오버라이드할 필요 없이 자동으로 GET과 POST 요청을 구분할 수 있습니다. 서블릿 컨테이너가 자동으로 GET 요청이면 doGet()을, POST 요청이면 doPost()를 호출해줍니다.
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
    // GET 요청 처리
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // POST 요청 처리
}
  • 서블릿 필터를 사용한 구분
    서블릿 필터를 사용하여 요청이 들어오기 전에 HTTP 메서드를 확인한 후, 그에 따 라 적절한 서블릿 으로 요청을 전달하거나 처리할 수 있습니다. 필터는 요청 전처리를 담당하기 때문에 유용하게 사용될 수 있습니다.

= 요청 URL 패턴에 따른 구분
때로는 HTTP 메서드가 아니라 요청 URL 패턴에 따라 처리를 구분할 수 있습니다. 예를 들어, /api/data로 들어오는 요청은 POST로만 처리하고, /api/view는 GET으로만 처리하는 방식입니다.

profile
백엔드에서 서버엔지니어가 된 사람

0개의 댓글