JSP

서블릿 클래스 정의하기

1장에서 이미 언급한 대로 서블릿 클래스를 정의하는 것은 정형화되어 있습니다. 새롭게 서블릿 클래스를 정의하기 위해서는 javax.servlet.http 패키지에서 제공하는 HttpServlet 클래스를 상속받아 구현해야 하고 브라우저를 통해 외부에서 실행되기 때문에 접근 제한자는 반드시 public 이어야 합니다.

 

public class HelloServlet extends HttpServlet{

 

}

=> 접근 제한자는 반드시 public 이어야 함.

우스운 질문일 수 있겠으나, 왜 서블릿은 위와 같이 정형화한 형태을 띄게 된 걸까요? 그냥 일반 자바 클래스 처럼 만들면 안 될 까요?

서블릿이 정형화 되어 있다는 말은 자바 클래스에서 사용하던 상속이란 개념을 그대로 사용하는데 자바 클래스에서는 어떤 클래스의 상속을 받아도 되지만 서블릿 클래스는 HttpServlet 클래스의 상속만을 받아야 하는 것으로 정해디져 있다는 것을 말합니다.

 

요청 메소드

1장에서 설명한 것처럼 요청 방식에 따라 doGet() 혹은 doPost() 가 호출되기 때문에 요청 방식에 맞추어서 HttpServlet 클래스의 doGet() 혹은 doPost()를 오버라이딩해야 합니다. 이 메소드는 요청이 되면 호출되기 때문에 요청 메소드라가 불리기도 합니다.

 

오버라이딩을 기억하시나요?

자바의 클래스들은 부모(수퍼) 클래스에 모든 필드나 메소드를 상속받아 사용합니다. 상속받아 사용하던 메소드의 기능을 더 이상 사용하지 않고 자식(서브) 클래스에서 새롭게 메소드를 정의해서 사용하는 것을 오버라이딩라고 합니다.

 

우리가 처음으로 작성할 서블릿은 브라우저의 주소란에 직접 서블릿 이름을 입력하여 수행시킬 것이기 때문에 HttpServlet 의 doGet() 을 오버라이딩하여 처리를 위한 코드를 입력하겠습니다.

 

public void doGet(HttpServletRequest request, HttpServletReponse response) throws IOException, ServetException

{

}

doGet() 은 두 개의 매개 변수(HttpServletRequest, HttpServletResponse) 를 갖습니다. HttpServletRequest 는 클라이언트의 요청(request) 을 처리하고, HttpServletResponse 는 요청 처리 결과를 클라이언트의 요청(request) 을 처리하고, HttpServletResponse 는 요청 처리 결과를 클라이언트에게 되돌리기 (응답하기, response) 위해 사용됩니다. doGet() 은 반드시 예외 처리 (IOException, Servlet Exception) 를 해주어야 하는데, 일반적으로 throws 절을 이용해서 doGet 메소드를 호출한 웹 서버에게 예외처리를 넘깁니다.

참고로 위의 서블릿은 get 방식으로만 요청을 처리하는데, 하나의 서블릿이라도 get과 post 방식에 따라 서로 다른 기능을 제공해야 하는 경우도 있기 때문에 그럴경우에는 doGet() 과 doPost() 를 모두 오버라이딩해야 합니다.

 

응답 객체에 콘텐트 타입 지정하기

HttpServletResponse 객체인 response 로 setContentType() 메소드를 호출하여 클라이언트에게 응답할 페이지에 대한 환경 설정을 결정해 주어야 합니다. 응답 방식이 "text/html" 로 지정되어 있으므로 text나 html 로 보여주겠다는 의미입니다.

 

서블릿의 실행 방식

일반적인 자바 클래스를 실행하기 위해서는 main 메소드가 있는 클래스에서 객체를 생성하여 실행해야 합니다. 하지만 서블릿은 이런 방식으로 실행되지 않고 웹 서버가 실행을 애주는 독특한 방식을 갖습니다.

서블릿은 Event-Driven Programming 으로 사용자의 요청이 들어오면 동작(실행)을 시작합니다. 요청이 들어오면 톰캣 서버가 서블릿 객체를 생성한 후에 init()  메소드를 호출한 후 요청 방식에 따라 doGet() 혹은 doPost() 메소드가 호출됩니다.

뒤에 자세하게 배우니 되니 지금은 이 정도만 알고 넘어갑시다.

 

결과로 출력할 내용이 한글일 경우 인코딩 방식을 지정하지 않으면 한글이 깨지는 현상이 나타납니다.

한글이 깨지지 않도록  하기 위해서는 인코딩을 UTF-8 로 지정해야 합니다.

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

 

출력 스트림 얻어오기

response.setContentType() 문장은 결과로 출력되는 HTML 문서의 인코딩 방식을 지정해 주는 문장입니다. 한글이 깨지지 않도록 하기 위해서 response.setContentType() 메소드에  캐릭터 셋을 UTF-8로 지정해 주어야 합니다. response.setContentType("text/html; charset=UTF-8") 문장에 의해서 response 객체의 인코딩 형태를 바꿔준 후에는 결과 출력을 위해 출력 스트림을 얻어오는 response.getWriter() 메소드를 호출해야 합니다. 순서가 바뀌면 인코딩 방식이 지정되지 않은 response 객체에서 출력 스트임을 얻게 되므로 반드시 response.setContentType("text/html; charset=UTF-8)문장이 먼저 실행된 후에 response.getWriter() 메소드로 출력 스트림 객체를 얻어 와야 합니다.

 

스트림이란

데이터는 메모리에 저장됩니다. 사용자가 입력한 데이터를 메모리에 저장하는 과정을 입력이라 하고 메모리에 저장된 데이터를 사용자가 볼 수 있도록 하는 과정을 출력이라고 하는데 입출력 과정이 가능하도록 하는 객체를 자바에서는 스트림이라고 합니다.

 

출력 스트림인 PrintWriter 객체를 HttpServletResponse 객체의 getWriter() 메소드로부터 얻어냅니다.

 

PrintWriter out =response.getWriter();

 

PrintWriter 는 파일 입출력을 위해서 제공해주는 자바 클래스로서 java.io 패키지에 정의되어 있습니다. 그래서 이 클래스를 사용하려면 java.io.PrintWriter 를 import 하고 사용해야 합니다. 이클립스에서는 Source ->Organize Imports 메뉴를 선택하거나 단축키인 Ctrl+Shift + O 를 누르면 자동으로 Import 됩니다.

클라이언트에게 HTML 문서 형태로 결과 출력하기 

PrintWirter 객체인 out 의 출력 메소드인 println() 을 통해 결과를 사용자에게 출력 할 수 있습니다. 사용자에게 전송된 내용은 브라우저를 통해 출력되기 때문에 결과를 HTML 태그 형태로 만들어서 보내야 합니다.

 

publi class HelloServlet extends HttpServlet{

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

    response.setContentType("text/html");

    PrintWriter out =response.getWriter();

    out.print("<html><body><h1>");

    out.print("Hello Servlet");

     out.print("</h1></body></html>");

     out.close();

}

}

서블릿의 동작 원리

서블릿의 동작 원리를 알기 위해서는 우선 서블릿 컨테이너에 대한 이해를 해야 합니다. 톰캣 사이트 Documentation에서는 아파치 톰캣을 서블릿/jsp 컨테이너어(이하 서블릿 컨테이너) 라고 규정하고 있습니다. 그리고 우리 책에서 사용하는 톰캣 7.0 버전은 서블릿 3.0 과 JSP2.2 를 지원하고 있는데요. 즉 톰캣이 웹 서버이면서 서블릿 컨테이너라는 얘기입니다. 톰캣의 내부구조까지는 자세히 알 필요는 없습니다. 하지만, 톰캣이 구동되면 자바가상머신(JVM) 이 구동되어 자바 문법을 따르는 서블릿을 처리할 수 있는 환경을 제공하여 서블릿 컨테이너라는 별칭이 붙여있다고 생각하면 될 것 같습니다.

 서블릿의 실행은 이렇게 서블릿 컨테이너에 의해 이루어집니다. 다음은 서블릿의 동작 원리를 나타낸 그림입니다. 자세한 사항은 뒤이어 서블릿 라이프사이클을 통해 알아보겠습니다.

1. 브라우저에서 서블릿을 요청합니다.

2. 그러면 WAS 안에 웹 서버가 서블릿 요청을 인식하여 서블릿 컨테이너에게 서블릿을 수행하도록 넘겨줍니다.

3. 서블릿은 스레드를 기동하여 해당 서블릿 객체를 생성하여 이를 수행합니다.

4. 서블릿 객체의 작업이 종료되면 기동되었던 스레드가 종료됩니다.

5. 서블릿 수행 결과가 웹 서버에 전송됩니다.

6. 이를 클라이언트에 전송하게 됩니다.

 

서블릿의 라이크 사이클

서블릿이 다른 웹 기술보다 주목을 받게 된 이유는 수행 속도가 빠르다는 점입니다. 수행 속도가 빠를 수 있는 이유는 두 번째 이상의 요청이 첫 번째 요청인 경우와 다르게 처리되기 때문입니다.

 다른 웹 기술들은 클라이언트들의 요청이 있을 때마다 작업을 처음부터 새롭게 하여 제공하지만, 서블릿은 그렇지 않습니다. 서블릿이 첫 번째 요청인 경우에는 서블릿 클래스를 찾아 메모리에 로딩하여 인스턴스(객체)를 생성합니다. 이때 생성된 서블릿 인스턴스는 메모리에 계속 남아 있게 되므로 이후부터는 서블릿이 호출되어도 서블릿 인스턴스가 다시 생성되지 않고 이미 메모리에 로딩된 서블릿으로부터 서비스만 받기 때문에 수행 속도가 빠릅니다. 반면에 PHP 나 ASP 는 요청될 때마다 인터프리터 방식으로 코드가 재해석되기 때문에 수행 속도가 느립니다.

위에 대한 설명을 서블릿의 라이프 사이클에 접목시켜 보다 자세히 살펴보겠습니다.

 

Instance 생성  -> init() ->doGet() 혹은 doPost()  ->destroy() 톰캣 해제시 자원 해제

 

우선 객체가 생성되면서 init() 메소드가 단 한번 호출됩니다. init() 메소드에서는 주로 초기화 작업을 합니다. 그 후에는 클라이언트이 요청이 있을 때 doGet() 혹은 doPost() 메소드가 실행됩니다. 여러 클라이언트가 동시에 요청을 하더라도 스레드가 생성되므로 동시에 doGet() 혹은 doPost() 메소드가 실행되기 때문에 수행 속도가 빠릅니다.

 서블릿이 더 이상 서비스를 하지 않을 경우 destroy() 메소드가 호출되는데, 예를 들면 서블릿 컨테이너가 종료(톰캣 재가동) 되거나 서블릿 내용이 변경되어 다시 컴파일해서 클래스 파일이 바뀌는 경우입니다.

 

package unit02;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class LifeCycle
 */
@WebServlet("/LifeCycle")
public class LifeCycle extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    int initCount =1;
    int doGetCount =1;
    int destroyCount =1;
    
    
    /**
     * @see HttpServlet#HttpServlet()
     */
    public LifeCycle() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init 메소드는 첫 요청만 호출됨  :" + initCount++);
    }

    /**
     * @see Servlet#destroy()
     */
    public void destroy() {
        System.out.println("destroy 메소드는 톰캣이 종료될 때만 호출됨 :" + destroyCount++);
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        System.out.println("doGet 메소드는 톰캣이 종료될 때만 호출됨 :" + destroyCount++);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}
 

init 메소드는 첫 요청만 호출됨  :1
doGet 메소드는 톰캣이 종료될 때만 호출됨 :1
doGet 메소드는 톰캣이 종료될 때만 호출됨 :2
doGet 메소드는 톰캣이 종료될 때만 호출됨 :3
doGet 메소드는 톰캣이 종료될 때만 호출됨 :4
doGet 메소드는 톰캣이 종료될 때만 호출됨 :5

 

서블릿의 한글처리와 데이터 통신

이번에는 HTML 의 <form> 태그를 사용하여 클라이언트의 브라우저 내에서 사용자로부터 데이터를 입력받는 방법과 서블릿에서 한글을 처리하는 방식을 알아보겠습니다.

 

서블릿에서 응답시 한글 처리

"Hello Servlet"을  출력했던 예제를 조금 바꾸어서 "헬로우 서블릿" 을 출력합니다.

[Project Explorer] 에서 새로 생성된 웹 프로젝트(web-study-02) 를 클릭하여 선택한 후 [New->Servlet] 을 선택합니다. [Create Servlet] 창이 나타나면 패키지 (unit03) 와 서블릿 클래스 이름(HelloServlet03) 을 입력한 후 [Finish] 버튼을 클릭 합니다.

 

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

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

@WebServlet("/HelloServlet03")
public class HelloServlet03 extends HttpServlet{
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
            // TODO Auto-generated method stub
        response.setContentType("text/html");
        PrintWriter out =response.getWriter();
        out.print("<html><body><h1>");
        out.print("Hello Servlet");
        out.print(" 헬로우 서블릿");
        out.print("</h1></body></html>");
        out.close();
    }
    
    
}

Hello Servlet ??? ???

 

 -> 실행 결과 한글이 깨져서 출력되는 것을 확인할 수 있습니다.

-> 브라우저에 응답하는 페이지에 대해서 한글을 출력할 경우 한글이 깨지지 않도록 하기 위해서는 HttpServletResponse 객체로 setContentType() 을 호출하여 응답 방식을 결정해 주면서 보여 주고자 하는 코드셋을  "UTF-8" 로 지정해야 합니다.

 

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

 

위와 같이 변경한 후에 다시 실행해보면 이제 제대로 한글이 출력되는 것을 확인할 수 있습니다. out.print() 를 두 번 호출해서 출력한 내용을 구분하기 위해서 띄우쓰기를 하려면 21:행에서 "헬로우 서블릿" 과 같이 출력할 내용 첫 문자 앞에 공백을 넣어야 합니다.

 

Hello Servlet 헬로우 서블릿

 

 

 

about author

PHRASE

Level 1  머나먼나라

댓글 ( 4)

댓글 남기기

작성