Java Servlet이란?
안녕하세요. 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
키워드를 service
나 doGet
, 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.setReadListener
나 ServletOutputStream.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.xml
에 web.xml
과 동일한 문법으로 정의하면 해당 라이브러리나 프레임웍을 모듈화하여 사용할 수 있다.
또한 web.xml
과 web-fragment.xml
, 어노테이션에서 정의한 것들의 순서도 지정할 수 있다.
Dispatching Requests
웹 어플리케이션을 개발하다보면 다른 서블릿에 요청을 포워딩하거나 다른 서블릿의 응답을 포함시켜야하는 경우가 있습니다. 이 때 사용할 수 있는 것이 RequestDispatcher
입니다.
그리고 비동기 프로세싱을 할 수 있도록 설정되어 있으면 AsyncContext
가 사용자가 요청을 서블릿 컨테이너로 다시 보낼 수 있습니다.
RequestDispatcher
는 ServletContext
를 통하여 사용할 수 있고 include
와 forward
함수를 사용하여 상황에 맞게 처리할 수 있습니다.
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 경로를 사용하여 어느 서블릿을 사용해야하는지 알 수 있는데요.
맵핑 순서는 다음과 같습니다.
- 전체 경로가 완전히 일치하는 값을 찾습니다.
- 클라이언트가 요청한 경로에서 가장 긴 값과 일치하는 값을 찾습니다.
- URL 경로가 확장자가 있으면, 해당 확장자와 일치하는 값을 찾습니다.
- 위의 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 discriptor
의 security-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
에 대한 부분도 이미 서블릿에서 정의된 것들이라는 것도 알고나니 앞으로 스프링을 이해하는데 많은 도움이 될 것이라고 생각합니다.
앞으로도 숙지해야할 것들이 많습니다.
차근차근 쌓아나가야 겠습니다.