서블릿(Servlet)이란?
정적 웹 페이지의 문제점을 보완하기 위해 나온 것이 동적 웹 페이지를 구현하는 JSP이다.
그러나 사실 동적 웹 페이지를 처음으로 구현한 방법은 JSP 말고 서블릿(Servlet, 자바로 만든 CGI 프로그램) 을 이용했다.
그리고 이 서블릿의 문제점을 보완하여 나온 것이 JSP이다. JSP의 많은 기능은 서블릿의 기능을 따르므로 서블릿을 먼저 이해하고 나면 JSP를 좀 더 수월하게 이해할 수 있을 것 같다.
Servlet
서블릿은 서버 쪽에서 실행되면서 클라이언트의 요청에 따라 동적으로 서비스를 제공하는 자바 클래스이다.
서블릿은 자바로 작성되어 있으므로 자바의 일반적인 특징을 모두 가진다.
하지만 서블릿은 일반 자바 프로그램과 다르게 독자적으로 실행되지 못하고 톰캣(Tomcat)과 같은 JSP/Servlet 컨테이너에서 실행된다는 점에서 차이가 있다.
- 서블릿의 특징
- 서버 쪽에서 실행되면서 기능을 수행
- 기존의 정적인 웹 프로그램의 문제점을 보완하여 동적인 여러 가지 기능을 제공
- 스레드 방식으로 실행
- 자바로 만들어져 자바의 특징(객체 지향)을 가짐
- 컨테이너에서 실행됨
- 컨테이너 종류에 상관없이 실행됨(플랫폼 독립적)
- 보안 기능을 적용하기 쉬움
- 웹 브라우저에서 요청 시 기능을 수행함
서블릿 API 계층 구조와 기능
서블릿은 자바로 만들어졌으므로 당연히 클래스들 간의 계층 구조를 가진다.
서블릿 API는 Servlet과 ServletConfig 인터페이스를 구현해 제공하며 GenericServlet 추상 클래스가 이 두 인터페이스의 추상 메서드를 구현한다.
그리고 이 GenericServlet을 다시 HttpServlet이 상속받는다.
- 서블릿 API 구성 요소 특징
- Servlet 인터페이스
- javax.servlet 패키지에 선언되어 있다.
- Servlet 관련 추상 메서드를 선언한다.
- init(), service(), destroy(), getServletInfo(), getServletConfig()를 선언한다.
- ServletConfig 인터페이스
- javax.servlet 패키지에 선언되어 있다.
- Servlet 기능 관련 추상 메서드가 선언되어 있다.
- getInitParameter(), getInitParameterNames(), getServletContext(), getServletName()이 선언되어 있다.
- GenericServlet 클래스
- javax.servlet 패키지에 선언되어 있다.
- 상위 두 인터페이스를 구현하여 일반적인 서블릿 기능을 구현한 클래스이다.
- GenericServlet을 상속받아 구현한 사용자 서블릿은 사용되는 프로토콜에 따라 각각 service()를 오버라이딩해서 구현한다.
- HttpServlet 클래스
- javax.servlet.http 패키지에 선언되어 있다.
- GenericServlet을 상속받아 HTTP 프로토콜을 사용하는 웹 브라우저에서 서블릿 기능을 수행한다.
- 웹 브라우저 기반 서비스를 제공하는 서블릿을 만들 때 상속받아 사용한다.
- 요청 시 service()가 호출되면서 요청 방식에 따라 doGet()이나 doPost()가 차례대로 호출된다.
- Servlet 인터페이스
- HttpServlet의 여러 가지 메서드
- protected doDelete(HttpServletRequest req, HttpServletResponse resp)
- 서블릿이 DELETE request를 수행하기 위해 service()를 통해서 호출된다.
- protected doGet(HttpServletRequest req, HttpServletResponse resp)
- 서블릿이 GET request를 수행하기 위해 service()를 통해서 호출된다.
- protected doHead(HttpServletRequest req, HttpServletResponse resp)
- 서블릿이 HEAD request를 수행하기 위해 service()를 통해서 호출된다.
- protected doPost(HttpServletRequest req, HttpServletResponse resp)
- 서블릿이 POST request를 수행하기 위해 service()를 통해서 호출된다.
- protected service(HttpServletRequest req, HttpServletResponse resp)
- 표준 HTTP request를 public service()에서 전달받아 doXXX() 메서드를 호출한다.
- public service(HttpServletRequest req, HttpServletResponse resp)
- 클라이언트의 request를 protected service()에게 전달한다.
- protected doDelete(HttpServletRequest req, HttpServletResponse resp)
- 서블릿의 생명주기
서블릿은 자바 클래스이므로 실행하면 초기화 과정, 메모리에 인스턴스를 생성하여 서비스를 수행하는 과정, 소멸하는 과정을 거친다.
이때, 서블릿 클래스의 메서드가 호출되어 초기화, 데이터베이스 연동, 마무리 작업을 수행한다. 각 과정에서 호출되어 기능을 수행하는 메서드들이 서블릿 생명주기 메서드이다.
즉, 서블릿 생명주기(Life Cycle) 메서드 란 서블릿 실행단계마다 호출되어 기능을 수행하는 콜백 메서드를 의미한다.- 초기화: init()
- 서블릿 요청시 제일 처음 한 번만 호출된다.
- 서블릿 생성 시 초기화 작업을 주로 수행한다.
- 작업 수행: doGet(), doPost() 등
- 서블릿 요청 시 매번 호출된다.
- 실제로 클라이언트가 요청하는 작업을 수행한다.
- 종료: destroy()
- 서블릿이 기능을 수행하고 메모리에서 소멸될 때 호출된다.
- 서블릿의 마무리 작업을 주로 수행한다.
- 초기화: init()
init() 메서드는 실행 초기에 서블릿 기능 수행과 관련된 기능을 설정하는 용도로 많이 사용된다. 그리고 destroy()는 서블릿이 메모리에서 소멸될 때 여러 가지 종료 작업을 수행한다.
따라서 만약 이런 기능이 필요 없으면 생략해도 된다. 반면에 doGet() 이나 doPost() 와 같이 do로 시작하는 메서드는 서블릿의 핵심 기능을 처리하므로 반드시 구현 해야 한다.
Servlet 예제
서블릿을 구현하고 실행하는 과정
- 사용자 정의 서블릿 클래스 만들기
- 서블릿 생명주기 메서드 구현
- 서블릿 매핑 작업
- 웹 브라우저에서 서블릿 매핑 이름으로 요청하기
ExampleServlet.java
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ExampleServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("init 메서드 호출");
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doGet 메서드 호출");
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("doPost 메서드 호출");
}
@Override
public void destroy(){
System.out.println("destroy 메서드 호출");
}
}
이 때, 서블릿 클래스 이름에 대응되는 서블릿 매핑 이름으로 실제 서블릿을 요청한다. 이 방법은 web.xml 설정을 이용하는 방법이다.
- web.xml에서 설정한다.
- <servlet> 태그와 <servlet-mapping> 태그를 이용한다.
- 여러 개의 서블릿 매핑 시에는 <servlet> 태그를 먼저 정의하고 <servlet-mapping> 태그를 정의한다.
web.xml
<servlet>
<servlet-name>servletFirst</servlet-name>
<servlet-class>projectName.packageName.ExampleServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>servletSecond</servlet-name>
<servlet-class>projectName.packageName.ExampleServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletFirst</servlet-name>
<url-pattern>/example</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servletSecond</servlet-name>
<url-pattern>/example2</url-pattern>
</servlet-mapping>
위의 코드를 보면, servlet-name의 값으로 <sevlet> 태그와 <servlet-mapping> 태그를 연결시켜 준다.
<url-pattern>은 웹 브라우저에서 요청하는 매핑이름으로, IP:PORT/example로 요청을 하면 <servlet-class>에 있는 서블릿인 ExampleServlet으로 매핑된다.
그리고 다수의 Servlet을 매핑하고 싶다면, 위의 web.xml처럼 작성하면 된다.
그러나 위와 같이 web.xml에 설정할 경우 상당히 복잡해질 수 있다는 단점이 있다. 따라서 각 서블릿 클래스에 어노테이션(@) 을 이용하여 서블릿 표시를 해주면 훨씬 가독성이 좋아진다.
어노테이션을 이용해 서블릿 매핑을 하려면 @WebServlet 을 이용하면 된다. 그리고 어노테이션이 적용되는 클래스는 반드시 HttpServlet 클래스를 상속받아야 한다.
AnnotationServlet.java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/annotation")
public class AnnotationServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("init 메서드 호출");
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("doGet 메서드 호출");
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
System.out.println("doPost 메서드 호출");
}
@Override
public void destroy(){
System.out.println("destroy 메서드 호출");
}
}
위와 같이 어노테이션을 이용하여 작성하면, web.xml로 매핑 설정한 것과 똑같이 동작한다.
주의할 점은 어떤 매핑 이름이 이미 사용중이라면 해당하는 매핑 이름은 사용 못한다.
참고: 자바 웹을 다루는 기술