백엔드 자바 웹 프로그래밍

2.1. 서블릿

들어가면서

이번 장에서는 서블릿에 대해 살펴본다. 서블릿은 자바 플랫폼에서 컴포넌트를 기반으로 하는 웹 애플리케이션 개발에 사용하는 핵심 기술이다.

서블릿 학습을 통해

등에 대해 배우고 컨트롤러(Controller) 개발에 활용할 수 있다.



서블릿(Servlet) 개요

서블릿은 자바로 만들어진 프로그램을 서버에서 실행하기 위해 만들어졌다. 특히 웹 서비스 개발에 특화되어 있으며 데이터베이스 연동, 외부 서비스 연동을 통해 정적인 웹에 동적인 정보 제공을 가능하게 한다.

서블릿 개발 및 동작 구조

서블릿은 순수 자바 코드로 작성되며 코드 자체만으로 보면 일반적인 자바클래스와 다르지 않다. 다만 서블릿 실행을 위해서는 웹 애플리케이션 형식으로 패키징 하는 과정이 필요하며 실행은 서블릿 컨테이너를 통해 이루어진다는 차이가 있다.

일반적인 서블릿의 개발 및 동작 구조는 다음과 같다.

[그림: 서블릿 개발과 동작 과정]
  1. HttpServlet 클래스를 상속받는 서블릿 클래스를 작성
  2. 컴파일후 웹 애플리케이션으로 패키징
  3. 서블릿 컨테이너에 배포
  4. 클라이언트의 URL 요청
  5. 애너테이션에 등록된 URL 매핑정보를 참고해 해당 서블릿 실행
  6. 요청 메서드에 따라 서블릿의 doGet(), doPost() 등의 메서드 호출
  7. 서블릿은 데이터베이스 연동 등 필요한 작업을 수행
  8. 데이터를 포함한 HTML 형식의 데이터를 클라이언트에게 전달

서블릿 컨테이너와 서블릿의 동작 구조는 서블릿 라이프사이클에서 자세히 다룬다.

서블릿 장단점

서블릿의 장점은 다음과 같다.

반면 단점으로는



서블릿 클래스 구현

서블릿 자체는 자바코드로 구현하지만 서블릿 컨테이너에 해당 클래스가 서블릿임을 알려야 하고 어떤 URL 접근에 실행해야 하는지 등록하는 과정이 필요하다. 기본적으로는 웹 애플리케이션 배포 서술자인 web.xml 에 등록해야 하지만 지금은 별도의 web.xml 작성 없이 자바 애너테이션을 이용한 방법이 사용되고 있다.

서블릿 클래스 구조

서블릿 클래스는 javax.servlet.Servlet 인터페이스를 구현한 추상 클래스인 GenericServlet 클래스와 HttpServlet 클래스중 하나를 상속해 구현하는 형태 이다. 일반적인 웹 개발의 경우 HTTP 프로토콜에 최적화 되어 있는 HttpServlet 클래스를 상속해서 구현한다.

서블릿이 제공하고자 하는 HTTP 메서드에 따라 doGet(), doPost()등의 메서드를 오버라이딩해서 필요한 내용을 구현하면 된다.

public class HelloWorldServlet extends HttpServlet {
    public doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ...
    }
    
    public doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
    ...
}

doGet(), doPost() 메서드에서 파라미터로 사용되고 있는 request와 response는 서블릿 컨테이너가 클라이언트 요청과 응답을 처리할 수 있도록 서블릿에 제공하는 객체로 클라이언트와의 상호작용에 필요한 다양한 메서드를 제공하고 있다.

request와 response 는 session, application 과 함께 JSP의 내장객체이기도 하며 속성 관리 기능을 이용해 컨트롤러와 뷰페이지간의 데이터 전달등의 목적에도 사용되므로 잘 알아두도록 한다.

HttpServletRequest

http프로토콜의 request정보를 서블릿에게 전달하기 위한 목적으로 사용한다. 헤더정보, 파라미터, 쿠키, URI, URL 등의 정보를 읽어 들이는 메소드를 가지고 있으며 Body의 Stream을 읽어 들이는 메소드를 가지고 있다

HttpServletRequest 에서 제공하는 주요 메서드는 다음과 같다.

메서드 반환 예 설명
getParameter(name) hong1234 name 속성으로 전달된 파라미터 값
getParameterValues(name) {hong, kang, kim} 동일한 name 속성으로 전달된 모든 파라미터 값
getRequestURL() http://www.xxx.com:8080/shop/list.jsp  
getRequestURI() /shop/list.jsp URL에서 스키마, 서버이름, 포트번호를 제외한 나머지 주소와 파라미터
getScheme() http http, https, ftp와 같은 프로토콜
getServerName() www.xxx.com 서버 이름
getServerPort() 8080 서버 포트
getContextPath() /shop 컨텍스트 경로
getMethod() GET GET, POST 등의 HTTP 메소드
isSecure() false SSL 보안 여부. https와 같은 보안 채널의 사용 여부 true/false
getLocale() ko_KR 지역 정보
getProtocol() HTTP/1.1 사용하는 프로토콜. 프로토콜/메이저버전.마이너버전
getLocalAddr() 127.0.0.1 서버의 로컬 IP 주소
getRemoteAddr() 210.102.111.212 클라이언트 IP 주소

HttpServletResponse

서블릿 컨테이너는 요청 클라이언트에게 응답을 보내기 위한 HttpServleResponse객체를 생성하여 서블릿에게 전달한다. 서블릿은 해당 객체를 이용하여 content type, 응답코드, 응답 메시지등을 전송할 수 있다.

HttpServletResponse 에서 제공하는 주요 메서드는 다음과 같다.

메서드 설명
sendRedirect(String location) 클라이언트에게 리다이렉트(redirect) 응답을 보낸 후 특정 URL로 다시 요청하게 함
getWriter() 클라이언트로 데이터를 보내기 위한 출력 스트림을 리턴
setContentType(String type) 클라이언트에 전달되는 콘텐츠 타입 지정
addCookie(Cookie cookie) 응답에 쿠키를 추가
addHeader(String name, String value) name과 value를 헤더에 추가
encodeURL(String url) 클라이언트가 쿠키를 지원하지 않을 때 세션 id를 포함한 특정 URL을 인코딩
getHeaderNames() 현재 응답이 헤더에 포함된 name을 얻어옴

request, response 의 전체 메서드는 Servelt 4.0 API를 참고하기 바란다.

서블릿 정보 등록

앞에서 언급한것 처럼 서블릿 클래스만으로는 톰캣에서 실행이 불가능 하며 web.xml 이나 애너테이션으로 서블릿 임을 선언해야 한다.

다음은 서블릿 2.x에서 사용가능한 web.xml 의 작성 예이다. 지금은 거의 사용하지 않는다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
  <servlet>
    <servlet-name>HelloWorld</servlet-name> //servlet 이름
    <servlet-class>jwprj.servlet.HelloServlet</servlet-class> //서블릿 클래스 지정
  </servlet>
  
  <servlet-mapping>
    <servlet-name>HelloWorld</servlet-name> //servlet name을 매핑
    <url-pattern>/hello</url-pattern>  //서블릿 요청 주소 매핑
  </servlet-mapping>
</web-app>

서블릿 3.0에서 부터는 자바 애너테이션을 이용해 다음과 같이 등록 한다. 물론 필요하다면 web.xml 을 사용하는 것도 가능하지만 애너테이션 사용이 권장 된다.

@WebServlet(description="Hello World Servlet", urlPatterns="/hello")
public class HelloWorldServlet extends HttpServlet {
    ...
}

애너테이션 방식이 훨씬 깔끔하고 간단한 것을 알 수 있다. urlPatterns 는 어떤 클라이언트 요청에 해당 서블릿을 실행할지 지정하는 것으로 여러 url 을 등록할 수 있으며 패턴 형식으로 특정 형식의 경우 실행될 수 있도록 하는것도 가능하다.

애너테이션과 관련해서는 이곳을 참고 하도록 한다.



서블릿 생명주기

서블릿 클래스는 기본적으로 doGet(), doPost()와 같이 HTTP 요청 메서드에 따라 필요한 메서드를 오버라이딩해 구현하는 구조이며 서블릿 컨테이너에 의해 객체의 생성과 소멸등이 관리 되므로 필요에 따라 특정 라이프사이클 이벤트에 동작하는 메서드를 구현하기도 한다.

서블릿의 생성과 소멸은 다음과 같은 과정을 거친다.

[그림: 서블릿 실행 구조 및 라이프사이클]
  1. 사용자 URL요청에 따른 서블릿 실행
  2. 이때 서블릿 인스턴스가 생성되지 않았다면 인스턴스 생성(new -> 생성자 호출)후 init() 메서드 호출
  3. 이미 객체가 생성되어 있는 경우라면 각 요청별로 WAS에서 스레드를 생성해 서블릿의 service() 메서드 호출
  4. 사용자 요청에 따라 doGet(), doPost() 등 메서드 호출
  5. 서블릿 컨테이너의 종료를 포함해 서블릿 변경등 기존 서블릿을 정리해야 할때 destroy() 메서드 호출

서블릿 초기화 : init() 메서드

클라이언트 요청이 들어오면 컨테이너는 해당 서블릿이 메모리에 있는지 확인한다. 해당 서블릿이 메모리에 없을 경우에는 서블릿을 메모리에 다시 적재해야 하는데, 이때 서블릿의 init() 메서드가 호출되며 각종 초기화 작업을 수행한다. 즉 init() 메서드는 처음 한 번만 실행되므로 해당 서블릿에 각각의 스레드에서 공통적으로 사용하기 위해 필요한 작업이 있다면 init() 메서드를 오버라이딩해서 구현한다.

만일 실행 중 서블릿이 변경될 경우에는 기존 서블릿은 종료(destroy) 되고 새로운 내용을 다시 적재하려고 init() 메서드가 호출된다.

요청/응답 : service() 메서드

init() 메서드는 최초에 한 번만 수행되고 이후 요청은 스레드로 실행되며, 각각 service()메서드를 통해 doGet( )이나 doPost( )로 분기된다. 이때 파라미터로 HttpServlet Request와 HttpServletResponse 클래스 타입인 request와 response 객체가 제공되는데, 사용자 요청 처리는 request로, 응답 처리는 response 객체로 처리한다.

서블릿 종료 : destroy() 메서드

컨테이너로부터 서블릿 종료 요청이 있을 때(대부분의 경우 컨테이너가 종료될 때지만 컨테이너는 실행된 상태에서 특정 서블릿을 로드/언로드하는 기능이 있다) destroy() 메서드를 호출한다. init() 메서드와 마찬가지로 한 번만 실행되며, 서블릿이 종료되면서 정리해야할 작업이 있을 때는 destroy() 메서드를 오버라이딩해서 구현하면 된다.



올바른 서블릿 활용

일반적으로 서블릿은 3장에서 배울 MVC 패턴에서 컨트롤러의 역할 구현을 위해 사용하게 된다. 클라이언트로 부터 전달되는 데이터(form 입력 데이터, url 파라미터등)를 처리하기 위한 다양한 방법과 효과적인 데이터 처리를 위한 객체 매핑 url redirection 이나 forwarding 개념에 대해서도 이해가 필요하고 자유롭게 구현할 수 있어야 한다.

또한 리스너와 필터와 같은 특수한 성격의 서블릿을 통해 관리 혹은 서비스 전반에 걸쳐 필요한 기능의 구현이 필요할 수 있으므로 이에대해서도 잘 알아두도록 한다.

다음은 올바른 서블릿 활용을 위해 필요한 것들이다.



실습-1: Hello World 서블릿

이클립스에서 간단한 서블릿을 생성하고 실행하는 과정을 실습합니다.

실습 코드랩


실습-2: 계산기 서블릿 만들기

HTML 폼과 연동되는 서블릿 개발 예제 입니다.

실습 코드랩