4 minute read

web.xml

현재 프로젝트(StudyServlet)로의 서블릿 요청이 발생하면 톰캣은 무조건 배포 서술자(Deployment Descriptor) 역할을 하는 WEB-INF/web.xml 에서 해당 요청 정보를 탐색한다. 만약, 프로젝트명(컨텍스트 루트)에 해당하는 요청이 들어오면 web.xml 파일 내의 welcome-file-list 태그의 welcome-file 항목과 일치하는 항목 여부를 판별하여 일치하는 파일명이 있을 경우 해당 파일을 응답한다. 만약, 일치하는 파일이 없을 경우 배포서술자(web.xml)의 매핑 정보를 탐색하거나 @WebServlet 어노테이션이 붙은 클래스를 탐색하고 해당 클래스에 요청을 보낸다.

서블릿 클래스에서 특정 서블릿 주소에 대한 요청을 처리하려면 서블릿 주소 매핑이 필요하며 두 가지 방법이 있다.

web.xml 파일에 매핑

첫 번째 방법은 web.xml(배포서술자) 파일에 매핑 작업을 기술하는 방법이다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>StudyServlet</display-name>
	<welcome-file-list>
	    <welcome-file>index.html</welcome-file>
	    <welcome-file>index.jsp</welcome-file>
	    <welcome-file>index.htm</welcome-file>
	    <welcome-file>default.html</welcome-file>
	    <welcome-file>default.jsp</welcome-file>
	    <welcome-file>default.htm</welcome-file>
  	</welcome-file-list>

  	<servlet>
  		<servlet-name>TestServlet1</servlet-name> 
  		<servlet-class>TestMyServlet</servlet-class> 
  	</servlet>

  	<servlet-mapping>
  		<servlet-name>TestServlet1</servlet-name>
  		<url-pattern>/myServlet</url-pattern>
  	</servlet-mapping>
</web-app>

요청이 들어오면 먼저 welcome-file-list를 확인하고 일치하는 경우가 없다면 servlet-mapping에 url-pattern을 비교한다. 일치하는 패턴을 찾으면 servlet-name이 동일한 servlet의 servlet-class를 실행시킨다.

public class TestMyServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("GET 방식 요청에 대한 doGet() 메서드 호출됨!");
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("POST 방식 요청에 대한 doPost() 메서드 호출됨!");
	}
}

서블릿 클래스는 위와 같이 작성한다. 클래스 정의 시 javax.servlet.http.HttpServlet 클래스를 상속받아 정의한다. 이 때 서블릿 기능을 사용하려면 servlet-api.jar 라이브러리가 필수로 필요하며 톰캣을 사용중일 경우 톰캣 라이브러리(Server Runtime)내에 이미 내장되어 있다.

메서드 파라미터로 HttpServletRequest, HttpServletResponse 객체가 자동 전달된다. JSP 페이지에서 사용하던 내장 객체 request, response 가 이 타입으로 생성된 객체다. 현재 위치는 JSP 에서 스크립틀릿 내부(<% %>) 와 동일한 위치다.

만약, doGet() 또는 doPost() 메서드 중 정의하지 않은 메서드가 있을 경우 해당 방식의 요청이 발생하면 HTTP 405 오류(허용되지 않는 메서드)가 발생하게 된다.

@WebServlet 어노테이션 매핑

두 번째 방법은 서블릿 클래스 선언부 상단에 @WebServlet 어노테이션을 사용하는 방법이다. @WebServlet(“/서블릿주소”) 또는 @WebServlet(“*.패턴”) 형식으로 지정한다. 패턴은 단일 패턴과 다중 패턴으로 나뉜다.

  • 단일 패턴 지정
    “/서블릿주소” 형태로 작성하고, 해당 패턴과 일치하는 URL 만 정확히 감지한다. 단일 패턴을 복수개 한꺼번에 지정도 가능하다.
  • 다중 패턴 지정
    “*.패턴명” 형태로 작성하고 XXX.패턴명 형태로 패턴명에 해당하는 부분만 일치하면 복수개의 서로 다른 URL 을 모두 감지하여 매핑이 가능하다.
@WebServlet("/myServlet3")
public class TestMyServlet3 extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("TestMyServlet3 - doGet()");
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("TestMyServlet3 - doPost()");
	}
	
}

어노테이션을 사용하는 방법이 훨씬 간편하며 배포서술자에 매핑하는 방법과 작동은 완전히 동일하다.

복수개의 서블릿 주소를 하나의 서블릿 클래스로 매핑

@WebServlet({ "/TestMyServlet5", "/myServlet5" })
public class TestMyServlet5 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("TestMyServlet5 - doGet()");
	}
}
<form action="myServlet5" method="get">
	<input type="submit" value="myServlet5 서블릿 주소 요청(GET)">
</form>

<form action="TestMyServlet5" method="get">
	<input type="submit" value="TestMyServlet5 서블릿 주소 요청(GET)">
</form>

서로 다른 서블릿 주소 요청들을 클래스 하나로 처리할 수 있다.

서로 다른 서블릿 주소 요청들을 클래스 하나로 처리

@WebServlet("*.do")
public class TestMyServlet6 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("TestMyServlet6 - doGet()");
	}
}

모든 주소 중 xxx.do 로 끝나는 주소가 모두 매핑된다.

<form action="myServlet6-1.do" method="get">
	<input type="submit" value="myServlet6-1.do 서블릿 주소 요청(GET)">
</form>

<form action="myServlet6Get.do" method="get">
	<input type="submit" value="myServlet6Get.do 서블릿 주소 요청(GET)">
</form>

앞 부분과 관계없이 마지막이 무조건 .do 로 끝나는 모든 주소를 하나의 클래스로 매핑이 가능하다.

서블릿 생명 주기

public class Test7ServletLifeCycle extends HttpServlet {
	@Override
	public void init() throws ServletException {
		System.out.println("init() 메서드 호출됨!");
		super.init();
	}
	
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("service() 메서드 호출됨!");
		super.service(req, resp);
	}

	@Override
	public void destroy() {

		System.out.println("destroy() 메서드 호출됨!");
		super.destroy();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Test7ServletLifeCycle - doGet()");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Test7ServletLifeCycle - doPost()");
	}

}

init() -> service() -> doGet() or doPost() -> destroy() 순서로 호출된다.

  • init()
    서블릿 클래스에 대한 요청 시 가장 먼저 자동으로 호출된다. 서블릿 클래스에 대한 인스턴스 생성 후 인스턴스 초기화 목적으로 호출된다. 서블릿 라이프 사이클 상에서 첫 요청에 대해 최초 1회만 실행된다.(= 서버 중지 전까지)
  • service()
    init() 메서드 호출 후 자동으로 호출된다. 서블릿 인스턴스 내에서 요청에 대한 쓰레드(Thread)가 1개 생성된다. 요청이 발생할 때마다(= 서블릿 호출될 때마다) 매번 호출된다. 즉, 매 요청마다 쓰레드가 1개씩 생성된다. 일반적으로 애플리케이션 개발 시 오버라이딩 하지 않는 메서드다.
  • destroy()
    서비스(= 톰캣 서버)가 중지(= 종료)될 때 자동으로 1회 호출되며 자바 콘솔에서 기본 상태로 확인이 불가능하다.

redirect

@WebServlet("/redirectServlet")
public class Test8RedirectServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String id = request.getParameter("id");
		String passwd = request.getParameter("passwd");
		response.sendRedirect("test8_redirect_result.jsp");
	}
}

현재 서블릿 주소가 http://localhost:8080/StudyServlet/redirectServlet 이면 컨텍스트 루트(StudyServlet)에서 실행중인 서블릿이므로 현재 위치가 webapp 폴더와 같다. 즉, webapp/test8_redirect_result.jsp 페이지로 이동 시 같은 위치로 취급하면 된다.
만약, 현재 서블릿 주소가 http://localhost:8080/StudyServlet/test/redirectServlet 일 경우 현재 위치를 상대경로로 지정 시 test 폴더 내의 jsp 파일을 찾게 되는데 실제 test 폴더는 존재하지 않고 webapp 폴더에 jsp 파일이 존재하므로 상대 주소를 “../test8_redirect_result.jsp” 로 지정해야한다.

<!-- test8_redirect_result.jsp -->
<h3>아이디 : ${param.id }</h3>
<h3>패스워드 : ${param.passwd }</h3>
<!-- 새로운 request 객체 생성으로 인해 이전에 저장된 폼 파라미터 값에 접근 불가 --%>

Redirect 방식 포워딩은 새로운 요청이 발생하므로 웹브라우저 주소표시줄의 URL 이 변경된다. 그리고 이전 요청에서 생성된 request 객체가 유지되지 않는다.(= 새 request 객체가 생성됨). 따라서, 이전 요청 시 저장된 폼 파라미터 데이터는 더 이상 존재하지 않으므로 새 페이지에서 이전 request 객체의 데이터에 접근이 불가능하다.

dispatch

@WebServlet("/dispatchServlet")
public class Test8DispatchServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String id = request.getParameter("id");
		String passwd = request.getParameter("passwd");
		RequestDispatcher dispatcher = request.getRequestDispatcher("test8_dispatch_result.jsp");

		// 파라미터 : 이전 요청에서 사용된(서블릿 클래스로 전달된) request, response 객체
		dispatcher.forward(request, response);
	}
}
<!-- test8_dispatch_result.jsp -->
<h3>아이디 : ${param.id }</h3>
<h3>패스워드 : ${param.passwd }</h3>
<!--Dispatch 방식으로 포워딩 된 페이지는 이전 request 객체가 유지되어
새로 포워딩 된 현재 페이지에서도 request 객체에 저장된 파라미터 값에 접근 가능 -->
--%>

Dispatch 방식 포워딩은 포워딩 시 지정한 새 URL(주소)가 웹브라우저 주소표시줄에 표시되지 않고 이전의 요청 주소가 그대로 유지된다. 즉, 주소표시줄 주소가 변경되지 않는다. 서버가 직접 포워딩을 수행하므로 클라이언트(웹브라우저)에서는 포워딩 여부를 알 수 없다. 이전 요청에서 생성된 request 객체를 포워딩 시점에서 파라미터로 함께 전달하므로 포워딩 후에도 기존 request 객체가 그대로 유지된다. 따라서, 원래 저장되어 있던 파라미터 등의 데이터도 그대로 유지되므로 포워딩 된 새 페이지에서 데이터 공유가 가능하다.

Leave a comment