본문 바로가기
Web/Backend

저는 서블릿이 처음이라니까요?

by 조엘 2021. 6. 22.

안녕하세요! 조엘입니다!

 

"처음이라니까요" 시리즈 여덟 번째 토픽은 서블릿입니다. 🎯🎯

자바로 웹 개발을 하다 보면 서블릿이라는 말을 많이 듣게 되는데요! 

스프링에서는 "디스패처 서블릿을 통해 프론트 컨트롤러 패턴을 구현했다" 같은 어려운 표현도 접해요. 

이번 기회에 서블릿이 무엇인지! 스프링을 공부하면서 왜 서블릿을 알아야 하는지 알아봅시다 🤞🤞

 

 

*** WAS란? ***

서블릿을 알아보기 전에 WAS(Web Application Server)에 대해 먼저 알아보아요. 

기존에는 웹이 정적 자원들만 반환했어요. 하지만 점점 웹에서 처리할 수 있는 영역이 넓어지면서, 요청에 따라 다른 자원을 반환하는 동적 웹에 대한 수요가 커져갔어요.

정적 자원들을 반환하고 간단한 동적 자원을 생성하는데 특화된 웹 서버로는 점점 한계가 드러났죠. 

그에 따라 DB에 접근하고, 다양한 로직 처리를 통해 동적 자원을 만드는 WAS가 필요해졌어요. 

 

 

자바 진영에서 대표적인 WAS Tomcat이에요. Tomcat 서블릿을 구동하는 서블릿 컨테이너 역할을 하는데요. 

이제 서블릿이 뭔지, 서블릿 컨테이너는 또 뭔지, 이들이 어떻게 동적 컨텐츠를 만들어내는지 한 번 알아봅시다. 

 

 

*** 서블릿이란? ***

위키피디아에서 정의하는 서블릿은 다음과 같아요. 

 

자바 서블릿(Java Servlet)은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말하며,

흔히 "서블릿"이라 불린다. 자바 서블릿은 웹 서버의 성능을 향상하기 위해 사용되는 자바 클래스의 일종이다.

 

즉, 서블릿은 서버 측에서 돌아가는 "웹 요청에 대해 동적인 처리가 가능한" 자바 프로그램이란 것이에요. 

 

서블릿이 어떻게 생겼는지 궁금해 지는데요. Servlet 인터페이스는 다음과 같이 정의되어 있어요. 

init()으로 서블릿 생성, service()로 필요한 처리 진행, destroy()로 서블릿 삭제를 하리라 예상이 되네요. 

package jakarta.servlet;

public interface Servlet {
    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();

    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    public void destroy();
}

 

이제 Servlet 인터페이스를 구현한 GenericServlet을 상속 받은 HttpServlet을 살펴보아요. 

주석에 HttpServlet을 어떻게 사용해야 하는지 상세한 설명이 나와있어 이를 그대로 가져왔어요. 

package jakarta.servlet.http;

/**
 * Provides an abstract class to be subclassed to create
 * an HTTP servlet suitable for a Web site. A subclass of
 * <code>HttpServlet</code> must override at least
 * one method, usually one of these:
 *
 * <ul>
 * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
 * <li> <code>doPost</code>, for HTTP POST requests
 * <li> <code>doPut</code>, for HTTP PUT requests
 * <li> <code>doDelete</code>, for HTTP DELETE requests
 * <li> <code>init</code> and <code>destroy</code>,
 * to manage resources that are held for the life of the servlet
 * <li> <code>getServletInfo</code>, which the servlet uses to
 * provide information about itself
 * </ul>
 *
 * <p>There's almost no reason to override the <code>service</code>
 * method. <code>service</code> handles standard HTTP
 * requests by dispatching them to the handler methods
 * for each HTTP request type (the <code>do</code><i>Method</i>
 * methods listed above).
 *
 */
public abstract class HttpServlet extends GenericServlet {
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String msg = lStrings.getString("http.method_get_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String msg = lStrings.getString("http.method_post_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String msg = lStrings.getString("http.method_put_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
    protected void doDelete(HttpServletRequest req,
                            HttpServletResponse resp)
        throws ServletException, IOException {

        String msg = lStrings.getString("http.method_delete_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
}

 

HttpServlet은 웹 사이트에 적합한 서블릿을 구현하기 위한 추상클래스에요. 

HttpServlet을 상속한 클래스는 다음과 같은 메서드들 중 필요한 메서드를 오버라이딩 해야해요. 

  - doGet() : HTTP GET 요청에 대한 처리

  - doPost() : HTTP POST 요청에 대한 처리

  - doPut() : HTTP PUT 요청에 대한 처리

  - doDelete() : HTTP DELETE 요청에 대한 처리

  - init() : 서블릿 생성

  - destroy() : 서블릿 파괴

 

위의 Servlet 인터페이스에서 봤던 service() 메서드를 따로 오버라이딩 해줄 필요는 없어요. 

HttpServlet service() 메서드가 HTTP 요청을 보고, doMethod() 꼴의 메서드를 실행해주어요. 

 

그럼 이제 HttpServlet을 상속한 HelloServlet을 정의해보도록 할게요. 

아래 실습에서 해당 코드를 사용하니 한 번 봐두세요! 👀👀

public class HelloServlet extends HttpServlet {
    public void init() {
        System.out.println("init...");
    }

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {
        System.out.println("doGet here");
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        out.println("<html><body>Hello Servlet!</body></html>");
    }

    public void destroy() {
        System.out.println("destroy!");
    }
}

 

 

*** 서블릿 컨테이너 ***

위에서 알아본 대로 개발자들은 "동적인 처리가 가능한 웹"을 서블릿으로 정의할 수 있어요.

이렇게 개발자들이 작성한 서블릿들은 서블릿 컨테이너에서 관리되어요. 

서블릿 컨테이너는 클라이언트의 요청을 받고, 응답할 수 있도록 웹 서버와 소켓으로 통신해요. 

또한 서블릿 컨테이너는 서블릿이 동작하는 환경을 제공해주죠.

위에서 잠깐 언급했듯, 톰캣이 이런 서블릿 컨테이너의 대표주자에요. 

 

서블릿 컨테이너는 다음과 같은 일을 해요. 

1. 웹 서버와 통신

2. 서블릿 생명주기 관리

3. 멀티쓰레드 지원 및 관리 (Thread Pool)

4. 선언적인 보안 관리

 

해당 내용을 이미 잘 설명해주신 블로그가 있어서 설명은 참고의 링크로 대신할게요 :)

 

 

*** 서블릿 동작 방식 ***

지금까지 동적 웹을 만들어주는 서블릿도 정의해봤고, 서블릿을 관리해주는 서블릿 컨테이너의 역할도 알아봤어요.

이제 서블릿 컨테이너가 어떻게 클라이언트의 요청에 대응하는 서블릿을 찾고, 

어떻게 해당 서블릿service() 메서드를 호출시켜 필요한 처리를 진행하는지 동작 과정을 살펴보아요. 👀👀

 

클라이언트 요청에 대응되는 서블릿의 service() 메서드 호출은 다음과 같은 과정을 거쳐요. 

 

1. 웹 서버가 WAS에게 클라이언트가 보낸 요청을 건네줌

  1-1. HTTP request의 정보를 통해 HttpServletRequest 객체를 생성

  1-2. 클라이언트에게 보낼 응답을 위해 HttpServletResponse 객체를 생성

  1-3. 서블릿 컨테이너의 web.xml 설정에서 요청 url에 대응되는 서블릿을 선택

  1-4. 선택된 서블릿에 HttpServletRequest와 HttpServletResponse를 넘겨줌

 

2. 서블릿 컨테이너가 선택된 서블릿 객체를 메모리에 올림

  2-1. 서블릿 컨테이너가 선택된 서블릿 객체를 컴파일

  2-2. 서블릿 객체를 만들어 메모리에 올림

  2-3. 메모리에 올라가면서 서블릿의 init() 메서드 실행

 

3. 서블릿 컨테이너가 해당 서블릿 객체의 service() 메서드를 호출함

  3-1. 하나의 쓰레드에서 요청에 대응되는 서블릿의 service() 메서드를 호출함

  3-2. service() 메서드 내부에서 HttpServletResponse에 필요한 내용을 담아서 반환함

 

 

*** 서블릿 정의해보기 ***

배운 내용을 기반으로 직접 서블릿을 정의하고, 이를 서블릿 컨테이너인 톰캣에서 관리해 보아요. 🎯🎯

우분투 서버에서 톰캣을 설치하고 실습을 진행하는 과정은 TIL에 상세히 정리해뒀어요. 

- TIL 링크: https://github.com/joelonsw/woowacourse-TIL/blob/master/Level2/2021-06-18.md#tomcat%EC%97%90-servlet-%EB%9D%84%EC%9B%8C%EB%B3%B4%EA%B8%B0

이번 포스팅에서는 간략히 과정만 살펴보도록 해요. 

 

 

1. 톰캣을 설치하고 실행해본다. 

톰캣을 설치하고 실행한 뒤, EC2 인스턴스 주소의 8080 포트로 접속하면 다음과 같은 사이트를 만나요. 

해당 페이지가 뜨면 톰캣이 잘 실행되었다는 뜻이에요. 이제 톰캣을 끄고 서블릿을 만들어봅시다. 

톰캣은 기본적으로 8080 포트를 사용한다.

 

2. 서블릿을 만들어 "apache-tomcat-10.0.7/webapps/ROOT/WEB-INF/classes" 에 저장한다. 

위에서 만든 HelloServlet 기억하시죠? 

HelloServlet 소스 코드와 이를 컴파일한 클래스 파일을 톰캣의 정해진 위치에 저장해주어요. 

- 소스 코드: apache-tomcat-10.0.7/webapps/ROOT/WEB-INF/classes

- 클래스 파일: apache-tomcat-10.0.7/webapps/ROOT/WEB-INF/src

 

3. web.xml 에 HelloServlet에 대한 정보를 정의해준다. 

web.xml은 URL 경로와 해당 경로의 요청을 처리하는 서블릿 사이의 매핑을 정의해요. 

다음과 같이 정의해 보아요. 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">

  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>

  <servlet>
          <servlet-name>HelloServlet</servlet-name>
          <servlet-class>HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
          <servlet-name>HelloServlet</servlet-name>
          <url-pattern>/HelloServlet</url-pattern>
  </servlet-mapping>

</web-app>

 

4.  다시 톰캣을 실행하고, 지정한 URL에 접속해본다. 

다시 톰캣을 실행하고, 지정해준 URL로 접속하면 다음과 같은 사이트를 볼 수 있어요! 🎉🎉🎉

HelloServlet에서 정의해둔 내용이 나온 것을 볼 수 있다.

 

 

이렇게 서블릿 컨테이너를 통해 개발자가 정의한 서블릿을 띄우는 것 까지 공부해 봤어요. 

 

혹시 잘못된 내용이 있거나, 질문이 있으시면 댓글로 남겨주세요 😁

긴 글 읽어주셔서 감사합니다~ 😊

 

 

참고

- https://www.youtube.com/watch?v=Zimhvf2B7Es 

- https://mangkyu.tistory.com/14

- https://gmlwjd9405.github.io/2018/10/28/servlet.html

- https://velog.io/@kdhyo/Apache-Tomcat-%EB%91%98%EC%9D%B4-%EB%AC%B4%EC%8A%A8-%EC%B0%A8%EC%9D%B4%EC%A7%80

- https://github.com/joelonsw/woowacourse-TIL/blob/master/Level2/2021-06-18.md

- https://github.com/joelonsw/woowacourse-TIL/blob/master/Level2/2021-06-19.md

 

 

반응형

댓글