JSP와 함께 서버 측에서 사용되는 기술이 "서블릿"이다. 이번에는 서블릿의 기본에 대해 설명한다.


서블릿과 JSP의 차이

서버 사이드 Java의 기본이라 할 수 있는 기술은 JSP 외에 또 하나가 있다. 그것은 "서블릿(Servlet)"이다. 이 두개는 도대체 어떻게 다른가?

대답은 "같다" 이다. 즉, JSP와 서블릿은 같은 것이다. 더 정확하게 말하자면, "JSP라는 서버 사이드 자체 스크립트 언어"라는 것은 존재하지 않는다. JSP는 사실 서블릿이기 때문이다.

좀 정리해보자. 서버 사이드 Java 프로그램은 일반적인 응용 프로그램과는 많이 다르다. 응용 프로그램은 그 프로그램을 기동시켜 실행하지만, 서버 사이드 Java 프로그램은 그렇지 않는다.

서버 사이드에는 Java 서버라는 것이 있고, 그 속에서 움직이는 프로그램을 개발하는 구조로 되어 있다. 예를 들면 Web 브라우저에서 "애플릿(Applet)"이라는 작은 프로그램을 포함하여 움직이는 것과 같다고 생각하면 된다. 애플릿은 응용 프로그램이 아니다. 미리 작은 프로그램을 실행하는 틀이 있고, 그 틀에 맞추어 만들어진 프로그램을 Web 페이지에 끼워 넣으면 자동으로 인식되어 움직인다.

서버 사이드도 마찬가지이다. Java 서버에는 그 안에서 프로그램을 작동시키기 위한 구조가 준비되어 있다. 그리고 그 구조에 따라 프로그램을 만들고 포함해 두었다. 사용자가 그 프로그램에 할당된 URL에 액세스하면 Java 서버는 그 프로그램을 실행하도록 되어 있다.

이 "Java 서버에서 움직이는 작은 프로그램"이 서블릿이다. "Web 페이지와 애플릿"를 그대로 "Java 서버와 서블릿"로 대체 생각해 보자.

그럼 JSP란 무엇인가? 서블릿은 결국에는 Java 프로그램이기 때문에, 만드는 것도 좀 힘들다. 모두 Java로 코딩해야 한다. 단지 내부의 처리뿐만 아니라 클라이언트(즉, Web 브라우저)에 표시되는 HTML 코드도 모두 Java 코드로 쓰지 않으면 안된다. HTML을 모두 println에서 만드는 상상해 보자. 대부분 어려운 작업이라고 생각이 될 것이다.

그래서 "더 간편하게 서버 사이드 Java를 사용할 수 있도록" 하는 것으로 생각된 것이 JSP이다. Java 서버가 수행하고 있으며, "간단한 태그를 사용하여 작성된 Java 코드를 바로 실행하는 것"은 사실 없다.

Java 서버는 JSP 코드를 읽어 들여, 그것을 서블릿 소스 코드로 변환한다. HTML 태그 등도, 모든 println으로 쓰여지도록 변환되는 것이다. 그렇게 생성된 서블릿 소스 코드를 컴파일하고, 서블릿을 생성하여 그것을 호출한다. 즉, "JSP가 서블릿"이 되는 것이다.


같다고는 하지만, 접하는 사용자에게는 상당히 차이가 있다. JSP는 HTML 안에 처리를 포함할 수 있기 때문에, HTML 페이지에 뭔가를 추가하는 경우에는 매우 편리하다. 반대로 서블릿은 HTML으로 자세하게 출력 등을 하지 않아도 되는 곳에서 활용하면 쓸모가 있다. 즉, 프런트 엔드(사용자에게 표시되는 측면)은 JSP으로 하고, 백엔드(서버에서 움직이는 보이지 않는 측면)은 서블릿으로 하는 방식으로 양쪽을 잘 구분하여 사용하면 좋다.

서블릿 버전

서블릿의 기본적으로 만드는 방법의 설명에 들어가기 전에 하나를 알아두어야 하는 것이 있다. 그것은 "서블릿 버전"이다.

서블릿도 서버 사이드 Java의 API로써 제공되고 있다. 물론 계속 향상되고 있으며, 때때로 버전 업하면 다양한 기능을 사용할 수 있게 된다. 문제는 "최신 버전에서 서블릿을 만들어도, 사용하는 서버(즉, WAS)가 지원하지 않으면 사용할 수 없다"는 점이다.

현재 서블릿을 이용하려고 하면 버전은 명확하게 두 가지로 나뉜다고 해도 좋을 것이다. 그것은 "3.0"이전과 이후이다. 서블릿 3.0에서 상당히 큰 개선이 이루어 졌으며, 만드는 방법도 많이 바뀌었다(기본 코드는 변함이 없지만). 3.0 이전에는 서블릿 코드 외에 설정 파일 등을 작성하지 않으면 안되지만, 3.0에서 서블릿 코드만 작성하면 다른 어떤 것도 필요 없게 되었다.

여기서 현재 서버, 서블릿, JSP 정보를 볼 수 있는 예제를 만들어 보도록 하자.

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Info jsp</title>
<style>
h1 {
    font-size: 16pt;
    background: #AAFFAA;
    padding: 5px;
}
</style>
</head>
<body>
    <h1>Sample jsp page</h1>
    <p>이 페이지는 샘플입니다.</p>
    <p>서버 정보 :<%=application.getServerInfo()%></p>
    <p>서블릿 정보 : <%=application.getMajorVersion()%>.<%=application.getMinorVersion()%></p>
    <p>JSP 정보 : <%=JspFactory.getDefaultFactory().getEngineInfo().getSpecificationVersion()%></p>
</body>
</html>

위에서 application에서 서버 및 서블릿 정보를 얻어와서 표시를 하고, JspFactory 객체에서 스팩에 대한 버전 정보를 표시하고 있다.

GAE에서 지원하는 서블릿 버전

GAE는 사실 아직 3.0을 지원하지 않는다. 그 이전의 "2.5"이라는 버전이 있다. 따라서 최신 버전에 비해 여러가지 귀찮다. 이 입문은 GAE를 사용하지만, 기본적으로 "JSP/서블릿 입문"이다. 따라서 3.0에 대해서도 언급하고자 하지만, GAE에서는 동작하지 않는다. 그래서, "2.5 기반으로 만들어 가지만, 일단 3.0에 대해도 설명을 해 둘것 이기에, 만약 GAE가 지원되게 되면 스스로 고쳐서 해보길 바란다"라는 접근법으로 가고자 한다.

서블릿 버전표

Servlet SpecJSP Spec웹소켓 스펙톰캣 버전지원 Java 버전
4.02.31.19.0.x8
3.12.31.18.0.x7
3.02.21.17.0.x6
2.52.1N/A6.0.x5
2.42.0N/A5.5.x1.4
2.31.2N/A4.1.x1.3
2.21.1N/A3.3.x1.1



서블릿의 기본 코드

그럼 서블릿의 기본을 살펴 보겠다. 서블릿이라는 것은 기본적으로 "Java 클래스"이다. 뭐, 당연하다고 한다면 당연하겠지만, "Java 클래스를 쓸 수 있어서 좋을"뿐이다. 뭔가 특별한 것이 아니다.

그럼 서블릿 클래스는 어떻게 작성되는지 보도록 하겠다. 기본 코드는 아래와 같다.

import javax.servlet.http.*;
 
@SuppressWarnings("serial")
public class 클래스명 extends HttpServlet {
     
    public void doGet(HttpServletRequest request, 
            HttpServletResponse response)
            throws IOException {
 
        ……여기에 GET 처리를 쓴다……
 
    }
 
    public void doPost(HttpServletRequest request, 
            HttpServletResponse response)
            throws IOException {
 
        ……여기에 POST 처리를 쓴다……
 
    }
}

이것이 서블릿의 기본 코드의 구조이다. 그럼 포인트를 정리하겠다.

1. 클래스는 HttpServlet을 상속받는다.

서블릿의 기본이 되는 것은 javax.servlet.http 패키지에 준비되어 있는 HttpServlet 클래스이다. 서블릿은 이 클래스를 상속하여 만든다.

2. 기본은 'doGet'과 'doPost' 메소드

클래스에 거의 필수 항목으로 제공되는 것은 'doGet'과 'doPost'메소드이다. 이들은 각각 HTTP 메소드인 GET/POST에 액세스할 때 실행된다. 이 중에 하나(또는 양쪽)가 반드시 준비될 것이다. 어느 메소드도 IOException이 발생할 수 있으므로 throws IOException해 둔다.

3. HttpServletRequest에서 요청 정보를 관리

이러한 메소드는 두 가지 중요한 객체가 인수로 전달된다. 하나는 'HttpServletRequest'이다. 이것은 요청 정보(클라이언트가 서블릿에 액세스하여 왔을 때 정보)를 관리하는 객체이다. 요청에 대한 다양한 정보는 이 객체의 메소드를 호출하여 얻을 수 있다.

4. HttpServletResponse에서 응답을 관리

또 다른 객체가 "HttpServletResponse"이다. 이것은 응답 정보(서블릿에서 클라이언트에 반환하는 정보)를 관리하는 객체이다. 클라이언트에 출력 등도 이 HttpServletResponse에서 PrintWriter를 꺼내어 써서 내보낸다.

Servlet 3.0의 어노테이션

이 밖에 Servlet 3.0을 사용하는 경우에는 이 서블릿의 공개 주소에 대해 어노테이션으로 지정할 수 있다. 이 클래스의 선언 부분(@SuppressWarnings이 있는 곳)에 작성한다.

@WebServlet(공개 주소)

이런 느낌이다. 예를 들어 http://OO/sample와 같은 주소로 공개한다면 @WebServlet("/sample")라고 작성해 두면 된다. 이것으로 서블릿은 지정한 주소로 자동으로 공개된다.

(2.5의 경우는 어노테이션은 사용할 수 없다. 별도의 설정 파일을 지정해야 한다. 이에 대해서는 계속 설명하겠다.)

web.xml에 서블릿 정보를 등록하기

Servlet 3.0을 사용하고 있다면, 이것으로 서블릿 작성은 끝이다. 이 후에는 배포할 뿐이다. 하지만, GAE처럼 아직 3.0을 지원하지 않는 환경에서는 서블릿 본체 소스 코드 외에 "서블릿 정보를 기술한 설정 파일"을 준비하지 않으면 안된다.

그것은 "web.xml"라는 파일이다. 일반적으로 Web 어플리케이션의 "WEB-INF" 폴더(외부에서 액세스되지 않는 특수 폴더이다)에서 준비한다. GAE 프로젝트는 기본적으로 자동으로 생성되어 있을 것이다.

이 web.xml에는 Web 응용 프로그램에 대한 다양한 설정 정보가 포함된다. 서블릿은 "서블릿의 이름", "사용하는 클래스", "공개 주소"와 같은 정보를 여기에 기술해야 한다.

아래에는 서블릿 관련 정보의 기본형을 정리해 두었다.

web.xml의 기본형

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

   <servlet>
       <servlet-name>서블릿 이름</servlet-name>
       <servlet-class>서블릿 클래스</servlet-class>
   </servlet>

   <servlet-mapping>
       <servlet-name>서블릿 이름</servlet-name>
       <url-pattern>공개하는 주소</url-pattern>
   </servlet-mapping>

   ……그 외에 태그 계속……

</web-app>

web.xml에 <web-app>라는 루트 태그 안에 설정 태그를 작성한다. 서블릿 정보는 다음의 2개의 태그로 제공된다.

<servlet> 태그

서블릿의 등록을 위한 것이다. 이 중에는 다음과 같은 두 개의 태그가 준비되어 있다. 서블릿 클래스는 단순히 클래스 이름뿐 아니라 패키지도 포함하여 작성한다.

  • <servlet-name> 서블릿의 이름을 등록한다.
  • <servlet-class> 사용하는 서블릿 클래스를 작성한다.

<servlet-mapping> 태그

서블릿 URL 매핑(어떤 주소로 공개하는지)을 설정하기 위한 것이다. 이 안에 이하 두개의 태그가 준비되어 있다. 이것으로 서블릿을 지정된 주소로 게시할 수 있다.

  • <sservlet-name> 서블릿 이름을 지정한다.
  • <surl-pattern> 공개하는 주소를 설명한다.

이 태그에 의해 생성된 서블릿 클래스가 지정된 주소로 공개된다. 몇번이나 설명을 했지만, 이는 "Servlet 3.0 이전"의 경우에 필요하다. 3.0 이상은 어노테이션으로 지정하면 필요없는 설정이다.

덧붙여서, web.xml에는 이 밖에 <welcome-file-list>라는 태그도 있다. 이것은 URL 경로(파일 이름)를 지정하지 않고 액세스했을 때, 디렉토리 안에 어떤 파일을 표시할지를 지정한다. 이것은 없어도 별도로 Web 응용 프로그램에 영향을 주지 않는다.



간단한 서블릿 작성

그러면 실제로 극히 초보적인 서블릿을 만들어 보자. 아래에 서블릿 샘플 코드 및 web.xml 파일의 등록 예제가 있다.

MyGaeAppServlet.java

package com.devkuma.mygaeapp;

import java.io.*;

import javax.servlet.http.*;

@SuppressWarnings("serial")
public class MyGaeAppServlet extends HttpServlet {
    
   public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
       response.setContentType("text/html");
       request.setCharacterEncoding("utf8");
       response.setCharacterEncoding("utf8");
       PrintWriter out = response.getWriter();
       out.println("<html><head></head><body>");
       out.println("<h1>Hello, world</h1><p>this is sample servlet.</p>");
       out.println("</body></html>");
   }

   public void doPost(HttpServletRequest request, HttpServletResponse response)
           throws IOException {
       // not use.
   }
}

web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
   <servlet>
       <servlet-name>MyGaeApp</servlet-name>
       <servlet-class>com.devkuma.mygaeapp.MyGaeAppServlet</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>MyGaeApp</servlet-name>
       <url-pattern>/mygaeapp</url-pattern>
   </servlet-mapping>
   <welcome-file-list>
       <welcome-file>index.html</welcome-file>
   </welcome-file-list>
</web-app>

GAE 프로젝트를 만들 때 기본적으로 MyGaeAppServlet라는 클래스의 소스 코드 파일이 생성되어 있을 것이다 (프로젝트 "src"폴더에 있다). 이것을 그대로 사용한다.

만약 새로운 서블릿을 만들고 싶다면, [File]-[New]-[Class] 메뉴를 선택하고 프로젝트의 "src"폴더에 새로운 클래스의 소스 코드 파일을 작성한다.

여기에서는 MyGaeAppServlet 클래스의 doGet 메소드에 간단한 출력의 샘플 코드가 작성되어 있다. doPost는 메소드만으로 내용은 없다. 완성되면 GAE에 배치하고 다음과 같이 주소를 지정하여 방문해 본다.

http://{응용 프로그램 이름}.appspot.com/mygaeapp

간단한 텍스트를 표시하는 것이지만, 일단 제대로 표시되면 OK이다. 아주 간단하지만, 서블릿 코드와 설정을 작성하여 배포하기까지의 기본은 이것으로 알 수 있었다.

기억해 두어야 하는 서블릿의 기본 처리

이 doGet는 서블릿에서 반드시라고 해도 될 만큼 사용되는 처리만으로 구성되어 있다. 이들은 "서블릿의 기본 처리"로 먼저 기억해야 한다. 다음에 순서대로 정리를 해 보겠다.

콘텐츠 형식의 설정

response.setContentType(유형);

클라이언트 측에 어떠 결과를 표시할 경우, 그 내용이 어떤 유형의 콘텐츠인지를 설정해 둘 필요가 있다 (그렇지 않으면 브라우저가 해당 콘텐츠를 표시할 수 있을지 없을지 알 수 없게 되기 때문에). 이것을 수행하는 것이 HttpServletResponse의 "setContentType"이다. 이것은 인수에 콘텐츠 형식을 나타내는 텍스트를 지정한다. 우선, 다음 두가지를 명심하자.

  • HTML의 경우 : "text/html"
  • 일반 텍스트의 경우 : "text/plain"

인코딩 설정

request.setCharacterEncoding(인코딩 이름);
response.setCharacterEncoding(인코딩 이름);

클라이언트와 정보를 주고 받는 경우, 기본은 '텍스트'이다. 그렇게 되면, 그 텍스트가 어떤 인코딩인지 모르면 글자가 깨질 수 있다. 그 설정을 수행하고 있는 것이, 이 두 문장이다.

"setCharacterEncoding"는 인수에 지정된 인코딩 설정을 하는 것이다. 이는 HttpServletRequest와 HttpServletResponse 양쪽 모두 제공되어 있다. HttpServletRequest는 브라우저 등에서 보내온 텍스트 인코딩이고, HttpServletResponse은 클라이언트에 텍스트를 보낼 때 인코딩이다. GAE의 경우 UTF-8이 기본이기 때문에, 모두 "utf8"로 해두면 된다.

PrintWriter 얻기

PrintWriter out = response.getWriter();

클라이언트에 출력(즉, 브라우저에 뭔가를 보내는 것)은 "PrintWriter"라는 클래스를 이용한다. 이것은 HttpServletResponse의 "getWriter"메서드를 호출하여 인스턴스 얻어올 수 있다. 이렇게 꺼낸 PrintWriter의 메소드를 호출하여 출력을 한다.

클라이언트에 출력

out.println(출력하는 값);

PrintWriter으로 값의 출력에 이용되는 것이 "println" 메소드이다. 이것은 인수에 지정된 값을 클라이언트에 보낸다. 오버로드되어 있어서 인수에 어떤 값으로도 지정할 수 있다.

여기에서는 HTML 소스 코드를 작성하고 있다. 어떤 HTML 페이지를 다시 보낼 경우, 이와 같이 HTML 태그를 println에 작성하면 된다.

그래서, 우선 간단한 서블릿을 만드는 방법은 이제 알았다. 이번 기본 부분은 서블릿 작성에 반드시 필요한 지식이다. 다음에 좀 더 구체적인 서블릿 작성을 할 것이기에 이것까지는 꼭 기억해 두도록 하자.



  1. 대학생 2019.10.18 14:56

    질문 있습니다... 서블릿과 JSP가 같다고 하셨는데 제가 배운 개념은 JSP HTML처럼 일반적인 정적 웹페이지이고 MVC로 따지면 View부분을 처리하고 서블릿이 자바의 핵심 비즈니스 로직을 처리하는 Controller 부분 아닌가요? 컴파일 되면서 서블릿 코드로 변환된다는 측면에서 같다라고 보신 것 같은데? 그렇다 하더라도 기능상의 차이?와 목적이 다르다 라고 말할 수 있지 않을까요?

    • 대학생 2019.10.18 15:30

      추가 의견 및 질문 있습니다. jsp는 스크립트 언어(인터프리터 방식)이고 서블릿은 컴파일 된 것 이기 때문에 다르다고 봅니다.

      그러면 서버에서는 jsp페이지가 아닌 컴파일된 jsp 서블릿과 그걸 다시 컴파일한 클래스 들을 웹상에서 보여주는게 맞나요?

    • 서블릿에서는 로직을 처리할 수 있고, 페이지를 만들어서 보낼 수도 있습니다. 서블릿 자체를 로직으로 쓰던 페이지로 쓰던 개발자 마음입니다. 즉, MVC 측면에서 서블릿이 Model이 될 수도 있고, View가 될 수도 있다는 것을 뜻합니다.

      JSP와 서블릿이 기능상의 차이와 목적은 개발자가 만들어 가는 겁니다. 여기서는 코드에 대한 설명만 하기에 개발자의 의견보다는 프로세스적인 측면에서 JSP와 서블릿이 동일하게 동작한다는 점에서 같다는 표현이 썼습니다. 이 표현이 잘못됐다고 할 수 없을거 같습니다.

      그리고 질문자님이 언급하신 비즈니스 로직을 처리하는 부분은 Controller이 아니라 Model입니다.

      또, 인터프리터란, 컴파일 없이 동작하는 언어를 말하는데 JSP는 서블릿 컴파일이 되고 Class 컴파일이 되기에 인터프리터 방식이 아니라 컴파일 방식의 언어입니다. 엄밀히 말하면 반컴파일 방식이라고도 합니다.

      이 게시물에서 설명을 하고 있듯이 JSP자체가 프로세스 상에 올라가는 것아 아니라 JSP가 서블릿이 되고 그 서블릿이 클래스가 생성되며, 그 생성된 클래스가 메모리에 올라가 인스턴스화가 되어 요청(request)에 대한 응답(response)를 해주는 것입니다. 참고로 이 모든 작업은 WAS에서 해줍니다.

+ Recent posts