서블릿 기초

de_sj_awa·2021년 5월 22일
0

1. 서블릿 기초

서블릿은 JSP 표준이 나오기 전에 만들어진 표준으로 자바로 웹 어플리케이션을 개발할 수 있도록 하기 위해 만들어졌다. 서블릿을 이용하면 자바 클래스를 이용해서 웹 어플리케이션을 개발하게 된다. 일반적인 서블릿의 개발 과정은 다음과 같다.

  1. 서블릿 규약에 따라 자바 코드를 작성한다.
  2. 자바 코드를 컴파일해서 클래스 파일을 생성한다.
  3. 클래스 파일을 /WEB-INF/classes 폴더에 패키지에 알맞게 위치시킨다.
  4. web.xml 파일에 서블릿 클래스를 설정한다.
  5. 톰캣 등의 컨테이너를 실행한다.
  6. 웹 브라우저에서 확인한다.

서블릿 개발 과정은 JSP와 비교하면 몇 가지 과정이 더 들어가 복잡한데, 이런 이유로 서블릿보다는 JSP를 사용하게 된다. 하지만, MVC 패턴을 지원하는 프레임워크를 만들어야 하는 경우 서블릿으로 기반 코드를 개발하는 경우가 많기 때문에, 서블릿 코드를 직접 구현하지는 않더라도 웹 개발을 배울 때 서블릿 자체에 대해서 이해하는 것은 중요하다.

2. 예제 프로젝트 생성

1. 서블릿 구현

아주 간단한 서블릿 클래스 코드를 보면서 서블릿의 구현 방법을 살펴보도록 하자.

package example;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public class NowServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        response.setContentType("text/html; charset=utf-8");

        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>현재시간</title></head>");
        out.println("<body>");
        out.println("현재 시간은");
        out.println(new Date());
        out.println("입니다.");
        out.println("</body></html>");
    }
}

서블릿 클래스를 구현하려면 먼저 HttpServlet 클래스를 상속받은 클래스를 작성해야 한다.

HttpServlet 클래스를 상속받았다면, 처리하고자 하는 HTTP 방식(method)에 따라 알맞은 메서드를 재정의해서 구현해야 한다. 예를 들어, 서블릿이 GET 방식의 요청을 처리해야 한다면, doGet() 메서드를 재정의하면 된다. doGet() 메서드는 HttpServletRequest와 HttpServletResponse의 두 파라미터를 갖는데, 이 두 파라미터는 각각 JSP의 기본 객체와 response 기본 객체에 해당한다.

재정의한 메서드는 request를 이용해서 웹 브라우저의 요청 정보를 읽어오던가 response를 이용해서 응답을 전송할 수 있다. 응답을 전송하려면 위의 예제와 같이 response.setContentType() 메서드를 이용해서 응답의 컨텐츠 타입을 지정해야 한다.

response.setContentType("text/html; charset=utf-8");

예제의 경우 응답 컨텐츠 타입이 text/html이고 캐릭터 셋이 utf-8이라고 지정하고 있다. 여기서 setContentType() 메서드에 전달되는 값은 JSP에서 page 디렉티브의 contentType 속성값과 동일하다.

응답의 컨텐츠 타입을 지정했다면, 그 다음으로 할 작업은 실제로 응답 결과를 웹 브라우저에 전송하는 것이다. 웹 브라우저 데이터를 전송하려면 위의 예제와 같이 response.getWriter()로 문자열 데이터를 출력할 수 있는 PrintWriter를 구해야 한다.

PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>현재시간</title></head>");
...
out.println("</body></html>");

PrintWriter는 println() 메서드를 제공하는데, 이 메서드를 이용해서 전송할 응답 데이터를 전달한다. println() 메서드에 전달한 데이터는 웹 브라우저에 전송되어 화면에 출력된다.

이클립스를 사용하면 이클립스가 서블릿 관련 클래스패스를 알아서 잡아주고 자바 코드를 컴파일하므로 컴파일과 관련된 부분을 신경 쓸 필요가 없지만, 직접 자바 코드를 작성하고 컴파일하려면 서블릿 API를 클래스패스에 추가해야 한다. 서블릿 관련 jar 파일은 [톰캣]\lib\servlet-api.jar 파일에 포함되어 있으므로 이 파일의 클래스패스에 추가하고 컴파일해야 한다.

2. web.xml로 매핑하기

서블릿 클래스를 생성했다면, 그다음으로 할 작업은 WEB-INF 폴더의 web.xml 파일에 서블릿 클래스를 등록하는 것이다. 아래 예제 코드는 NowServlet 클래스를 web.xml 파일에 등록한 예를 보여주고 있다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

     <servlet>
        <servlet-name>now</servlet-name>
        <servlet-class>example.NowServlet</servlet-class>
    </servlet>

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

서블릿을 등록하려면 다음의 두 가지를 설정해야 한다.

  • 서블릿으로 사용할 클래스
  • 서블릿과 URL 간의 매핑

먼저 <servlet> 태그를 이용해서 서블릿 클래스를 등록한다. 이 때 <servlet-name>은 해당 서블릿을 참조할 때 사용할 이름을 입력하고 <servlet-class>는 서블릿으로 사용할 클래스의 완전한 이름을 입력한다. 위 예의 경우 example.NowServlet 클래스를 "now"라는 이름의 서블릿으로 등록한다.

사용할 서블릿을 지정했다면 그다음으로 할 작업은 해당 서블릿이 어떤 URL을 처리할지에 대한 매핑 정보를 등록하는 것이다. 이는 <servlet-mapping> 태그를 이용해서 지정한다. <servlet-class> 태그에서 <servlet-name>은 매핑할 서블릿의 이름을 지정하며, <url-pattern>은 매핑할 URL의 패턴을 지정한다. 예를 들어, 위의 코드에서는 now라는 이름의 서블릿이 /now라는 URL 패턴을 처리한다고 지정하고 있다. 앞서 위의 코드에서 NowServlet 클래스를 now라는 이름의 서블릿으로 등록했으므로, 결과적으로 /now라는 URL을 NowServlet이 처리하게 된다.

실제로 NowServlet이 정상적으로 동작하는지 확인하기 위해 톰캣을 실행하고 웹 브라우저에 http://localhost:8080/chap17/now URL을 입력해보자. 그러면 다음과 같은 결과 화면이 출력되는 것을 확인할 수 있다.

<url-pattern>은 다음과 같이 한 번 이상을 사용할 수 있다. 이 경우 각각의 URL 패턴에 해당 서블릿을 매핑한다.

    <servlet-mapping>
        <servlet-name>now</servlet-name>
        <url-pattern>/now</url-pattern>
        <url-pattern>/now2</url-pattern>
    </servlet-mapping>

서블릿 매핑할 때 사용하는 <url-pattern>은 웹 어플리케이션 경로를 제외한 나머지 경로를 기준으로 적용된다. 예를 들어, 앞서 web.xml 설정에서 다음과 같이 서블릿 매핑을 설정했다.

    <servlet-mapping>
        <servlet-name>now</servlet-name>
        <url-pattern>/now</url-pattern>
    </servlet-mapping>

이 경우 실제 <url-pattern>의 /now와 매핑되는 URL은 웹 어플리케이션 경로를 포함한 /chap17/now가 된다. <url-pattern>에는 이외에도 다양한 매핑 규칙을 지원한다.

3. 애노테이션으로 매핑하기

서블릿 2.5 버전까지는 web.xml 파일에 서블릿으로 등록해야 서블릿 클래스를 사용할 수 있었는데, 서블릿 3.0 버전부터는 @WebServlet 애노테이션을 사용하면 web.xml 파일에 따로 등록하지 않아도 서블릿으로 등록된다. 톰캣 7(서블릿 3.0)이나 8 버전(서블릿 3.1)처럼 서블릿 3.0을 지원하는 웹 컨테이너는 @WebServlet이 적용된 클래스를 검색해서 서블릿으로 자동으로 등록한다.

@WebServlet 애노테이션은 아래 예제 코드와 같이 사용된다.

package example;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html; charset=utf-8");

        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>인사</title></head>");
        out.println("<body>");
        out.println("안녕하세요, ");
        out.println(request.getParameter("name"));
        out.println("님");
        out.println("</body></html>");
    }
}

위에서 @WebServlet 애노테이션은 urlPatterns 속성을 갖는데 이 속성은 해당 서블릿에 매핑될 URL 패턴을 지정할 때 사용된다. 위 코드는 /hello로 들어오는 URL을 HelloServlet이 처리하도록 설정하고 있다. 만약 두 개 이상의 URL 패턴을 처리하도록 하고 싶다면 다음과 같이 urlPatterns 속성값으로 배열을 전달하면 된다.

@WebServlet(urlPatterns = {"/hello, "/hello1"})

HelloServlet 클래스를 컴파일한 뒤 톰캣을 재시작해보자. 톰캣이 chap17 웹 어플리케이션을 구동하면서 @Webservlet 애노테이션이 적용된 서블릿을 찾게 되므로, 사용할 서블릿으로 HelloServlet을 등록하게 된다. 따라서 web.xml에 별도의 설정을 하지 않더라도 http://localhost:8080/chap17/hello URL을 이용해서 HelloServlet을 실행할 수 있게 된다.

@WebServlet 애노테이션을 사용할 때 고려할 점은 서블릿이 범용적으로 사용되는 서블릿인지의 여부이다. MVC 프레임워크는 어떤 URL을 서블릿이 처리할지 미리 알 수 없다. 단지, 다양한 요청 URL을 MVC 프레임워크가 처리할 수 있는 기능을 구현할 수 있을 뿐이다.

이는 @WebServlet 애노테이션을 사용할 경우 서블릿이 처리해야 할 URL 패턴이 변경될 때마다, 자바 소스 코드의 urlPatterns 속성값을 변경하고 다시 컴파일해야 한다는 것을 뜻한다. 반면, @WebServlet을 사용하지 않고 web.xml 파일을 사용하면 URL 경로가 바뀔 경우 web.xml 파일만 변경하면 된다. 따라서, 서블릿의 용도에 따라서 @WebServlet 애노테이션을을 사용할지 아니면 web.xml 설정을 사용할지를 알맞게 결정해야 한다.

@WebServlet 애노테이션이 적용된 서블릿을 web.xml 파일에 등록하면 어떻게 될까? 이 경우 @WebServlet 애노테이션에 적용된 URL 패턴과 web.xml 파일에 등록된 URL 패턴에 모두 매핑된다. 정확하게는 @WebServlet 애노테이션이 적용된 클래스의 객체가 생성되고 web.xml 파일에서 지정한 클래스의 객체가 생성된다. 즉, 서로 다른 두 개의 객체가 생성되고 각각 @WebServlet 애노테이션에서 지정한 URL 패턴과 web.xml 파일에 지정한 URL 패턴에 매핑된다.

4. HTTP 각 방식별 구현 메서드

HTTP는 GET, POST, HEAD, PUT, DELETE의 방식을 지원하고 있는데, 일반적으로 웹에서 사용되는 그리고 웹 브라우저가 지원하는 방식은 GET과 POST이다.

HttpServlet은 HTTP의 각 방식에 따라 알맞은 메서드를 이용해서 구현하도록 정의하고 있다. 예를 들어, GET 방식은 doGet() 메서드를 이용해서 처리하고, POST 방식의 경우 doPost() 메서드를 이용해서 처리하도록 정의하고 있다. GET과 POST 방식의 요청을 처리하는 메서드는 각각 다음과 같다.

  • GET 방식 :
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

  • POST 방식 :
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

만약 GET 방식과 POST 방식을 모두 처리해야 하는 서블릿이라면 다음과 같이 두 메서드를 모두 재정의해서 알맞게 처리해주면 된다.

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	... // GET 방식에 대한 처리, 예: 폼 출력하기
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	... // POST 방식에 대한 처리, 예: 폼 데이터 처리하기
}

5. 서블릿 로딩과 초기화

NowServlet의 코드를 다시 보자.

public class NowServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        response.setContentType("text/html; charset=utf-8");

        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head><title>현재시간</title></head>");
        out.println("<body>");
        out.println("현재 시간은");
        out.println(new Date());
        out.println("입니다.");
        out.println("</body></html>");
    }
}

web.xml에서 NowServlet을 /now 경로에 매핑했다. 웹 브라우저에서 /now 경로로 접근하면 NowServlet을 실행하는데, 서블릿 컨테이너는 처음 서블릿을 실행할 때 서블릿 객체를 생성한다. 아래 그림과 같이 서블릿을 최초 요청할 때 서블릿 객체를 생성하고, 이후 요청이 오면 앞서 생성한 서블릿 객체를 그대로 사용한다.

위의 그림에서 웹 컨테이너가 서블릿 객체를 생성하고 init() 메서드를 호출하는 과정을 '서블릿 로딩' 과정이라고 한다. 서블릿 로딩 과정을 보면 init() 메서드를 호출하는데, 서블릿은 이 init() 메서드를 이용해서 필요한 초기화 과정을 수행한다. init() 메서드의 기본 구현은 다음과 같다.

// GenericServlet 구현
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

public void init() throws ServletException {
}

서블릿 컨테이너는 서블릿을 초기화하기 위해 ServletConfig 파라미터를 갖는 init() 메서드를 실행한다. 위 코드에서 보는 것처럼 init(ServletConfig) 메서드는 다시 파라미터가 없는 init() 메서드를 호출한다. 따라서, 초기화가 필요한 서블릿을 파라미터가 없는 init() 메서드를 재정의하면 된다. (물론, ServletConfig가 필요하면 ServletConfig를 파라미터롤 갖는 init() 메서드를 재정의하면 된다.) DBCPInit 클래스도 다음과 같이 파라미터가 없는 init() 메서드를 재정의해서 초기화를 했다.

public class DBCPInit extends HttpServlet {

    @Override
    public void init() throws ServletException {  // 서블릿을 초기화할 때 사용
        loadJDBCDriver();
        initConnectionPool();
    }

보통 초기화 작업은 상대적으로 시간이 오래 걸리기 때문에, 처음 서블릿을 사용하는 시간보다는 웹 컨테이너를 처음 구동하는 시점에 초기화를 진행하는 것이 좋다. 이를 위한 <load-on-startup> 태그이다. 예를 들어, DBCP 초기화를 위해 web.xml 파일에 추가한 설정은 다음과 같았다.

<servlet>
  <servlet-name>DBCPInit</servlet-name>
  <servlet-class>jdbc.DBCPInit</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<load-on-startup> 태그를 설정하면 웹 어플리케이션을 시작할 때 서블릿을 로딩한다. 즉, 톰캣을 구동하는 시점에 DBCPInit 서블릿 객체를 생성하고 init() 메서드를 실행한다. 웹 어플리케이션을 시작하는 시점에 커넥션 풀을 초기화하므로, JSP나 서블릿 코드에서 커넥션 풀을 사용할 수 있게 된다.

<load-on-startup> 태그의 값은 로딩 순서를 의미한다. 값을 기준으로 오름차순으로 서블릿을 로딩한다. 예를 들어, 다음과 같은 설정이 있으면 <load-on-startup>의 값의 작은 Config 서블릿을 먼저 로딩하고 그다음에 DBCPInit 서블릿을 로딩한다.

<servlet>
  <servlet-name>Config</servlet-name>
  <servlet-class>config.ConfigInit</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
  <servlet-name>DBCPInit</servlet-name>
  <servlet-class>jdbc.DBCPInit</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>

@WebServlet 태그를 사용하는 경우에는 loadOnStartup 속성을 이용해서 로딩 값을 지정한다.

@WebServlet(urlPatterns = "/hello", loadOnStartup = 1)
public class InitServlet extends HttpServlet{
    ...
}

6. 초기화 파라미터

DBCPInit 코드의 일부를 다시 보자.

public class DBCPInit extends HttpServlet {

    @Override
    public void init() throws ServletException {
        loadJDBCDriver();
        initConnectionPool();
    }

    private void loadJDBCDriver() {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException("fail to load JDBC Driver", ex);
        }
    }

    private void initConnectionPool() {
        try {
            String jdbcUrl =
                    "jdbc:mysql://localhost:3306/jsptest?" +
                            "useUnicode=true&characterEncoding=utf8";
            String username = "jspexam";
            String pw = "jsppw";
            ... 코드 생략
    }

DBCPInit 클래스는 한 가지 단점이 있다. 위 코드에서 보듯이 JDBC 드라이버 클래스 이름과 JDBC URL, DB 사용자 정보가 코드에 있기 때문에, DB를 MySQL에서 오라클로 변경하거나 JDBC URL을 변경하려면 코드를 직접 수정해야 한다.

서블릿은 코드를 직접 변경하지 않고 사용할 값을 변경할 수 있는 방법을 제공하는데, 그 방법은 초기화 파라미터를 사용하는 것이다. 서블릿은 web.xml의 <init-param> 태그를 이용해서 서블릿을 초기화할 때 필요한 값을 전달하는 방법을 제공하고 있다. 예를 들어, 다음과 같이 초기화 파라미터를 전달할 수 있다.

<servlet>
  <servlet-name>DBCPInit2</servlet-name>
  <servlet-class>jdbc.DBCPInit2</servlet-class>
  <init-param>
    <param-name>jdbcdriver</param-name>
    <param-value>com.mysql.jdbc.Driver</param-value>
  </init-param>
  <init-param>
    <param-name>poolName</param-name>
    <param-value>chap14</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<init-param> 태그는 서블릿의 초기화 파라미터를 지정할 때 사용하며 자식 태그는 다음과 같다.

  • <param-name> 태그 : 초기화 파라미터의 이름을 지정한다.
  • <param-value> 태그 : 초기화 파라미터의 값을 지정한다.

위 코드는 jdbcDriver라는 이름의 초기화 파라미터 값으로 com.mysql.jdbc.Driver를 지정하고, poolName 초기화 파라미터의 값으로 chap14를 지정하고 있다.

서블릿 클래스에서 초기화 파라미터에 접근하려면 getInitParameter() 메서드를 사용하면 된다. 초기화 파라미터를 사용해서 커넥션 풀과 관련된 설정 정보를 전달받도록 구현한 DBCPInit2 클래스는 아래 예제 코드처럼 getInitParameter() 메서드를 사용해서 초기화 파라미터를 사용한다.

package jdbc;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import org.apache.commons.dbcp2.*;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import java.sql.DriverManager;

public class DBCPInit2 extends HttpServlet {

    @Override
    public void init() throws ServletException {
        loadJDBCDriver();
        initConnectionPool();
    }

    private void loadJDBCDriver() {
        String driverClass = getInitParameter("jdbcdriver");
        try {
            Class.forName(driverClass);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException("fail to load JDBC Driver", ex);
        }
    }

    private void initConnectionPool() {
        try {
            String jdbcUrl = getInitParameter("jdbcUrl");
            String username = getInitParameter("dbUser");
            String pw = getInitParameter("dbPass");

            ConnectionFactory connFactory =
                    new DriverManagerConnectionFactory(jdbcUrl, username, pw);

            PoolableConnectionFactory poolableConnFactory =
                    new PoolableConnectionFactory(connFactory, null);
            poolableConnFactory.setValidationQuery("select 1");

            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            poolConfig.setTimeBetweenEvictionRunsMillis(1000L * 60L * 5L);
            poolConfig.setTestWhileIdle(true);
            poolConfig.setMinIdle(4);
            poolConfig.setMaxTotal(50);

            GenericObjectPool<PoolableConnection> connectionPool =
                    new GenericObjectPool<>(poolableConnFactory, poolConfig);
            poolableConnFactory.setPool(connectionPool);

            Class.forName("org.apache.commons.dbcp2.PoolingDriver");
            PoolingDriver driver =
                    (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");
            String poolName = getInitParameter("poolName");
            driver.registerPool("chap14", connectionPool);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

DBCPInit2 클래스는 getInitParameter() 메서드를 이용해서 "jdbcdriver" 초기화 파라미터 값을 읽어와, 이 값을 이용해서 JDBC 드라이버를 로딩한다. 또 커넥션을 생성할 때 사용할 JDBC URL, DB 사용자, 암호를 초기화 파라미터에서 읽어오고, 커넥션 풀의 이름으로 사용할 초기화 파라미터를 읽어온다.

web.xml 파일을 서블릿이 필요로 하는 초기화 파라미터를 알맞게 설정해주면 된다. DBCPInit2를 위한 설정 예는 아래와 같다.

    <servlet>
        <servlet-name>DBCPInit2</servlet-name>
        <servlet-class>jdbc.DBCPInit2</servlet-class>
        <init-param>
            <param-name>jdbcdriver</param-name>
            <param-value>com.mysql.jdbc.Driver</param-value>
        </init-param>
        <init-param>
            <param-name>jdbcUrl</param-name>
            <param-value>jdbc:mysql://localhost:3306/jsptest?characterEncoding=utf8</param-value>
        </init-param>
        <init-param>
            <param-name>dbUser</param-name>
            <param-value>jspexam</param-value>
        </init-param>
        <init-param>
            <param-name>dbPass</param-name>
            <param-value>jsppw</param-value>
        </init-param>
        <init-param>
            <param-name>poolName</param-name>
            <param-value>chap14</param-value>
        </init-param>
    </servlet>

JDBC URL이나 다른 설정을 바꾸려면 서블릿을 변경할 필요 없이 web.xml 파일의 초기화 파라미터 값을 변경하면 된다. 예를 들어, 커넥션 풀의 이름을 변경하고 싶다면 poolName 초기화 파라미터 값을 "chap14"에서 다른 이름으로 변경한 뒤 톰캣을 재시작하면 된다.

getInitParameter() 메서드는 지정한 초기화 파라미터가 존재하면 해당 값을 리턴하며, 존재하지 않으면 null을 리턴한다. 따라서, null인 경우 기본값을 사용하고 싶거나 다른 예외 처리를 하고 싶다면 초기화 파라미터 값이 null인지 여부를 확인해서 알맞게 처리해야 한다.

// 초기화 파라미터가 없으면 null을 리턴한다.
String poolName = getInitParameter("poolName");
if (poolName == null) poolName = "pool";

웹 어플리케이션에서 전반적으로 필요한 초기화 작업을 수행하는 또 다른 방법으로는 ServletContextListener를 사용하는 것이 있다.

@WebServlet 애노테이션으로 매핑한 경우 초기화 파라미터를 전달하려면 initParams 속성의 값으로 @WebInitParam 애노테이션 목록을 전달하면 된다. 다음의 설정 예를 보여주고 있다.

@WebServlet(urlPatterns = {"/hello", "/hello1"},
  initParams = {
      @WebInitParam(name="greeting", value="Hello"),
      @WebInitParam(name="title", value="제목")
  }
}

초기화 파라미터를 사용하는 이유 중의 하나는 클래스의 수정 없이 초기화 과정에서 필요한 값을 수정할 수 있기 때문이다. 하지만, 소스 코드에 @WebInitParam 애노테이션을 이용해서 초기화 파라미터를 설정하면, 초기화 설정을 변경해야 할 때마다 자바 코드를 수정해야 하기 때문에 @WebInitParam 애노테이션은 변경의 유연함을 떨어뜨린다.

3. URL 패턴 매핑 규칙

<servlet-mapping> 태그는 <url-pattern> 태그를 사용해서 서블릿과 URL을 매핑하고, @WebServlet의 경우 urlPatterns 속성을 이용해서 서블릿과 URL을 매핑한다는 내용을 앞에서 살펴봤다. 여기서는 <url-pattern> 태그와 urlPatterns 속성에서 사용할 수 있는 URL 패턴이 어떻게 서블릿과 매핑되는지 자세히 살펴보자.

서블릿 규약에 따르면, URL 패턴은 다음 규칙에 따라 서블릿을 매핑한다.

  • '/'로 시작하고 '/*'로 끝나는 url-pattern은 경로 매핑을 위해서 사용한다.
  • '*.'로 시작하는 url-pattern은 확장자에 대한 매핑을 할 때 사용한다.
  • 오직 '/'만 포함하는 경우 어플리케이션의 기본 서블릿으로 매핑한다.
  • 이 규칙 외, 나머지 다른 문자열은 정확한 매핑을 위해서 사용한다.

이해를 돕기 위해 서블릿 규약에 포함된 예제를 발췌해 보았다. URL 패턴에 따른 처리 서블릿을 아래 표와 같이 설정했다고 하자.

URL 패턴 매핑 서블릿
/too/bar/* servlet1
/baz/* servlet2
/catalog servlet3
*.bop servlet4

위의 표와 같이 URL 패턴을 설정한 경우 실제 요청 경로에 따라서 요청을 처리하는 서블릿은 아래 표와 같다.

요청 경로 일치 URL 패턴 요청 처리 서블릿
/foo/bar/index.html /foo/bar/* servlet1
/foo/bar/index.bop /foo/bar/* servlet1
/baz /baz/* servlet2
/baz/index.html /bax/* servlet2
/catalog /catalog servlet3
/catalog/racecar.bop *.bop servlet4
/index.bop *.bop servlet4

서블릿 3에 추가된 주요 기능 중의 하나로 비동기 서블릿이 있다.

참고

  • 최범균의 JSP2.3 웹 프로그래밍
profile
이것저것 관심많은 개발자.

0개의 댓글