지난 포스트에서 서블릿이 뭔지 알아보았습니다. 이번 포스트에서는 서블릿 프로젝트를 생성하고 작성하는 방법에 대해서 알아보겠습니다.


Apache Tomcat Container

서블릿이 동작할 때는 요청을 웹 서버가 받아서 웹 애플리케이션 서버(WAS)에 위임을하고, WAS가 서블릿을 호출해서 요청에 대한 작업을 처리한다고 했습니다.

이때 WAS로 가장 많이 사용되는 것들 중 하나가 톰캣 컨테이너(이하 톰캣)입니다. 톰캣을 사용하기 위해서는 다운로드가 필요한데요. 아파치 톰캣 공식 페이지에서 다운로드 하실 수 있습니다.

왼쪽의 Download 메뉴를 보시면 Tomcat 10이 있는데, 이 Tomcat 10에서 32-bit/64bit Windows Service Installer를 다운받고 설치합니다.

기본 설정으로 진행하다가 아래와 같은 화면이 나타납니다.
여기서 HTTP 포트 번호는 8090 (또는 8080 써도 됨), 어드민 계정 유저 명과 비밀번호는 기억할만한 것으로 설정하고 넘겨줍니다.

그 다음 화면에서는 jdk의 위치를 경로를 지정해주고 next합니다.

그 다음엔 설치 경로인데요. 기본으로 사용해도 좋고, 원하는 위치로 바꿔도 좋습니다. 이 설정까지 마친 후 install을 하시면 톰캣이 설치가 됩니다.


서블릿 프로젝트 생성

Windows 11, JAVA 17, Tomcat 10, IntelliJ 환경에서 실습이 진행되었습니다.

기존에는 gradle init, spring initailzr 또는 empty project를 이용해서 프로젝트를 생성했었는데요. 이번엔 서블릿을 사용하기 위해서 조금 다른 방식으로 프로젝트를 생성해볼까 합니다.

New Project를 선택하시고 Jakarta EE를 선택합니다.

Jakarta EE는 기존 Java EE가 오라클에서 이클립스 재단으로 이관됨에 따라 바뀐 명칭입니다. Java EE

추가적으로 Java EE는 자바로 서버측 개발을 하기 위한 플랫폼입니다. 기존에서는 javax.라는 패키지명을 사용했으나 Jakarta EE로 명칭이 바뀐 후 패키지명도 jakarta.로 변경되었습니다.


이후 프로젝트명(Name), 프로젝트 위치(Location), Group는 기본값 그대로 두고 Template, Application server, Language, Build system은 사진과 동일하게 맞춰주세요. (Artifact는 프로젝트 명과 동일하게, JDK는 설치된 JDK 사용하시면 됩니다.)

이전에 톰캣을 설치했기 때문에 Application Server에 톰캣이 자동으로 설정되어있을 수도 있는데 추후에 수동 설정법을 알아보기 위해서 여기서는 No인 상태로 설정합니다.

Next를 누르면 Dependencies 설정창이 나오는데요. 여기서 Jakarta EE 10, Servlet(6.0.0)을 선택해주고 Create를 눌러서 프로젝트를 생성해줍니다.

그러면 프로젝트가 열리면서 3개의 기본 파일이 등장하게 됩니다.


프로젝트 구성

  • pom.xml: Maven 구성 정보가 포함된 Project Object Model입니다. 프로젝트에 필요한 종속성과 플러그인에 대한 정보도 포함하고 있습니다.

  • HelloServlet.java: HttpServlet을 상속받은 서블릿 자바 클래스입니다.

//HelloServlet.java
package com.example.helloservlet;

import java.io.*;

import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;

@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
    private String message;

    public void init() {
        message = "Hello World!";
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    	throws IOException {
        response.setContentType("text/html");

        // Hello
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }

    public void destroy() {
    }
}

/hello-servlet이라는 GET HTTP 요청을 받으면 Hello World!라는 문구를 웹 브라우저에 렌더링하는 코드입니다. 지난번에 본 생명주기 메소드인 init(), doGet(), destroy()의 모습도 보입니다.

  • index.jsp: root 디렉토리에 접근할 때 열리는 페이지로, 애플리케이션이 시작될 때 보여지는 페이지입니다. Hello World!라는 문구와 /hello-servlet으로 이동하기 위한 링크가 있습니다.

애플리케이션 서버 설정하기

이제 이 서블릿 프로젝트에 위에서 설치한 톰캣 컨테이너를 애플리케이션 서버로 설정해보겠습니다. File -> Setting -> Build, Execution, Deployment -> Application Servers에 들어간 후 +를 클릭해서 위에서 설치한 톰캣을 연결해줍니다. Apply를 누르면 애플리케이션 서버로 톰캣이 연결이 됩니다.

이제 생성한 애플리케이션 서버로 run을 할 수 있도록 run을 구성해주어야합니다.인텔리제이 상단을 보시면 Current File이라고 적힌 부분이 있습니다. 이곳을 클릭하면 Edit Configuration마찬가지로 +를 누릅니다. 구성 방법이 많은데 내리다 보면 Tomcat Sever가 있고 여기서 Local을 선택합니다. Remote같은 경우에는 원격 컴퓨터에서 접속하기 위한 구성입니다.Local을 선택하고나면 여러가지 기본설정을 해주는데요. 여기서 바로 Apply하지 마시고, 아래있는 FIX를 눌러주세요.그리고 프로젝트명:war exploded를 선택해줍니다. 다시 Server 탭으로 돌아오면 아래 사진처럼 URL이 설정된 것을 볼 수 있습니다. 잘 설정되었다면 Apply 및 Ok를 통해 설정을 마쳐주세요.

진짜로 잘 설정되었는지 확인해봐야겠죠?다시 상단 메뉴에서 Run을 누르거나 Shift + F10을 누릅니다. 그러면 서버 로그가 이렇게 착착착 뜨다가 서블릿 페이지가 하나 열리게 됩니다.전부 잘 작동한다면 서버 설정 및 run 설정까지 무사히 마친 것 입니다.

서버 종료시에는 Ctrl + F2를 두 번 입력하거나, 정지 표시, 해골 표시를 눌러서 종료해주세요.


서블릿 작성해보기

자 그러면 서블릿을 작성할 모든 준비를 마쳤으니 실제로 한 번 작성해보겠습니다. 프로젝트 파일에서 src\main\java\com\example\helloservlet 위치에 FirstServlet.class를 하나 생성해줍니다. (클래스 이름은 자유!) 이 클래스의 내용은 다음과 같이 작성해주세요.

package com.example.helloservlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class FirstServlet extends HttpServlet {
    @Override
    public void init() {
        System.out.println("init() method called");
    }

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        System.out.println("doGet() method called");
    }

    @Override
    public void destroy() {
        System.out.println("destroy() method called");
    }
}

위 코드처럼 사용자 정의 서블릿은 HttpServlet을 상속 받은 후 생명 주기 메소드들을 오버라이딩해서 사용하는 형태로 작성합니다.

서블릿 매핑

브라우저에서 서블릿의 이름으로 요청을 하기 위해서는 패키지명을 포함한 서블릿 클래스 이름을 전부 적어서 요청을 해야합니다. 그런데 우리가 작성한 서블릿의 패키지 명까지 더한 이름은 com.example.helloservlet.FirstServlet입니다. 이 이름을 URL에 넣어 요청하면 다음과 같은 긴 주소가 탄생합니다.

http://ip주소:포트번호/프로젝트명/com.example.helloservlet.FirstServlet

이렇게 URL을 사용한다면 입력하기도 힘들고 프로젝트명, 패키지, 클래스 이름이 URL에 그대로 노출이 되기 때문에 보안성이 좋다고 할 수도 없습니다. 따라서 현재는 서블릿 매핑이라는 기능을 이용해서 별명을 붙인 후 해당 별명을 통해 요청을 하게 됩니다. URL도 다음과 같이 간단해지죠.

http://ip주소:포트번호/서블릿_매핑_이름

서블릿 매핑을 수행하는 방법에는 .xml을 이용하는 방식과 Annotaion을 이용하는 방식 두 가지 방식이 있습니다.

.xml을 이용한 서블릿 매핑

서블릿 매핑이 무엇이고 왜 사용하는지 이해하셨다면 서블릿 매핑을 해보겠습니다. 서블릿 매핑은 src\main\webapp\WEB-INF\web.xml에서 설정합니다.

<?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_6_0.xsd"
         version="6.0">
  <!-- 여기에 매핑 내용 작성 -->
</web-app>

web.xml<web-app>태그 내부에 서블릿 매핑을 작성하게 되는데요. 먼저 매핑을 작성한 뒤 설명을 드리려고 합니다. 다음 코드대로 작성해주세요.

<?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_6_0.xsd"
         version="6.0">
  
    <servlet>
        <servlet-name>firstmapping</servlet-name>
        <servlet-class>com.example.helloservlet.FirstServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>firstmapping</servlet-name>
        <url-pattern>/first</url-pattern>
    </servlet-mapping>
</web-app>
  • <servlet>: 요청하는 매핑 이름에 대해서 실제로 실행될 서블릿 클래스를 설정합니다.

  • <servlet-name>: 서블릿 이름을 실제 서블릿 클래스에 연결합니다. 일종의 식별자와 같기 때문에 매핑하려는 <servlet-name>과 반드시 동일하게 name을 붙여주어야합니다.

  • <servlet-mapping>: 매핑 이름으로 요청하는 경우에 <servlet>태그에서 <servlet-name>이 동일한 태그와 연결됩니다.

  • <url-pattern>: URL을 통해 요청하는 매핑 이름을 지정합니다. 프로젝트 명 뒤에 오는 패키지명-클래스이름을 대체하는 이름입니다. 반드시 /(슬래시)로 시작해야합니다.

이렇게 설정하고 run으로 톰캣 서버를 실행한 뒤 http://localhost:8090/HelloServlet_war_exploded/first로 접속합니다. 그러면 빈화면이 뜨는데요. 당연히 화면에 렌더될 내용을 프로그래밍하지 않았으므로 빈 화면이 뜰 것이고, 서버 로그를 확인해야합니다. 우리가 작성했던 서블릿 코드대로 생명 주기에 따라 init()과 요청 과정에서 doGet()이 호출되었음을 볼 수 있습니다. (destory()는 소멸 과정이 없어서 호출되지는 않았습니다.)

추가적으로 다수의 서블릿을 매핑할 때는 <servlet>, <servlet-mapping>을 각각 분리해서 작성합니다.

다른 창을 하나 더 띄워서 http://localhost:8090/HelloServlet_war_exploded/first로 접속하면 이전과는 다르게 init()없이 doGet()만이 호출됩니다.이는 서블릿이 동작할 때 처음 한 번 톰캣 서버의 메모리에 있는 지 확인하고 메모리에 해당 서블릿이 없다면 init()을 수행합니다. 그래서 두 번 째 접속하는 경우에 톰캣 메모리에 이미 서블릿이 등록되어있기 때문에 init()호출 없이 바로 doGet()이 호출된 것 입니다.

만약 첫 번째 수행 후 destroy()를 호출해서 메모리에서 제거를 했다면, 다시 init()이 호출되었을 것 입니다.

이 결과로 인해 우리는 서블릿이 메모리에 등록된 서블릿을 재사용해서 효율 좋은 동작을 함을 알 수 있습니다.


Annotation으로 서블릿 매핑

Maven이 입지를 잃어가는 이유 중 하나는 xml의 작성 방법이 복잡하고 가독성이 떨어지기 때문입니다. 마찬가지로 xml을 이용한 매핑 방법은 작성법이 까다롭기도 하면서 여러 서블릿을 매핑하는 경우 가독성이 떨어진다는 문제가 발생하기도 합니다.

그래서 Tomcat 7 이후로는 어노테이션을 사용해서 매핑을 할 수 있도록 지원하고 있습니다.

이전에 했던 xml의 서블릿 매핑은 삭제하거나 주석 처리 <!-- -->를 해주세요.

서블릿 매핑을 하고자하는 클래스 위에 @WebSevlet("/매핑이름") 어노테이션을 붙이기만 하면 끝입니다. 정말 간단하죠?

package com.example.helloservlet;

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

import java.io.IOException;

@WebServlet("/first")	//어노테이션 서블릿 매핑
public class FirstServlet extends HttpServlet {
    @Override
    public void init() {
        System.out.println("init() method called");
    }

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        System.out.println("doGet() method called");
    }

    @Override
    public void destroy() {
        System.out.println("destroy() method called");
    }
}

마찬가지로 톰캣 서버를 실행하고 http://localhost:8090/HelloServlet_war_exploded/first로 접속하면 xml 서블릿 매핑과 동일한 결과를 얻을 수 있습니다.

추가적으로, 서블릿 매핑에 사용된 이름은 중복해서 사용할 수 없음에 주의해주세요.

서블릿 포스팅이 계속 된다면, 앞으로는 가독성도 좋고 작성하기 편한 어노테이션을 이용해서 서블릿 매핑을 할 예정입니다.

0개의 댓글