서블릿(Servlet)의 생명주기는 서블릿이 생성되고, 클라이언트 요청을 처리한 후, 종료될 때까지의 일련의 과정을 말합니다. 서블릿은 Java 기반의 웹 애플리케이션에서 서버 측에서 실행되며, 클라이언트의 요청에 대해 동적인 응답을 생성하는 역할을 합니다. 서블릿의 생명주기는 Servlet 인터페이스에 정의된 메서드들에 의해 관리되며, 크게 4단계로 나눌 수 있습니다:
서블릿 컨테이너가 서블릿을 처음 생성할 때 호출됩니다. 서블릿 객체는 처음에 메모리에 적재되고, 필요한 초기 설정을 수행하는 단계입니다. 이 단계에서는 서블릿이 초기화 파라미터를 받아서 초기화를 하거나, 데이터베이스 연결을 설정하는 등의 작업을 할 수 있습니다.
init() 메서드가 사용되며, 서블릿이 생성될 때 한 번만 호출됩니다.
예시:
public void init(ServletConfig config) throws ServletException {
// 초기화 작업
System.out.println("Servlet 초기화");
}
클라이언트가 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);
}
}
서블릿 컨테이너가 서블릿을 메모리에서 제거하기 전에 호출됩니다. 이는 서버가 종료되거나, 서블릿이 더 이상 필요하지 않을 때 실행됩니다. 이 단계에서 서블릿은 메모리 해제, 리소스 정리(예: 데이터베이스 연결 해제)를 수행합니다.
destroy() 메서드는 서블릿이 종료될 때 한 번 호출됩니다.
예시:
public void destroy() {
// 리소스 정리 작업
System.out.println("Servlet 종료");
}
서블릿은 일반적으로 싱글톤으로 생성되며, 여러 클라이언트 요청을 동시에 처리하기 위해 여러 쓰레드가 생성되어 동작합니다. 한 번 로드된 서블릿 객체는 컨테이너가 제거할 때까지 메모리에서 유지되며, 매 요청마다 init()을 반복하지 않고 service() 메서드만 호출됩니다. 이 때문에 서블릿은 상태를 저장하는 멤버 변수를 신중하게 사용해야 합니다.
서블릿은 멀티스레드 환경에서 실행되기 때문에 멤버 변수의 상태 관리에 주의해야 합니다. 멀티스레딩 문제를 해결하는 몇 가지 방법을 소개합니다:
지역 변수(Local Variables) 사용
멤버 변수 대신 지역 변수를 사용하면 각 요청에 대해 독립적으로 관리되므로 스레드 간의 충돌이 발생하지 않습니다.
동기화(Synchronization)
멤버 변수를 반드시 사용해야 하는 경우, synchronized 키워드를 사용하여 특정 메서드나 블록을 동기화할 수 있습니다. 하지만, 동기화는 성능 저하를 일으킬 수 있기 때문에 최소화하는 것이 좋습니다.
public synchronized void someMethod() {
// 동기화 처리
}
SingleThreadModel 인터페이스 (Deprecated)
예전에는 SingleThreadModel 인터페이스를 구현하면 각 요청에 대해 새로운 서블릿 인스턴스를 생성했지만, 성능 문제로 현재는 더 이상 권장되지 않으며, 최신 서블릿 API에서는 제거되었습니다.
ThreadLocal 사용
각 스레드가 고유하게 사용할 수 있는 변수를 ThreadLocal을 통해 설정할 수 있습니다. 이를 통해 멀티스레드 환경에서도 안전하게 데이터를 관리할 수 있습니다.
private static ThreadLocal<String> threadLocalVar = new ThreadLocal<>();
init() 메서드는 서블릿이 처음 생성될 때 호출되므로, 서블릿이 실행되는 동안 사용될 자원들을 초기화하는 데 사용됩니다. 여기서 초기화할 수 있는 주요 자원은 다음과 같습니다:
데이터베이스 연결 설정 (Connection Pool)
데이터베이스와의 연결을 미리 설정하여 이후 요청에서 효율적으로 데이터베이스에 접근할 수 있도록 할 수 있습니다.
외부 파일 로드 (Configuration Files)
서블릿이 동작하는 데 필요한 설정 파일(예: web.xml, properties 파일 등)을 로드하여 내부적으로 사용할 준비를 합니다.
서블릿의 초기화 매개변수 (Init Parameters)
web.xml 파일에서 정의된 서블릿 초기화 매개변수들을 읽어들여, 서블릿의 설정 값으로 사용합니다. 예를 들어, 특정 API 키나 기본 URL 등을 설정할 수 있습니다.
서버 자원 접근 설정
파일 시스템 경로나 다른 서블릿 혹은 필터에 접근할 때 필요한 경로나 리소스를 미리 설정할 수 있습니다.
일반적으로 service() 메서드에서 request.getMethod()를 호출하여 GET과 POST 요청을 구분하는데, 아래와 같은 다른 방법도 사용할 수 있습니다:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// GET 요청 처리
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// POST 요청 처리
}
= 요청 URL 패턴에 따른 구분
때로는 HTTP 메서드가 아니라 요청 URL 패턴에 따라 처리를 구분할 수 있습니다. 예를 들어, /api/data로 들어오는 요청은 POST로만 처리하고, /api/view는 GET으로만 처리하는 방식입니다.