지난 글에서 서블릿과 서블릿 컨테이너 각각의 역할과 두 관계에 대해서 설명했다. 이번 글에서는 보다 구체적으로, HTTP 요청이 처리되는 과정을 볼 것이다.
[ 그림 1 ] 에서 볼 수 있듯이 서블릿 컨테이너에는 여러 개의 서블릿이 존재할 수 있는데, 이 때 각각의 서블릿은 package javax.servlet
에 정의된 인터페이스 구현체다.
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public void destroy();
}
서블릿 인터페이스에 정의된 메소드를 통해서 서블릿의 생명주기를 관리한다. (초기화 ~ 소멸) 여기서 서블릿의 생명주기를 관리하는 주체는 누굴까? (모두 알겠지만) 서블릿 컨테이너다. 지난 글에서 예로 들었던, 좋아요 누른 포스트 리스트 조회 요청 처리 과정을 보면서 서블릿 컨테이너와 서블릿의 관계를 상기해보자.
- 클라이언트가 (로그인 유저) 좋아요 누른 포스트 리스트를 조회 요청한다.
- 웹 서버는 이 요청을 서블릿 컨테이너 에 위임한다.
- 서블릿 컨테이너는 해당 요청을 처리할 수 있는 서블릿을 찾고, 위임한다.
- 서블릿은 해당 요청을 처리하고 동적 웹 페이지를 생성한다.
- 웹 서버는 서블릿을 통해 생성된 웹 페이지를 클라이언트에 반환한다.
서블릿 컨테이너는 웹 서버로부터 클라이언트 요청을 받아서 실제로 처리를 담당할 서블릿을 찾고 해당 요청을 위임한다. 우선 서블릿에 요청을 위임하려면 당연히 서블릿 컨테이너 내 서블릿이 init()
를 통해 준비가 돼야 한다. 이를 위해서 기본적으로 서블릿이 클라이언트 요청에 의해 호출되는 시점에 초기화가 되고, 필요한 경우 초기화 시점을 조정할 수 있다. (이 내용이 궁금한 분은 load-on-startup
을 찾아보자)
초기화가 완료돼서 서블릿으로서 역할을 할 준비가 되면, 서블릿 컨테이너는 해당 서블릿의 service()
메소드를 호출해서 클라이언트 요청을 처리 한 뒤 동적 웹 페이지를 생성하도록 한다. 이후 웹 서버를 통해 클라이언트에 응답하는 구조이다. 이 때 한 번 초기화된 서블릿은 바로 사라지는 것이 아니라 서블릿 컨테이너 주소에 남아 있으면서 요청을 처리할 수 있다. (초기화는 한 번만) 이후 destroy()
를 통해 소멸된 서블릿에 대해서는 더 이상 요청을 위임할 수 없게 되고 JVM 가비지 컬렉터에 의해서 처리된다.
이번 글에서는 하나의 요청을 처리하는 과정에서 서블릿 컨테이너가 서블릿을 어떻게 관리하는 지(서블릿 생명주기) 에 대해서 알아봤다. 다음 글에서는 동시에 여러 요청을 처리하는 상황을 가정하고, 서블릿 컨테이너가 이를 어떻게 대처하는 지에 대해서 다룰 예정이다.