Servlet - 서블릿 생명주기와 HttpServletRequest, HttpServletResponse

앞서 서블릿을 이용해 화면에 Hello world를 찍어보는 간단한 실습을 해보았다. 이번엔 이 때 사용했던 개념들에 대해 조금 더 알아보는 시간을 가지려고 한다.


우선 서블릿이 어떻게 호출되고, 실행되는지부터 알아보자.

Servlet 작동원리

웹서버에 HTTP 프로토콜로 요청이 들어오면, 웹서버는 해당 요청을 파악하고, 요청에 맞는 리소스를 다시 HTTP 프로토콜로 반환한다. 간단하지만 이게 웹의 기본이자 핵심이다.

웹의 동작을 천천히 따라가보자.

브라우저 주소창에서 URL을 입력하면, 브라우저는 요청메세지를 만들어서 HTTP 프로토콜을 이용하여 DNS에 보낸다.

요청메세지를 통해 웹서버가 알수있는 정보들

그럼 DNS에서는 해당 URL 주소를 갖는 IP주소를 찾아서 IP주소가 가리키는 웹서버로 아까 클라이언트로부터 받은 요청 메세지를 던진다. 위의 이미지는 웹서버를 띄워서 요청메세지(HttpServletRequest)를 통해 어떤 정보를 웹서버가 알 수 있는지를 보여주는 코드를 작성해보았다.

그럼 웹서버는 이 요청에 맞는 리소스를 찾아서 응답 메세지로 만들어 다시 HTTP 프로토콜을 이용하여 요청메세지의 클라이언트에 반환한다.

이 과정에서 서블릿에서 일어나는 일들을 정리해보면,

웹 서버가 실행되고, 서블릿의 초기화 메서드(init())가 호출된다. 이후 웹 서버에 요청이 들어오면, 서비스(service())가 호출되면서 요청을 파악하게 된다.

서비스에서 요청을 파악하여 HTTP 메서드가 GET이면 doGet()을, POST이면 doPost()을 호출한다.

Apache의 HttpServlet 클래스 문서를 보면, HttpServlet의 메서드들 대부분이 HTTP 메서드와 연관된 메서드(GET/POST/PUT/DELETE..)들임을 확인할 수 있다.

그리고 이 HttpServlet 클래스에서 사용하는 파라미터는 HttpServletRequestHttpServletResponse 인터페이스 객체이다.


Servlet 생명주기

서블릿에 여러가지 메서드가 중요하지만, 서블릿의 시작과 끝 등을 다루는 주요 메서드로는 아래 3가지가 있다.

  • .init()
  • .service(request, response)
  • .destroy()

위 메서드들은 HttpServlet의 메서드를 오버라이딩해서 사용할 수 있다. 서블릿을 생성해서 위 3가지 HttpServlet 메서드를 오버라이딩해서 사용해보자.

생성자와 HttpServlet 3가지 메서드를 포함하여 4가지 메서드가 생성된다. Sysout으로 콘솔에서 식별할 수 있는 문구를 작성해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet("/lifecycle")
public class LifecycleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public LifeCycleServlet() {
System.out.println("LifeCycleServler 생성!!");
}

public void init(ServletConfig config) throws ServletException {
System.out.println("init 호출!!");
}

public void service(HttpServletRequest request, HttpServletResponse response) {
System.out.println("Service 호출!!");
}

public void destroy() {
System.out.println("destory 호출!!");
}
}
1
2
3
4
// console result
LifeCycleServler 생성!!
init 호출!!
Service 호출!!

서버 실행 후, URL로 호출하면, 콘솔결과에 출력되는걸 확인할 수 있다.

  • 생성자 : 서블릿이 생성될때 호출된다.
  • init : 서블릿이 초기화될때 호출된다.
  • service : 서블릿이 호출될때 호출된다.

서버를 실행하고, 브라우저에서 URL을 호출하면, WAS는 서블릿 요청을 받아서 해당 서블릿이 메모리에 있는지 여부를 확인한다. 있으면 바로 service()를 호출하고, 없으면 생성자부터 init()을 호출하게 된다.

처음 웹서버를 실행하고 서블릿을 호출하면,

  1. 생성자 호출
  2. init() 호출
  3. service() 호출

순으로 호출된다.

브라우저에서 다시 URL을 호출하거나, 현재창에서 refresh하면, 1) 생성자, 2) init() 을 skip하고 service()만 호출한다. WAS가 서블릿 요청을 받아서 해당 서블릿이 메모리에 있는지 확인했는데 이미 존재하기 때문에 서블릿을 생성하지 않고 곧바로 service()를 호출한 것이다.

여기서 하나 알아둬야할 점은 JSP 처럼 서블릿을 이용하여 화면에 뿌리는 메서드는 service() 뿐이다. service()에서만 반환객체 HttpServletResponse 를 파라미터로 받고있기 때문이다.
(JSP 엔진에서 사용하는 서비스 메서드명은 _jspService()이다.)

그럼 destory()는 언제 실행되는걸까?

서블릿 클래스를 수정하면, eclipse가 자동으로 수정된 클래스를 바탕으로 재 빌드를 하면서 콘솔화면에 destroy()가 실행된걸 알 수 있다.

즉, destroy()가 호출되는 경우는 WAS가 죽는 경우, 즉 웹 애플리케이션이 종료되는 시점이다.


Servlet 2.x대와 3.x대의 차이

서블릿 버전 2.0과 3.0의 작성법이 다르다고 한다. 3.0에선 HttpServlet을 상속받고, 어노테이션 @WebServlet()을 작성함으로써 간단하게 작성이 가능하지만, 2.0에선 XML 기반으로 작성해야했다.

Servlet 2.xx

1
2
3
4
5
6
7
8
9
10
<servlet>
<description></description>
<display-name>myServlet</display-name>
<servlet-name>myServlet</servlet-name>
<servlet-class>edwith.myServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name></servlet-name>
<url-pattern>/helloservlet</url-pattern>
</servlet-mapping>

Servlet 3.xx

1
2
3
4
5
@WebServlet("/helloservlet")
public class helloservlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ... }
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ... }
}

Servlet의 요청과 응답

요청할 때엔 갖고있는 정보를 HttpServletRequest에 저장한다.

응답을 보낼때는 HttpServletResponse 객체를 생성해서 HttpServletResponse를 통해 응답한다.

HttpServletRequest

  • ServletRequest를 상속받는 클래스이다.

  • http 프로토콜의 request 정보를 서블릿에게 전달하기 위한 목적으로 사용한다.

  • 헤더 정보, 파라미터, 쿠키, URI, URL 등의 정보를 읽어 들이는 메서드를 가진다.

  • body의 stream을 읽어들이는 메서드가 존재한다.

HttpServletRequest 클래스의 주요 메서드

  • getParameterNames() : E
    • 현재 요청에 포함된 매개변수를 반환한다. 열거(Enumeration)로 가져온다. 파라미터가 없으면 빈 열거를 반환한다.
  • getParameter(name) : String
    • 문자열 name과 같은 이름을 가진 매개변수 값을 요청으로부터 가져온다.
  • getParameterValues(name) : String[]
    • 문자열 name과 같은 이름을 가진 매개변수 값을 문자열 배열 형태로 가져온다.
      ( 주로 checkbox, mutilple list 등에 사용 )
  • getCookies() : Cookie[]
    • 모든 쿠키값을 javax.servlet.http.Cookie의 배열 형태로 가져온다.
  • getMethod() : String
    • 현재 요청이 GET인지 POST인지 HTTP Method를 반환한다.
  • getSession() : HttpSession
    • 현재 세션 객체를 가져온다.
  • getRemoteAddr() : String
    • 현재 클라이언트의 IP 주소를 가져온다.
  • getProtocol() : String
    • 현재 서버의 프로토콜을 문자열 형태로 가져온다.
  • setCharacterEncoding() : void
    • 웹서버로 전달되는 내용을 인코딩한다.
  • getAttribute(name) : Object
    • 문자열 name과 같은 이름을 갖는 매개변수 값을 Object 타입으로 가져온다.
  • setAttibute(name, value) : void
    • 문자열 name과 같은 이름을 가진 매개변수의 값을 value로 수정한다.

HttpServletRequest의 메서드를 활용한 예제 코드.

header로 저장되는 정보 브라우저 화면에서 출력하는 법

1
2
3
4
5
6
Enumeration<String> headerNames = request.getHeaderNames();
while( headerNames.hasMoreElements() ) {
String headernames = headerNames.nextElement();
String headerValue = request.getHeader(headernames);
out.println(headerNames+" : "+headerValue+"<br>");
}

HttpServletResponse

​ • WAS는 어떤 클라이언트가 요청을 보냈는지 알고 있고, 해당 클라이언트에게 응답을 보내기 위한 HttpServlerResponse 객체를 생성하여 서블릿에게 전달한다.

​ • 서블릿은 해당 객체를 이용하여 content-type, 응답 메세지 등을 HttpServlerResponse 에 담아서 전송한다.

HttpServletResponse 클래스의 주요 메서드

  • setContentType() : void
    • 문자열 형태의 type에 지정된 MIME Type으로 Content Type으로 지정한다.
  • setHeader(name, value) : void
    • 문자열 name의 이름으로 value 값을 헤더로 설정한다.
  • setDateHeader(name, date) : void
    • 문자열 name의 이름으로 date에 설정된 ms시간 값을 헤더에 설정한다.
  • sendRedirect(url) : void
    • 클라이언트 요청을 다른페이지로 보낸다.

참고 문서

서블릿으로 Hello World 찍어보는 실습 구경하러 가기
-> Servlet 실습하며 알아보기