IT/Java

Java Servlet이란?

yeTi 2021. 8. 24. 17:22

안녕하세요. yeTi입니다.
오늘은 Spring MVC를 공부하기전에 서블릿을 공부하는 시간을 가져보고자 합니다.

서블릿 이전에는 CGI (Common Gateway Interface)를 사용했는데 요청이 있을 때마다 새로운 프로세스가 생성되어 응답처리가 무겁다는 단점이 있었습니다.

이를 Servlet이라는 클래스를 만듬으로써 자바 개발자들이 보다 쉽게 웹 어플리케이션을 만들 수 있도록 합니다.

이 문서는 Java™ Servlet Specification - Version 3.1을 참고하여 작성했습니다.

개념간의 관계를 그린 개념도 입니다.

Servlet                : 서블릿 구동
    Filter            : 요청 필터링
Servlet Context        : 서블릿간 정보공유
Servlet Container    : 서블릿 관리자
    ▲▲▲ 클라이언트 요청

Listener            : 이벤트 종류에 따른 생애주기 이벤트 처리

Servlet이란?

서블릿이란, 자바 기반으로 만들어진 웹 컴포넌트로, 서블릿 컨테이너에 의해서 관리되고 동적 컨텐츠를 생성할 수 있도록 지원하는 클래스입니다.

Servlet Container란?

웹 서버나 웹 어플리케이션 서버의 한 요소로 서블릿의 라이브싸이클을 관리하며, 클라이언트의 요청과 응답을 처리하고, MIME 타입을 디코드하는 관리 서비스입니다.

Servlet Life Cycle

Loading and Instantiation

Servlet Container는 Java 클래스 로드 기능을 사용하여 서블릿 클래스를 로드한 후 인스턴스화 합니다.

Initialization

Servlet Container는 Servlet 인터페이스의 init 메소드를 호출하여 서블릿을 초기화합니다.
이 때, 서블릿은 configuration data를 읽거나 resources를 초기화 하거나 그 밖에 정의된 행동들을 수행합니다.

초기화 중 서블릿 인스턴스는 UnavailableException이나 ServletException은 던질 수 있습니다. 이 때 서블릿 인스턴스는 반환되며 destroy는 호출되지 않습니다.

Request Handling

클라이언트의 요청은 ServletRequest, ServletResponse 형의 오브젝트로 표현됩니다. HTTP request의 경우에는 HttpServletRequest, HttpServletResponse형으로 표현됩니다.

Multithreading Issue

서블릿 클래스는 thread-safe하지 않기 때문에 서블릿 컨테이너가 활용하는 멀티쓰레드 환경에 대응하기 위해서는 적절한 조치를 해야 합니다.

이를 처리하기 위한 방안 중에 하나로
서블릿 컨테이너가 service 메소드에서 한 번에 하나의 요청만 처리하도록 SingleThreadModel 인터페이스를 구현하는 것입니다.
이렇게 함으로써 동시성 처리에 대한 역할을 서블릿 컨테이너로 넘겨 서블릿 컨테이너가 요청을 queuing하고 서블릿 인스턴스 풀을 유지하는 방식으로 처리할 수 있습니다.
하지만 권장하지는 않습니다.

다른 방안으로는 synchronized 키워드를 servicedoGet, doPost와 같은 함수에 사용하는 것입니다. 이 방식은 서블릿 컨테이너에서 인스턴스 풀 접근 방식을 사용할 수 없습니다.
하지만 synchronized 키워드를 사용하는 방식은 성능 저하의 원인이 될 수 있으므로 사용을 추천하지 않습니다.

그렇다면 어떻게 thread-safe하게 사용할 수 있을까?
멀티 쓰레드 환경에서 스프링빈 주의사항 - BeyondJ2EE이나 Spring bean thread safety guide - DEV IN WEB에 따르면 클래스의 멤버 변수를 사용하지 말고 함수의 로컬 변수를 사용함으로써 stateless한 인스턴스를 가져가는게 좋다고 합니다.

Request

클라이언트가 HTTP로 보내는 요청은 HttpServletRequest 객체로 받을 수 있습니다.

해당 객체에는 HTTP의 정보가 담겨있는데요.
Header, Attribute, File, Request Path, Cookie, SSL Attribute, Internationalization에 대한 정보가 들어있습니다.

또한 ReadListener를 활용하여 Non-Blocking으로 요청을 처리할 수 있도록 지원합니다.

Servlet Context

ServletContext는 컨테이너 공급자가 제공해야하는 구현체로 서블릿은 ServletContext 객체를 통하여 이벤트를 기록하거나 리소스의 URL을 참고하거나 attribute를 저장 및 사용하거나 동일 컨텍스트내 다른 서블릿에 접근할 수 있습니다.

ServletContext는 각 JVM마다 존재하며 서블릿을 관리합니다.

서블릿 3.0부터 서블릿 컨텍스트는 서블릿이나 필터, URL 패턴을 프로그램 레벨에서 추가할 수 있습니다.

하나의 IP 구동되는 서버에서 다수의 로지컬 호스트를 가지는 경우에는 virtual hosting 을 할 수 있습니다. 이러한 경우에는 각 로지컬한 호스트마다 서블릿 컨텍스트를 가져야하고, 서블릿 컨텍스트간 공유는 불가합니다.

Response

클라이언트에 보낼 HTTP로 응답은 HttpServletResponse 객체를 사용할 수 있습니다.

서블릿 컨테이너에서 buffer의 제공은 필수는 아니지만 클라이언트에 효율적인 응답성을 위하여 buffer를 제공합니다.

HTTP 헤더를 설정할 수 있는데, response가 commit되기 전에 header를 설정해야 클라이언트가 성공적으로 받을 수 있습니다.

Non-blocking IO는 서블릿과 필터가 async request processing 되도록 구성되어야만 사용할 수 있습니다. 그렇지 않으면 ServletInputStream.setReadListenerServletOutputStream.setWriteListener를 invoke할 때 IllegalStateException이 발생합니다.

Filtering

Filter는 자바 컴포넌트의 하나로 클라이언트의 요청이나 응답시 처리할 수 있는 행동을 정의하도록 지원합니다.

가령 요청을 invoke하기 전에 리소스에 접근하거나 응답을 위해 헤더나 데이터를 수정하는것을 필터를 통해 처리할 수 있습니다.

필터는 javax.servlet.Filter 인터페이스를 구현하고 deployment descriptor<filter>, <filter-mapping> 태그를 활용하여 정의할 수 있습니다. 또는 @WebFilter 어노테이션을 사용하여 필터를 정의할 수 있습니다.

Sessions

HTTP는 상태를 저장하기 않도록 설계된 프로토콜입니다.
하지만 웹 어플리케이션을 개발하다보면 누구의 접속정보인지를 알아야하는 경우가 많습니다.
이를 위해서 많은 세션 트래킹 전략들이 있는데요. 서블릿에서는 Cookies, SSL Sessions, URL Rewriting, Sesstion Integrity를 활용하여 세션 트래킹을 수행합니다.

서블릿 개발자는 세션에 대한 관리, thread safe, 분산 환경에 대한 고려를 하여야 합니다.

Annotation and pluggability

서블릿 3.0부터 어노테이션을 활용하여 서블릿을 정의할 수 있습니다.

서블릿은 다음과 같이 @WebServlet 어노테이션으로 정의할 수 있습니다.

@WebServlet(name=”MyServlet”, urlPatterns={"/foo", "/bar"}) 
public class SampleUsingAnnotationAttributes extends HttpServlet{ 
  public void doGet(HttpServletRequest req, HttpServletResponse res) {

  }
}

필터는 다음과 같이 @WebFilter 어노테이션으로 정의할 수 있습니다.

@WebFilter(“/foo”)
public class MyFilter implements Filter { 
  public void doFilter(HttpServletRequest req, HttpServletResponse res) {

  }
}

@WebListener 어노테이션을 활용하여 다양한 리스너를 implements하여 서블릿 컨텍스트를 정의할 수 있습니다.

@WebListener 
public class MyListener implements ServletContextListener{ 
  public void contextInitialized(ServletContextEvent sce) { 
    ServletContext sc = sce.getServletContext(); 
    sc.addServlet("myServlet", "Sample servlet", "foo.bar.MyServlet", null, -1); 
    sc.addServletMapping("myServlet", new String[] { "/urlpattern/*" });
  }
}
/*
List of listener
- javax.servlet.ServletContextListener
- javax.servlet.ServletContextAttributeListener 
- javax.servlet.ServletRequestListener 
- javax.servlet.ServletRequestAttributeListener 
- javax.servlet.http.HttpSessionListener 
- javax.servlet.http.HttpSessionAttributeListener 
- javax.servlet.http.HttpSessionIdListener
*/

META-INF 디렉토리에 web-fragment.xmlweb.xml과 동일한 문법으로 정의하면 해당 라이브러리나 프레임웍을 모듈화하여 사용할 수 있다.
또한 web.xmlweb-fragment.xml, 어노테이션에서 정의한 것들의 순서도 지정할 수 있다.

Dispatching Requests

웹 어플리케이션을 개발하다보면 다른 서블릿에 요청을 포워딩하거나 다른 서블릿의 응답을 포함시켜야하는 경우가 있습니다. 이 때 사용할 수 있는 것이 RequestDispatcher입니다.

그리고 비동기 프로세싱을 할 수 있도록 설정되어 있으면 AsyncContext가 사용자가 요청을 서블릿 컨테이너로 다시 보낼 수 있습니다.

RequestDispatcherServletContext를 통하여 사용할 수 있고 includeforward 함수를 사용하여 상황에 맞게 처리할 수 있습니다.

Web Applications

웹 어플리케이션은 서블릿, HTML 페이지, 클래스, 리소스의 집합입니다. 그리고 웹 서버내의 특정 경로를 root로 하여 웹 어플리케이션이 동작합니다.

서블릿 컨테이너는 웹 애플리케이션과 ServletContext 간의 일대일 대응 시킵니다.

웹 어플리케이션은 다음과 같은 구성요소를 가집니다.

  • Servlets
  • JSP™ 페이지
  • Utility 클래스
  • 정적 리소스 (HTML, 이미지, 등등)
  • 클라이언트단 자바 애플릿, 빈즈, 클래스
  • 위의 정보들을 구성하는 메타 정보

실제 배포 구조는 다음과 같이 표현됩니다.

/index.html 
/howto.jsp 
/feedback.jsp 
/images/banner.gif 
/images/jumping.gif 
/WEB-INF/web.xml 
/WEB-INF/lib/jspbean.jar 
/WEB-INF/lib/catalog.jar!/METAINF/resources/catalog/moreOffers/books.html 
/WEB-INF/classes/com/mycorp/servlets/MyServlet.class 
/WEB-INF/classes/com/mycorp/util/MyUtils.class

아카이브의 파일의 형태로 Web ARchive format (WAR)를 사용하고 위와 같은 구조를 가집니다.

내부에 포함된 web.xml에는 deployment descriptor를 표현하고 있는데요.
다음과 내용을 포함합니다.

  • ServletContext Init Parameters
  • Session Configuration
  • Servlet/JSP Definitions
  • Servlet/JSP Mappings
  • MIME Type Mappings
  • Welcome File list
  • Error Pages
  • Security

기본 리다이렉트를 위한 웰컴 파일에 대한 설정은 deployment descriptor에 다음과 같이 설정할 수 있습니다.

<welcome-file-list>
  <welcome-file>index.html</welcome-file>
  <welcome-file>default.jsp</welcome-file>
</welcome-file-list>

Application Lifecycle Events

웹 어플리케이션 개발자가 ServletContext, HttpSession, ServletRequest의 라이프싸이클을 다루기 쉽게 어플리케이션 이벤트에 대한 기능들을 제공합니다.

Listener에 대한 정의는 deployment descriptor에 다음과 같이 정의할 수 있습니다.

<web-app>
  <display-name>MyListeningApplication</display-name>
  <listener>
    <listener-class>com.acme.MyConnectionManager</listenerclass>
  </listener>
  <listener>
    <listener-class>com.acme.MyLoggingModule</listener-class>
  </listener>
  <servlet>
    <display-name>RegistrationServlet</display-name>
    ...etc
  </servlet>
</web-app>

Mapping Requests to Servlets

웹 컨테이너는 클라이언트의 요청을 서블릿과 맵핑하여 처리하도록 합니다.

이 때, URL 경로를 사용하여 어느 서블릿을 사용해야하는지 알 수 있는데요.
맵핑 순서는 다음과 같습니다.

  1. 전체 경로가 완전히 일치하는 값을 찾습니다.
  2. 클라이언트가 요청한 경로에서 가장 긴 값과 일치하는 값을 찾습니다.
  3. URL 경로가 확장자가 있으면, 해당 확장자와 일치하는 값을 찾습니다.
  4. 위의 1 ~ 3 과정에 일치하는 값이 없으면 default로 정의한 값을 찾습니다.

Security

보안에 대해 고려해야할 부분은 인증, 리소스의 접근 권한, 데이터 무결성, 기밀성이나 데이터 보완 입니다.

이를 구현하기 위해서는 선언적 보안프로그램적 보안이 있는데요. 선언적 보안은 어플리케이션의 외부 형식으로 표현하는 것이고, 프로그램적 보안HttpServletRequest에서 제공하는 다양한 인터페이스를 활용하여 구현할 수 있습니다.

프로그램적 보안에 대한 정책은 @ServletSecurity 어노테이션을 사용하여 정의할 수 있습니다.

다음과 같이 표현할 수 있습니다.

@ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), 
httpMethodConstraints = @HttpMethodConstraint(value="TRACE", emptyRoleSemantic = EmptyRoleSemantic.DENY)) public class Example7 extends HttpServlet { }

이는 TRACE를 제외한 모든 HTTP 메소드에서 R1의 롤이 필요하고 TRACE는 모든 접근은 막는다.

이를 deployment discriptorsecurity-constraint 엘리먼트를 활용하여 표현하면 다음과 같습니다.

<security-constraint>
  <web-resource-collection>
    <url-pattern>...</url-pattern>
    <http-method-omission>TRACE</http-method-omission>
  </web-resource-collection>
  <auth-constraint>
    <role-name>Role1</role-name>
  </auth-constraint>
</security-constraint>
<security-constraint>
  <web-resource-collection>
    <url-pattern>...</url-pattern>
    <http-method>TRACE</http-method>
  </web-resource-collection>
  <auth-constraint/>
</security-constraint>

인증

웹 클라이언트는 다음 인증 방식 중 하나는 선택하여 사용할 수 있습니다.

  • HTTP Basic Authentication
  • HTTP Digest Authentication
  • HTTPS Client Authentication
  • Form Based Authentication

Deployment Discriptor

Deployment discriptor의 웹 컨테이너 지원을 위한 Java Servlet 스펙을 정의합니다.

설정 가능한 옵션은 다음과 같습니다.

  • ServletContext Init Parameters
  • Session Configuration
  • Servlet Declaration
  • Servlet Mappings
  • Application Lifecyle Listener classes
  • Filter Definitions and Filter Mappings
  • MIME Type Mappings
  • Welcome File list
  • Error Pages
  • Locale and Encoding Mappings
  • Security configuration, including login-config, security-constraint, security-role, security-role-ref and run-as

후기

오라클에서 제공하는 서블릿에 대한 정보를 대략적으로 보는데만해도 3일의 시간이 걸렸습니다.

그 만큼 자바를 활용하여 웹 어플리케이션을 만들기 위해서 필수적으로 알아야할 내용이라고 생각이 들었고 이제서야 서블릿의 스펙을 봤다는거 자체가 부끄럽게 느껴지기도 하였습니다.

비단 스프링의 옵션이라고 생각했던 web.xml에 대한 부분도 이미 서블릿에서 정의된 것들이라는 것도 알고나니 앞으로 스프링을 이해하는데 많은 도움이 될 것이라고 생각합니다.

앞으로도 숙지해야할 것들이 많습니다.
차근차근 쌓아나가야 겠습니다.

참고문헌