서블릿

  • 자바로 웹 애플리케이션을 개발 할 수 있는 스팩과 API를 제공
  • 요청 당 쓰레드(를 만들거나, 풀을 통해)사용
  • 그 중에서 가장 중요한 클래스중 하나가 HttpServlet

서블릿 등장 이전에 사용하던 CGI는 요청 당 프로세스를 만들어 사용한다.

서블릿의 장점

  • 빠르다
  • 플랫폼 독립적이다(자바 기반이기 때문에)
  • 보안
  • 이식성

서블릿 엔진 또는 서블릿 컨테이너(톰캣, 제티, 언더토 등등)

  • 세션 관리
  • 네트워크 서비스
  • MIME 기반 메시지 인코딩 디코딩
  • 서블릿 생명주기 관리

서블릿 생명주기

  • 서블릿 컨테이너가 서블릿 인스턴스의 init()메소드를 호출하여 초기화 한다.
    • 최초 요청을 받았을 때 한 번 초기화하고 나면 그 다음 요청부턴느 해당 과정을 생략한다.
  • 서블릿이 초기화 된 다음부터 클라이언트의 요청을 처리한다. 각 요청은 별도의 쓰레드로 처리하고 이 때 서블릿 인스턴스의 service()메소드를 호출한다.
    • 이 안에서 HTTP 요청을 받고 클라이언트로 보낼 HTTP응답을 만든다.
    • service()는 보통 HTTP Method에 따라 doGet(), doPost()등으로 처리를 위임한다.
    • 따라서 보통 doGet(), doPost()등을 구현한다.ㅌ
  • 서블릿 컨테이너 판단에 따라 해당 서블릿은 메모리에서 내려야 할 시점에 destroy()메소드를 호출한다.
package me.cjk;

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 HelloServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("init");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
        resp.getWriter().println("<html>");
        resp.getWriter().println("<head>");
        resp.getWriter().println("<body>");
        resp.getWriter().println("<h1>Hello Servlet</h1>");
        resp.getWriter().println("</body>");
        resp.getWriter().println("</head>");
        resp.getWriter().println("</html>");
    }

    @Override
    public void destroy() {
        System.out.println("destroy...");
    }
}

web.xml 파일 :

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>me.cjk.HelloServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

위와 같이 작성한 후, 톰캣서버를 실행하면 먼저 init이라고 출력이 된다. 그 후, /hello url에 접근을 할 때마다 doGet이 출력되는 것을 볼 수 있다.
위 과정을 통해 가장 원시적인 Servlet 구동 과정을 살펴보았다. 우리가 앞으로 배우는 스프링 MVC는 HttpServlet 기반으로 만들어져 있다고 하였다. 어떻게 하면 애노테이션으로 컨트롤러, 또는 리퀘스트 매핑 요청을 처리하였을까? 또, view를 직접적으로 작성하는 것이 아니라 어떻게 타임리프로 구현하였을까? 마지막으로, web.xml을 만들지 않고 우리는 웹 애플리케이션을 잘 띄울 수 있었을까에 대해 앞으로 살펴보기로 하자!


서블릿 리스너

웹 애플리케이션에서 발생하는 주요 이벤트를 감지하고 각 이벤트에 특별한 작업이 필요한 경우에 사용할 수 있다. 예를 들어, 애플리케이션 구동 시점에 DB 커넥션을 맺고 DB 커넥션을 여러 서블릿에게 제공할 수 있다. 서블릿 컨테이너가 종료될 때는, 초반에 만들어놨던 DB 커넥션을 정리하는 일도 할 수 있다.

  • 서블릿 컨텍스트 수준의 이벤트
    • 컨텍스트 라이프사이클 이벤트
    • 컨텍스트 애트리뷰트 변경 이벤트
  • 세션 수준의 이벤트
    • 세션 라이프사이클 이벤트
    • 세션 애트리뷰트 변경 이벤트

예제 파일 :

package me.cjk;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/* 서블릿 컨텍스트의 라이프사이클을 감지하는 이벤트 */
public class MyListener implements ServletContextListener {


    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ContextInitialized");
        sce.getServletContext().setAttribute("name","cjk");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ContextDestroyed");
    }
}

web.xml 파일 추가 코드(리스너) :

  <listener>
    <listener-class>me.cjk.MyListener</listener-class>
  </listener>

서블릿 컨테이너가 초기화 된 직 후, setAttribute()메소드로 namecjk을 설정하였다. 이렇게 설정 함으로써 어느 서블릿에 접근하더라도 getAttributename의 값, cjk를 가져올 수 있다.

서블릿 필터

들어온 요청을 서블릿으로 보내고, 또 서블릿이 작성한 응답을 클라이언트로 보내기 전에 특별한 처리가 필요한 경우에 사용할 수 있다. 동시다발적으로 진행되는 것이 아니라,체인 형태의 구조로 이루어져 있어 순차적으로 필터가 진행되며, 보통 전/후처리가 필요할 때 사용된다.

package me.cjk;

import javax.servlet.*;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter Init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter");
        filterChain.doFilter(servletRequest,servletResponse); // 맨 마지막 필터는 결국 서블릿으로 연결된다.
    }

    @Override
    public void destroy() {
        System.out.println("Filter Destroy");
    }
}

web.xml 파일(필터 등록) :

  <filter>
    <filter-name>myFilter</filter-name>
    <filter-class>me.cjk.MyFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>myFilter</filter-name>
    <servlet-name>hello</servlet-name>
  </filter-mapping>

결과 :

ContextInitialized
Filter Init
init
Filter
doGet
Filter
doGet

참고자료

  1. 스프링 웹 MVC

oksusutea's blog

꾸준히 기록하려고 만든 블로그