2021. 7. 10. 22:39ㆍWeb/Servlet
1. 서블릿
서블릿(Servlet)은 자바 코드를 통하여 동적인 웹 어플리케이션을 개발할 수 있게 해주는 프로그램입니다. 서블릿 이전의 웹 사이트는 정적인 정보만을 제공하는 경우가 대부분이었습니다. 이를 개선하여 사용자에게 인터렉티브한 정보를 제공해주기 위해 서블릿이 개발되었습니다. 서블릿은 자바로 작성되었기 때문에 자바와 같은 특징을 가집니다.
서블릿은 다른 자바 프로그램과 다르게 독자적으로 실행할 수 없습니다. 서블릿을 실행하기 위해서는 컨테이너(Container)를 함께 사용해야 합니다. 여기서 말하는 컨테이너란 서블릿을 구동시키기 위한 프로그램을 말합니다. 대표적으로 Apache 재단에서 만든 오픈소스 프로그램인 톰캣(Tomcat)이 있습니다. 사용자의 요청에 따른 흐름은 다음과 같습니다.
- 사용자가 웹 서버에 html과 같은 정보를 요청합니다.
- 웹 서버는 요청을 받고 실행 중인 웹 어플리케이션 서버(이하 WAS)에 요청을 위임합니다.
- WAS는 연동된 서버 프로그램인 서블릿을 호출합니다.
- 서블릿은 요청에 따라 작업을 수행하고 결과를 반환합니다.
- 이 결과가 다시 WAS, 웹 서버를 거칩니다.
- 최종적으로 결과가 클라이언트에게 응답됩니다.
단순히 정보만을 제공하는 서비스라면 서블릿과 같은 프로그램이 필요 없습니다. 하지만 쇼핑몰, 게시판과 같이 사용자와 빠르게 상호작용해야 하는 서비스라면 어떨까요? 정보가 빠르게 변하고 이를 사용자에게 응답해야 합니다. 이런 기능을 수행하고 응답하는 기능을 서블릿을 통해 효과적으로 처리할 수 있습니다.
그 외에 서블릿은 다음과 같은 특징이 있습니다.
- 서버 측에서 실행되면서 기능을 수행합니다.
- 스레드 방식으로 실행됩니다.
- 자바로 만들어져 객체지향적인 특징을 가집니다.
- 컨테이너에서 실행되며 단독으로 실행할 수 없습니다.
- 컨테이너 종류에 상관없이 실행됩니다.
2. 서블릿 API 계층 구조
서블릿은 자바로 구현되었으므로 클래스들 간의 계층 구조를 가집니다. 다음은 서블릿 관련 클래스들의 계층 구조를 나타냅니다.
서블릿 API는 Servlet과 ServletConfig 인터페이스를 구현해 제공하몀 GenericServlet 추상 클래스가 이 두 인터페이스의 추상 메서드를 구현합니다. 그리고 이 GenericServlet을 다시 HttpServlet이 상속받습니다.
Servlet
- Servlet 관련 추상 메서드를 선언합니다.
- init(), service(), destroy(), getServletInfo(), getServletConfig()를 선언합니다.
ServletConfig
- Servlet 관련 추상 메서드가 선언되어 있습니다.
- getInitParameter(), getInitParameterNames(), getServletContext(), getServletName()이 선언되어 있습니다.
GenericServlet
- 상위 두 인터페이스를 구현하여 일반적인 서블릿 기능을 구현한 클래스입니다.
- 이 서블릿을 상속받아 구현한 사용자 서블릿은 사용되는 프로토콜에 따라 각각 service()를 오버라이딩하여 구현합니다.
HttpServlet
- GenericServlet을 상속받아 HTTP 프로토콜을 사용하는 웹 브라우저에서 서블릿 기능을 수행합니다.
- 웹 브라우저 기반 서비스를 제공하는 서블릿을 만들 때 상속받아 사용합니다.
- 요청 시 service()가 호출되면서 요청 방식에 따라 메서드가 호출됩니다.
3. 서블릿의 생명주기 메서드
서블릿도 자바 클래스이므로 실행 → 초기화 → 인스턴스 생성 → 수행 → 소멸의 과정을 거칩니다. 이런 단계를 거칠 때마다 서블릿 클래스의 메서드가 호출되어 초기화, 기능 수행, 마무리 작업을 수행합니다. 각 단계마다 호출되는 메서드가 서블릿 생명주기 메서드입니다.
초기화 단계 - init()
- 서블릿 요청 시 맨 처음 한 번만 호출됩니다.
- 서블릿 생성 시 초기화 작업을 주로 수행합니다.
작업 수행 - doGet(), doPost 등
- 서블릿 요청 시 매번 호출됩니다.
- 실제 클라이언트의 요청 작업을 수행합니다.
종료 - destroy()
- 서블릿이 기능 수행 후 메모리에서 소멸될 때 호출됩니다.
- 서블릿의 마무리 작업을 주로 수행합니다.
init()이나 destroy()는 별다른 작업이 없을 시 생략 가능하지만 작업을 수행하는 메서드는 핵심 기능을 담당하므로 반드시 구현해야 합니다.
4. 서블릿 매핑
서블릿을 만든 후 실제 클라이언트로부터 요청받기 위해서는 매핑을 해야 합니다. 방법은 web.xml에 등록하는 방법과 어노테이션을 사용하는 두 가지가 있습니다.
1. web.xml
이 파일은 deploy descriptor로서 일종의 환경 설정 파일입니다. 웹 어플리케이션에 대한 여러 가지 설정을 할 때 사용합니다. 여기에서 서블릿을 매핑해보겠습니다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi= ...>
<servlet>
<servlet-name>abc</servlet-name>
<servlet-class>ex1.FirstServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>def</servlet-name>
<servlet-class>ex1.SecondServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>abc</servlet-name>
<url-pattern>/first</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>def</servlet-name>
<url-pattern>/second</url-pattern>
</servlet-mapping>
</web-app>
servlet 태그에는 서블릿을 가리키는 name과 실제 서블릿 경로를 적어줍니다. servlet-mapping 태그에는 servlet 태그와 연동하기 위한 name과 클라이언트로부터 요청받을 경로를 적어줍니다. 즉, /first 라는 요청을 받으면 abc에 해당하는 서블렛을 servlet 태그 내에서 찾고 그 클래스에서 작업을 수행합니다.
2. 어노테이션
어노테이션을 활용한 방법은 간단합니다. 만든 서블렛에 @WebServlet(String url-pattern)을 적어주면 됩니다. 어노테이션을 사용할 땐 반드시 HttpServlet을 상속받아야 합니다.
import ...
@WebServlet("/first");
public FirstServlet extends HttpServlet {
import ...
@WebServlet("/second");
public SecondServlet extends HttpServlet {
5. 서블릿 동작 과정
서블릿의 상세한 동작 과정을 살펴보겠습니다.
서블릿은 최초 요청 시에 메모리에 로드되고 init() 메서드를 호출합니다. 그 이후에는 하나의 서블릿을 모든 스레드(사용자)가 공유하여 사용합니다. 즉, 각 요청마다 서블릿이 생성되는 것이 아니라 하나의 서블릿 인스턴스로 모든 스레드의 요청을 처리하도록 함으로써 훨씬 빠르고 효율적으로 동작합니다.