2023. 4. 17. 12:16ㆍSpring Framework/Web on Servlet Stack
Servlet
서블릿(Servlet) 자체는 웹 애플리케이션의 구성 요소 중 하나입니다. 웹 애플리케이션은 클라이언트의 요청에 대한 동적인 처리를 위해 서버상에서 실행되는 애플리케이션을 말하며, 서블릿은 그 중에서 Java를 사용하여 웹 서버에서 실행되는 프로그램입니다.
서블릿은 HTTP 요청을 받아 처리하고, 그 결과를 웹 브라우저로 다시 전송(응답)하는 역할을 합니다. 이는 HTML, XML 등의 형태로 응답을 생성하여 클라이언트(보통 웹 브라우저)에 전달합니다. 서블릿은 Java EE(Enterprise Edition) 스펙의 일부이며, JSP(JavaServer Pages), JSF(JavaServer Faces)와 같은 다른 Java 웹 기술과 함께 웹 애플리케이션의 백엔드 로직을 구현하는 데 사용됩니다.
요약하자면, 서블릿은 웹 애플리케이션의 한 부분이지만, 전체 웹 애플리케이션 자체는 아닙니다. 웹 애플리케이션은 서블릿뿐만 아니라 HTML, CSS, JavaScript, 데이터베이스 연결, 보안 설정 등 다양한 구성 요소와 기술로 이루어져 있습니다.
Java Servlet Versions
Java Servlet has these versions:
- J2EE 1.2 (December 12, 1999) (Java Servlet 2.2, JSP 1.1, EJB 1.1, JDBC 2.0)
- J2EE 1.3 (September 24, 2001) (Java Servlet 2.3, JSP 1.2, EJB 2.0, JDBC 2.1)
- J2EE 1.4 (November 11, 2003) (Java Servlet 2.4, JSP 2.0, EJB 2.1, JDBC 3.0)
- Java EE 5 (May 11, 2006) (Java Servlet 2.5, JSP 2.1, JSTL 1.2, JSF 1.2, EJB 3.0, JDBC 3.0)
- Java EE 6 (December 10, 2009) (Java Servlet 3.0, JSP 2.2/EL 2.2, JSTL 1.2, JSF 2.0, EJB 3.1, JDBC 4.0)
- Java EE 7: expected in end of 2012.
http-nio-8080
http-nio-8080은 Apache Tomcat 서버에서 사용되는 네트워크 커넥터의 설정 중 하나입니다. 이 설정은 Tomcat이 HTTP 프로토콜을 사용하여 8080 포트를 통해 클라이언트 요청을 수신하고 처리하도록 구성되어 있음을 나타냅니다. 여기서 nio는 Non-blocking I/O를 의미하는데, 이는 Java의 비동기 입출력 모델 중 하나입니다.
http-nio-8080의 주요 특징:
- HTTP: 이 커넥터는 HTTP 프로토콜을 사용하여 웹 요청과 응답을 처리합니다.
- NIO (Non-blocking I/O): NIO 모델은 논블로킹 입출력을 지원하여, 하나의 스레드가 여러 네트워크 연결을 동시에 관리할 수 있게 합니다. 이는 리소스 사용을 최적화하고, 서버의 처리량과 성능을 향상시킬 수 있습니다.
- 8080 포트: 기본적으로 웹 서버는 8080 포트를 사용하여 HTTP 요청을 수신합니다. 이 포트 번호는 웹 서버의 설정 파일에서 변경할 수 있습니다.
Tomcat의 server.xml 설정 파일에서는 다양한 타입의 커넥터를 구성할 수 있으며, http-nio-8080 설정은 특히 비동기 처리와 높은 동시성을 요구하는 웹 애플리케이션에 적합합니다. Tomcat 8 이상에서는 NIO가 기본 커넥터로 사용되며, 더 효율적인 네트워크 처리를 위해 APR(Apache Portable Runtime) 라이브러리와 함께 사용될 수도 있습니다.
HTTP 클라이언트-서버 시스템 아키텍처
HelloServlet 서블릿 작성
서블릿은 Apache Tomcat과 같은 Java 지원 HTTP 서버 내에서 실행되는 Java 프로그램입니다. 웹 사용자는 웹 브라우저(HTTP 클라이언트)에서 적절한 URL을 발행함으로써 서블릿을 호출합니다.
Write a "HelloServlet" Java Servlet
Java 서블릿은 HTTP 서버 내에서 실행되는 Java 프로그램입니다. 웹 사용자는 브라우저(또는 HTTP 클라이언트)에서 URL을 발행하여 서블릿을 호출합니다.
이 예제에서는 "Hello, world!"라고 말하는 HelloServlet이라는 Java 서블릿을 작성할 것입니다. 웹 사용자가 자신의 브라우저에서 http://ip_addr:port/hello/sayhello URL을 발행하여 이 서블릿을 호출할 수 있도록 구성할 것입니다.
webapps은 Tomcat에서 웹 컨텍스트로 알려져 있으며, HTML 파일, CSS, 자바스크립트, 이미지, 프로그램 및 라이브러리와 같은 일련의 리소스로 구성됩니다.
Java webapps은 다양한 유형의 리소스를 저장하기 위한 표준화된 디렉토리 구조를 가지고 있습니다.
톰캣의 webapps 디렉토리 아래에 helloservlet 디렉토리[ <CATALINA_HOME>\webapps\helloservlet ]를 생성합니다.
* 여기서 <CATALINA_HOME>은 Tomcat이 설치된 디렉토리를 나타냅니다
helloservlet 아래에 WEB-INF와 META-INF 하위 디렉토리를 생성합니다.
WEB-INF 아래에 classes, lib 및 src 하위 디렉토리들을 생성합니다. 디렉토리 이름이 대소문자를 구분한다는 점에 주의하세요.
리소스는 각각의 디렉토리에 보관되어야 합니다:
- <CATALINA_HOME>\webapps\helloservlet: 이 디렉토리는 웹 컨텍스트 helloservlet에 대한 컨텍스트 루트로 알려져 있습니다. 이곳에는 HTML, CSS, 스크립트 및 이미지와 같이 클라이언트가 접근할 수 있는 리소스가 포함되어 있습니다. 이 리소스들은 그대로 클라이언트에게 전달될 것입니다. 리소스를 더 분류하기 위해 images, css, scripts와 같은 하위 디렉토리를 생성할 수 있습니다.
- <CATALINA_HOME>\webapps\helloservlet\WEB-INF: 이 디렉토리는 클라이언트가 직접 접근할 수 없습니다. 여기에는 애플리케이션 특정 구성 파일(예: web.xml)을 보관하며, 그 하위 디렉토리에는 프로그램 클래스, 소스 파일, 라이브러리가 포함되어 있습니다.
- <CATALINA_HOME>\webapps\helloservlet\WEB-INF\src: Java 프로그램 소스 파일을 보관하세요. 소스 파일과 클래스를 분리하여 배포를 용이하게 하는 것은 좋은 관행입니다.
- <CATALINA_HOME>\webapps\helloservlet\WEB-INF\classes: 소스 코드에서 컴파일된 Java 클래스를 보관하세요. 패키지에 정의된 클래스는 패키지 디렉토리 구조에 따라 보관해야 합니다.
- <CATALINA_HOME>\webapps\helloservlet\WEB-INF\lib: 이 웹앱에서만 사용 가능한 외부 패키지에서 제공하는 JAR 파일을 보관하세요.
- <CATALINA_HOME>\webapps\helloservlet\META-INF: 이 디렉토리도 클라이언트가 접근할 수 없습니다. 특정 서버(예: Tomcat, Glassfish)와 관련된 리소스 및 구성(예: context.xml)을 보관합니다. 반면에 WEB-INF는 서버와 독립적인 이 웹 앱과 관련된 리소스를 위한 곳입니다.
Write a Hello-world Java Servlet - HelloServlet.java
서블릿은 Java 지원 HTTP 서버 내에서 실행되는 Java 프로그램입니다. 사용자는 브라우저(HTTP 클라이언트)에서 특정 URL을 발행함으로써 서블릿을 호출할 수 있습니다. 이 예제에서는 HelloServlet.java라는 서블릿을 작성하고 HelloServlet.class로 컴파일할 것입니다. 클라이언트는 http://hostname:port/helloServlet/sayhello URL을 발행하여 HelloServlet.class를 호출할 수 있습니다.
서블릿은 적절한 배포를 위해 기본 이름이 없는 패키지가 아닌 Java 패키지 내에 보관되어야 합니다. 우리의 패키지를 "mypkg"라고 합시다. WEB-INF\src 아래에 mypkg라는 하위 디렉토리를 생성하세요. 프로그래밍 텍스트 에디터를 사용하여 다음 소스 코드를 입력하고 <CATALINA_HOME>\webapps\helloservlet\WEB-INF\src\mypkg에 HelloServlet.java로 저장하세요.
// To save as "<CATALINA_HOME>\webapps\helloservlet\WEB-INF\src\mypkg\HelloServlet.java"
package mypkg;
import java.io.*;
import jakarta.servlet.*; // Tomcat 10
import jakarta.servlet.http.*; // Tomcat 10
import jakarta.servlet.annotation.*; // Tomcat 10
//import javax.servlet.*; // Tomcat 9
//import javax.servlet.http.*; // Tomcat 9
//import javax.servlet.annotation.*; // Tomcat 9
// Configure the request URL for this servlet (Tomcat 7/Servlet 3.0 upwards)
// @WebServlet("/sayhello")
public class HelloServlet extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// Set the response message's MIME type
response.setContentType("text/html;charset=UTF-8");
// Allocate a output writer to write the response message into the network socket
PrintWriter out = response.getWriter();
// Write the response message, in an HTML page
try {
out.println("<!DOCTYPE html>");
out.println("<html><head>");
out.println("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>");
out.println("<title>Hello, World</title></head>");
out.println("<body>");
out.println("<h1>Hello, world!</h1>"); // says Hello
// Echo client's request information
out.println("<p>Request URI: " + request.getRequestURI() + "</p>");
out.println("<p>Protocol: " + request.getProtocol() + "</p>");
out.println("<p>PathInfo: " + request.getPathInfo() + "</p>");
out.println("<p>Remote Address: " + request.getRemoteAddr() + "</p>");
// Generate a random number upon each request
out.println("<p>A Random Number: <strong>" + Math.random() + "</strong></p>");
out.println("</body>");
out.println("</html>");
} finally {
out.close(); // Always close the output writer
}
}
}
- HelloServlet에서는 doGet() 메소드를 오버라이드합니다(@Override 어노테이션에 의해 표시됨). doGet() 메소드는 사용자가 URL을 통해 발행한 HTTP GET 요청에 대한 응답으로 실행됩니다. doGet() 메소드는 두 개의 아규먼트, 즉 요청과 응답 메시지에 해당하는 HttpServletRequest 객체와 HttpServletResponse 객체를 전달 받습니다.
- HttpServletRequest 객체는 incoming HTTP 요청 헤더와 폼 데이터를 검색하는 데 사용할 수 있습니다. HttpServletResponse 객체는 HTTP 응답 헤더(예: content-type)와 응답 메시지 본문을 설정하는 데 사용할 수 있습니다.
- 우리는 응답 메시지의 "MIME" 타입을 "text/html"로 설정합니다. 클라이언트는 받은 데이터를 올바르게 표시하기 위해 메시지 타입을 알아야 합니다. (다른 MIME 타입에는 text/plain, image/jpeg, video/mpeg, application/xml 등이 있습니다.) 그리고 우리는 네트워크를 통해 클라이언트에게 응답 메시지를 작성하기 위한 Writer 객체인 out을 검색합니다. 그런 다음 out.println()을 사용하여 "Hello, world!" 메시지를 포함한 적절한 HTML 페이지를 출력합니다. 이 서블릿은 또한 클라이언트의 요청 정보 중 일부를 에코하고, 각 요청에 대해 무작위 숫자를 출력합니다.
Configure the Application Deployment Descriptor - web.xml
웹 사용자는 브라우저에서 특정 URL을 발행함으로써 웹 서버에 보관된 서블릿을 호출합니다. 이 예제에서는 다음 요청 URL을 구성하여 "HelloServlet"을 트리거할 것입니다:
http://hostname:port/helloservlet/sayhello
web.xml이라는 구성 파일을 생성하고 webapps\helloservlet\WEB-INF 아래에 다음과 같이 저장하세요:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- To save as <CATALINA_HOME>\webapps\helloservlet\WEB-INF\web.xml -->
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>mypkg.HelloServlet</servlet-class>
</servlet>
<!-- Note: All <servlet> elements MUST be grouped together and
placed IN FRONT of the <servlet-mapping> elements -->
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/sayhello</url-pattern>
</servlet-mapping>
</web-app>
- web.xml은 웹 애플리케이션 배포 디스크립터라고 불립니다. 이것은 URL과 서블릿 클래스 간의 매핑을 정의하는 등, 해당 웹 애플리케이션에 대한 구성 옵션을 제공합니다.
- 위의 구성은 mypkg.HelloServlet.class(앞서 작성됨)에 구현된 HelloWorldServlet이라는 이름의 서블릿을 정의하고, URL /sayhello에 매핑합니다. 여기서 /는 이 webapp helloservlet의 context root를 나타냅니다. 즉, 이 서블릿의 절대 URL은 http://hostname:port/helloservlet/sayhello입니다.
- 각 서블릿은 임의이지만 고유한 <servlet-name>을 통해 매핑을 수행하기 위해 <servlet> 및 <servlet-mapping> 엘리먼트 페어가 필요하다는 점을 유념하세요. 또한, 모든 <servlet> 엘리먼트는 함께 그룹화되어야 하며 XML 스키마에 지정된 대로 <servlet-mapping> 엘리먼트보다 앞에 배치되어야 합니다.
Request Header and Response Header
HTTP는 요청-응답 프로토콜입니다. 클라이언트는 서버로 요청 메시지를 보냅니다. 그러면 서버는 응답 메시지를 반환합니다. 요청과 응답 메시지는 두 부분으로 구성됩니다: 헤더(메시지에 대한 정보)와 본문(내용). 헤더는 메시지에 대한 정보를 제공합니다. 헤더의 데이터는 이름-값 쌍으로 구성됩니다.
HttpServletRequest
이 메시지는 HttpServletRequest 객체에 캡슐화되며, 이 객체는 doGet() 메소드로 전달됩니다. HttpServletRequest는 헤더를 검색하기 위해 여러분이 사용할 수 있는 많은 메소드를 제공합니다:
- General methods: getHeader(name), getHeaders(name), getHeaderNames().
- Specific methods: getContentLength(), getContentType(), getCookies(), getAuthType(), etc.
- URL related: getRequestURI(), getQueryString(), getProtocol(), getMethod().
HttpServletResponse
응답 메시지는 HttpServletResponse에 캡슐화되며, 서블릿 출력을 받기 위해 참조로 doGet()에 전달됩니다.
- setStatusCode(int statuscode), sendError(int code, String message), sendRedirect(url).
- response.setHeader(String headerName, String headerValue).
- setContentType(String mimeType), setContentLength(int length), etc.
ServletConfig and ServletContext
ServletConfig
ServletConfig는 서블릿 컨테이너(예: Tomcat, GlassFish)가 초기화하는 동안 서블릿에 정보를 전달하기 위해 사용하는 서블릿 구성 객체입니다. 이것은 init() 메소드의 아규먼트로 전달됩니다. 초기화 파라미터는 애플리케이션 특정 배포 설명자 web.xml에 선언됩니다. ServletConfig.getInitParam("paramName") 메소드를 통해 초기화 파라미터를 검색할 수 있습니다. 예를 들어, 애플리케이션의 web.xml이 데이터베이스 연결에 대한 이러한 초기화 파라미터를 선언한다고 가정해 보겠습니다:
<web-app ...>
...
<servlet>
...
<init-param>
<param-name>databaseURL</param-name>
<param-value>jdbc:mysql://localhost:3306/ebookshop</param-value>
</init-param>
<init-param>
<param-name>user</param-name>
<param-value>myuser</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>xxxx</param-value>
</init-param>
</servlet>
...
</web-app>
servlet의 init() 메소드에서 init 파라미터를 다음과 같이 확인할 수 있습니다:
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// Read the init params and save them in web context for use by
// servlets and JSP within this web app.
ServletContext context = config.getServletContext();
context.setAttribute("databaseURL", config.getInitParameter("databaseURL"));
context.setAttribute("user", config.getInitParameter("user"));
context.setAttribute("password", config.getInitParameter("password"));
......
}
ServletContext
각 webapp은 서블릿 컨테이너(주로 톰캣의 Catalina[엔] 컴포넌트를 의미) 내에서 하나의 Context로 표현됩니다. Servlet API에서 이 컨텍스트는 jakarta.servlet.ServletContext 인터페이스로 정의되어 있으며 하나의 webapp은 많은 서블릿을 사용할 수 있습니다. 같은 webapp에 배포된 서블릿들은 공유된 ServletContext 객체를 사용하여 서로 간에 정보를 공유할 수 있습니다.
webapp(또는 Context) 당 하나의 ServletContext가 있으며, ServletConfig.getServletContext()를 통해 검색할 수 있습니다. 서블릿은 이를 사용하여 서블릿 컨테이너와 통신할 수 있습니다. 예를 들어, 파일의 MIME 타입을 얻거나, 요청을 디스패치하거나, 로그 파일에 기록하는 등의 작업을 할 수 있습니다. ServletContext는 "애플리케이션" 범위를 가지고 있으며, 같은 애플리케이션 내의 서블릿과 JSP 사이에 정보를 전달하는 데에도 사용될 수 있습니다.
주요 기능과 사용 예:
- 공유 데이터: 서블릿 컨텍스트를 사용하여 애플리케이션 전체에서 접근 가능한 속성을 설정하고 공유할 수 있습니다. 예를 들어, 애플리케이션 초기화 시 데이터베이스 연결 정보나 공통으로 사용되는 리소스를 서블릿 컨텍스트 속성으로 저장하고, 서블릿 간에 이를 공유할 수 있습니다.
- 애플리케이션 설정 정보: web.xml 파일 또는 애너테이션을 통해 정의된 웹 애플리케이션의 초기화 파라미터를 서블릿 컨텍스트를 통해 접근할 수 있습니다. 이 정보는 애플리케이션의 구성 설정이나 환경 변수 등을 포함할 수 있습니다.
- 리소스 접근: 서블릿 컨텍스트는 웹 애플리케이션의 리소스에 접근하는 메소드를 제공합니다. 예를 들어, 서블릿 컨텍스트를 사용하여 웹 애플리케이션의 리소스 파일(이미지, 텍스트 파일 등)에 접근할 수 있습니다.
- 로그 기록: 서블릿 컨텍스트를 통해 애플리케이션의 로그를 기록할 수 있습니다. 이는 애플리케이션의 상태를 모니터링하거나 문제를 진단하는 데 유용합니다.
- 애플리케이션 범위의 이벤트 리스너 등록: 서블릿 컨텍스트를 사용하여 애플리케이션 수명 주기 이벤트에 대한 리스너를 등록할 수 있습니다. 이를 통해 애플리케이션의 시작 및 종료와 같은 이벤트를 감지하고 적절한 처리를 할 수 있습니다.
서블릿 컨텍스트는 jakarta.servlet.ServletContext 인터페이스를 통해 정의되며, 웹 애플리케이션이 시작될 때 서블릿 컨테이너에 의해 생성되고, 애플리케이션이 종료될 때까지 유지됩니다. 서블릿에서는 getServletContext() 메소드를 호출하여 서블릿 컨텍스트에 접근할 수 있습니다.
Servlet Container
서블릿과 서블릿 컨테이너는 웹 서버 환경에서 밀접한 관계를 가지고 있습니다. 이들 간의 관계를 이해하려면 각각의 역할을 알아야 합니다:
- 서블릿(Servlet): 서블릿은 Java를 사용하여 웹 서버에서 실행되는 프로그램으로, 클라이언트의 요청을 처리하고 그 결과를 클라이언트에게 돌려주는 역할을 합니다. 서블릿은 주로 동적인 웹 컨텐츠를 생성하기 위해 사용됩니다.
- 서블릿 컨테이너(Servlet Container) 또는 서블릿 엔진: 서블릿 컨테이너는 서블릿의 생명주기를 관리하는 웹 서버의 일부분입니다. 서블릿 컨테이너는 서블릿이 정의한 방식대로 HTTP 요청을 받아 서블릿에 전달하고, 서블릿이 생성한 응답을 클라이언트에 전송하는 역할을 합니다. 또한, 서블릿 컨테이너는 서블릿의 로딩, 초기화, 실행, 종료 등의 생명주기를 관리합니다.
서블릿과 서블릿 컨테이너의 관계는 다음과 같습니다:
1. request 처리: 클라이언트(보통 웹 브라우저)에서 웹 서버로 HTTP 요청이 전송되면, 서블릿 컨테이너는 이 요청을 받아 적절한 서블릿에게 전달합니다. 서블릿 컨테이너는 URL 패턴이나 다른 구성 요소를 기반으로 어떤 서블릿이 요청을 처리할지 결정합니다.
2. 생명주기 관리: 서블릿 컨테이너는 서블릿의 생명주기를 관리합니다. 이는 서블릿의 생성(초기화), 요청 처리, 그리고 서블릿의 종료(제거)를 포함합니다. 서블릿 컨테이너는 필요할 때 서블릿 인스턴스를 생성하고 init(), service(), destroy() 메소드를 호출하여 각각 초기화, 요청 처리, 종료 과정을 관리합니다.
3. 자원 관리 및 멀티스레딩: 서블릿 컨테이너는 네트워크 연결과 같은 자원을 관리하고, 각 HTTP 요청마다 별도의 스레드를 할당하여 서블릿이 동시에 여러 요청을 처리할 수 있도록 합니다.
4. 보안 및 기타 서비스: 서블릿 컨테이너는 보안, 세션 관리, 컨텍스트 공유 등의 추가적인 서비스를 제공하여 서블릿이 보다 효율적으로 실행될 수 있도록 지원합니다.
요약하자면, 서블릿 컨테이너는 서블릿이 웹 서버에서 실행될 수 있도록 하는 실행 환경을 제공하며, 서블릿의 생명주기와 HTTP 요청/응답 처리를 관리하는 역할을 합니다. 서블릿은 이러한 컨테이너 내에서 동작하며, 실제로 웹 어플리케이션의 비즈니스 로직을 구현하는 구성요소입니다.
서블릿 컨테이너는 보통 웹 애플리케이션 단위로 작동합니다. 하나의 웹 애플리케이션에 여러 서블릿이 포함될 수 있으며, 이 모든 서블릿은 동일한 서블릿 컨테이너에 의해 관리됩니다. 예를 들어, Apache Tomcat, Jetty, JBoss EAP 등의 서버는 모두 서블릿 컨테이너의 역할을 수행하며, 단일 인스턴스에서 여러 웹 애플리케이션과 각 애플리케이션 내의 여러 서블릿을 실행할 수 있습니다.
서블릿 컨테이너는 서블릿의 초기화, 서비스 요청 처리, 종료 등의 과정을 관리하며, 각 서블릿은 일반적으로 요청 처리를 위해 별도의 스레드에서 실행됩니다. 그러나 서블릿 자체는 단일 인스턴스로 존재하며, 동시에 여러 요청을 처리할 수 있습니다.
Interface Servlet
Servlet 인터페이스는 Java 서블릿 API의 중심 개념입니다. 가장 일반적으로 사용되는 HTTP 요청을 처리하는 HttpServlet은 Servlet 인터페이스를 구현하는 GenericServlet의 하위 클래스입니다.
Servlet 인터페이스는 다음과 같은 추상 메서드를 선언합니다:
// Servlet's lifecycle
void init(ServletConfig config)
void destroy()
void service(ServletRequest request, ServletResponse response)
// Servlet configuration and information
ServletConfig getServletConfig()
String getServletInfo()
※ 서블릿을 구현할 때 주로 사용되는 Java EE(Java Enterprise Edition)의 클래스나 인터페이스는 다음과 같습니다:
1. jakarta.servlet.Servlet 인터페이스: 모든 서블릿이 구현해야 하는 기본 인터페이스입니다. 이 인터페이스는 서블릿의 생명주기를 정의하는 메소드들(init(), service(), destroy())을 포함하고 있습니다.
2. jakarta.servlet.GenericServlet 클래스: Servlet 인터페이스를 구현하는 추상 클래스로, Servlet 인터페이스의 일부 메소드를 구현해놓았습니다. 특정 프로토콜에 종속되지 않는 서블릿을 만들 때 사용할 수 있으며, 주로 service() 메소드를 구현하면 됩니다.
3. jakarta.servlet.http.HttpServlet: HTTP 프로토콜을 사용하는 웹 애플리케이션을 만들기 위한 서블릿을 구현할 때 가장 자주 사용되는 클래스입니다. GenericServlet을 상속받아 HTTP 프로토콜에 특화된 메소드(doGet(), doPost(), doPut(), doDelete() 등)를 제공합니다. 대부분의 웹 서블릿은 이 클래스를 확장하여 구현됩니다.
4. jakarta.servlet.ServletRequest 인터페이스와 javax.servlet.http.HttpServletRequest 인터페이스: 클라이언트로부터 받은 요청 정보를 서블릿에 전달하는 데 사용됩니다. HttpServletRequest는 ServletRequest를 확장한 인터페이스로, HTTP 요청 정보를 처리하는데 필요한 메소드들을 제공합니다.
5. jakarta.servlet.ServletResponse 인터페이스와 javax.servlet.http.HttpServletResponse 인터페이스: 서블릿이 클라이언트에 응답을 전송하는 데 사용됩니다. HttpServletResponse는 ServletResponse를 확장한 인터페이스로, HTTP 응답을 생성하는데 필요한 메소드들을 제공합니다.
6. jakarta.servlet.ServletConfig 인터페이스: 서블릿의 초기화 파라미터에 접근하는 데 사용됩니다. 서블릿 컨테이너는 서블릿이 초기화될 때 ServletConfig 객체를 서블릿에 전달하며, 서블릿은 이 객체를 사용하여 초기화 파라미터를 읽어들일 수 있습니다.
7. jakarta.servlet.ServletContext 인터페이스: 웹 애플리케이션의 실행 환경에 대한 정보를 제공하고, 애플리케이션 전체에서 공유할 수 있는 리소스에 접근하는 데 사용됩니다.
이 클래스들과 인터페이스들은 서블릿을 구현하고, 클라이언트의 요청을 처리하며, 응답을 생성하는 과정에서 필수적으로 사용됩니다. Java EE 스펙에 정의된 이러한 API를 통해 개발자는 웹 서버에서 실행되는 동적인 웹 애플리케이션을 쉽게 개발할 수 있습니다.
Servlet's Life cycle
servlet의 life cycle은 init(), service() 그리고 destroy() 메서드들에 의해 관리됩니다.
Loading and Initialization
서블릿 컨테이너는 서블릿을 로딩하고 인스턴스화하는 역할을 담당합니다. 이 컨테이너는 시작될 때 서블릿을 로딩하고 인스턴스화할 수도 있고, 서블릿이 요청을 처리해야 할 필요가 있는 경우에야 해당 서블릿을 로딩하고 인스턴스화할 수도 있습니다 (일반적으로 첫 번째 요청 시에 이루어집니다).
서블릿 컨테이너는 서블릿의 init(ServletConfig) 메서드를 호출하며, 이 메서드에 ServletConfig 객체를 인수로 제공합니다. init() 메서드는 한 번만 실행됩니다. 이 메서드는 일반적으로 영구적인 구성 데이터를 읽고 비용이 많이 드는 리소스를 초기화하는 데 사용됩니다.
ServletConfig 객체를 통해 해당 서블릿의 init parameter에 액세스할 수 있습니다. 이러한 파라미터는 웹 애플리케이션 배포 디스크립터 파일(즉, "web.xml")에서 서블릿의 이름 아래에 정의됩니다.
<servlet>
<servlet-name>ServletName</servlet-name>
<servlet-class>ServletClassFile</servlet-class>
<init-param>
<param-name>initParam1</param-name>
<param-value>initParam1Value</param-value>
</init-param>
<init-param>
<param-name>initParam2</param-name>
<param-value>initParam2Value</param-value>
</init-param>
</servlet>
ServletConfig 인터페이스는 이 서블릿의 init-param를 검색하기 위해 다음과 같은 메서드를 정의합니다.
String getInitParameter(String name)
java.util.Enumeration getInitParameterNames()
예를 들면,
public void init(ServletConfig config) throws ServletException {
// Read all the init parameters for this servlet
Enumeration e = config.getInitParameterNames();
while (e.hasMoreElements()) {
String initParamName = (String)e.nextElement();
String initParamValue = config.getInitParameter(initParamName);
......
}
}
ServletConfig 인터페이스는 HTTPServlet 및 GenericServlet에 의해 구현됩니다. 따라서 getInitParameter() 및 getInitParameterNames() 메서드를 init() 또는 service() 내에서 직접 호출할 수 있습니다.
또한 ServletConfig는 ServletContext 객체에 액세스할 수 있는 기회를 제공하는데, 이 객체는 현재 웹 컨텍스트(또는 웹 애플리케이션)에 대한 정보를 제공합니다.
In Service
서블릿이 한번 초기화되면, 서블릿 컨테이너는 클라이언트 요청을 처리하기 위해 해당 서블릿의 service() 메서드를 호출합니다. 이 메서드는 각 요청마다 한 번 호출됩니다. 일반적으로, 서블릿 컨테이너는 동일한 서블릿에 대한 동시 요청을, 다른 스레드에서 service()를 호출하여 처리합니다
HttpServlet의 경우, service() 메서드는 HTTP GET, POST, HEAD, OPTIONS, TRACE 등의 요청을 처리하기 위해 각각 doGet(), doPost(), doHead(), doOptions(), doTrace() 등으로 디스패치합니다.
HttpServlet의 service() 메서드는 두 개의 인수를 받습니다. HttpServletRequest 객체와 HttpServletResponse 객체이며, 각각 HTTP 요청 및 응답 메시지에 해당합니다.
End of Service
서블릿 컨테이너가 서블릿을 컨테이너에서 제거해야 하는 상황(예: 컨테이너 종료 또는 시간 제한, 이는 구현에 따라 다를 수 있음)일 때, 해당 서블릿에서 사용 중인 모든 리소스를 해제하고 영구 상태를 저장하기 위해 destroy() 메서드를 호출합니다. 서블릿 컨테이너가 destroy()를 호출하기 전에는 모든 service() 스레드가 완료되거나 시간 제한에 도달할 때까지 기다려야 합니다.
Interface ServletContext
ServletContext 인터페이스는 서블릿이 실행 중인 웹 앱(또는 웹 컨텍스트)에 대한 서블릿의 관점을 정의합니다 (사실 더 나은 이름은 ApplicationContext일 것입니다). ServletContext 객체를 통해 서블릿은 컨테이너와 통신할 수 있으며, 예를 들어 이벤트 로그에 기록하거나 리소스에 대한 URL 참조를 얻거나 동일한 컨텍스트 내의 다른 서블릿이 액세스할 수 있는 속성을 가져오고 설정할 수 있습니다.
컨테이너에는 각 웹 애플리케이션당 하나의 ServletContext 객체가 있습니다. 웹 애플리케이션 배포 디스크립터에서 웹 컨텍스트 내의 모든 서블릿에서 사용 가능한 초기화 파라미터들을 지정할 수 있습니다. 예를 들어, 다음과 같이 지정할 수 있습니다.
<web-app ......>
<context-param>
<param-name>jdbcDriver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>databaseUrl</param-name>
<param-value>jdbc:mysql://localhost/eshop</param-value>
</context-param>
......
</web-app>
이 웹 컨텍스트하의 서블릿들은 ServletConfig의 메서드를 통해 컨텍스트의 초기화 파라미터에 액세스할 수 있습니다.
Filtering
필터는 HTTP 요청, 응답 및 헤더 정보의 내용을 변환할 수 있는 재사용 가능한 코드 조각입니다. 필터 구성 요소의 예시로는 다음과 같은 것들이 있습니다:
- Authentication filters
- Logging and auditing filters
- Image conversion filters
- Data compression filters
- Encryption filters
- Tokenizing filters
- Filters that trigger resource access events
- XSL/T filters that transform XML content
- MIME-type chain filters
- Caching filters
Web Application Deployment Descriptor "web.xml"
"web.xml" 파일은 웹 애플리케이션 배포 디스크립터를 포함합니다. Tomcat은 시스템 전체(글로벌) "web.xml"을 "<CATALINA_HOME>\conf" 경로에 가지고 있습니다. 각 웹 애플리케이션은 "ContextRoot\WEB-INF" 경로에 자체 "web.xml"을 가지고 있으며, 이 파일은 글로벌 설정을 재정의합니다. Tomcat은 모든 웹 애플리케이션의 "web.xml" 파일을 모니터링하고, reloadable이 true로 설정되어 있다면 "web.xml"이 변경될 때 해당 웹 애플리케이션을 다시 로드합니다.
web.xml 샘플
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- General Description of the web application -->
<display-name>Workshop Continue</display-name>
<description>We shall continue our e-bookstore...</description>
<!-- Context initialization parameters -->
<!-- Provide the database related parameters -->
<context-param>
<param-name>jdbcDriver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>databaseUrl</param-name>
<param-value>jdbc:mysql://localhost/eshop</param-value>
</context-param>
<!-- Define servlets -->
<servlet>
<servlet-name>BookQuery</servlet-name>
<servlet-class>BookQueryServlet</servlet-class>
<init-param>
<param-name>popularAuthor</param-name>
<param-value>Kelvin Jones</param-value>
</init-param>
</servlet>
<!-- Define servlet's URL mapping -->
<servlet-mapping>
<servlet-name>BookQuery</servlet-name>
<url-pattern>/query</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<mime-mapping>
<extension>pdf</extension>
<mime-type>application/pdf</mime-type>
</mime-mapping>
<!-- For directory request -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
</web-app>
Servlet Deployment Descriptor
서블릿을 배포하려면, 일치하는 <servlet-name>을 가진 <servlet> 및 <servlet-mapping> 요소 한 쌍을 작성해야 합니다. <servlet-class>는 서블릿 클래스의 완전한 이름을 지정합니다. <url-pattern>은 URL을 지정합니다. 예를 들어, 다음과 같습니다.
<web-app ...>
<servlet>
<servlet-name>ServletName</servlet-name>
<servlet-class>mypkg.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletName</servlet-name>
<url-pattern>/MyURL</url-pattern>
</servlet-mapping>
</web-app>
위 web.xml에 적용된 URL은 http://hostname:port/WebContext/MyURL 가 됩니다.
<url-pattern>에서 패턴 매칭을 위해 와일드카드 '*'을 사용할 수 있습니다. 예를 들어, /MyURL.*는 /MyURL.html 및 기타와 일치합니다. /MyURL/*은 /MyURL/test 및 기타와 일치합니다.
서블릿에는 항상 사용자 정의 URL을 사용하는 것이 좋습니다. 이렇게 하면 짧고 의미 있는 URL을 선택하고 초기화 매개변수, 필터, 보안 설정을 배포 디스크립터에 포함시킬 수 있습니다.
Servlet Initialization Parameters
"web.xml"에서 name-value 쌍의 형태로 초기화 파라미터를 특정 서블릿으로 전달할 수 있습니다. 예를 들어, 다음과 같이 할 수 있습니다.
<web-app ...>
<servlet>
<servlet-name>ServletName</servlet-name>
<servlet-class>mypkg.MyServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>listing</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ServletName</servlet-name>
<url-pattern>/MyURL</url-pattern>
</servlet-mapping>
</web-app>
서블릿 내부에서는 ServletConfig 객체를 통해 초기화 파라미터를 검색할 수 있습니다.
package mypkg;
public class MyServlet extends HttpServlet {
private boolean debug = false, listing = false;
@Override
public void init() {
ServletConfig config = getServletConfig();
String strDebug = config.getInitParameter("debug");
if (strDebug.equals("true")) debug = true;
String strListing = config.getInitParameter("listing");
if (strListing.equals("true")) listing = true;
}
......
}
Application Initialization Parameters
웹 애플리케이션의 "WEB-INF\web.xml"에 지정되며, 이 웹 애플리케이션 하에서 모든 서블릿에서 사용할 수 있습니다. 초기화 파라미터를 검색하려면 ServletContext 객체의 getInitParameter() 메서드를 사용할 수 있습니다.
<web-app ......>
<context-param>
<param-name>email</param-name>
<param-value>query@abcde.com</param-value>
</context-param>
......
</web-app>
Server-wide Initialization Parameters
이는 애플리케이션 초기화 파라미터와 유사하지만 전역 "<CATALINA_HOME>\conf\web.xml"에 정의됩니다.
<context-param>
<param-name>email</param-name>
<param-value>query@abcde.com</param-value>
</context-param>
Welcome Page
웹 context root에 대한 요청에 표시할 페이지를 지정합니다. 예를 들어, 다음과 같습니다.
<web-app ...>
......
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
<welcome-file>test/index.html</welcome-file>
</welcome-file-list>
</web-app>
Dispatcher Servlet
디스패처 서블릿(Dispatcher Servlet)은 주로 스프링 프레임워크의 웹 애플리케이션 개발에서 사용되는 중앙 집중식 서블릿입니다. 이 서블릿은 웹 애플리케이션에서 들어오는 모든 요청을 받아 해당 요청을 처리할 컨트롤러로 전달(디스패치)하는 역할을 합니다. 디스패처 서블릿을 통해 모든 요청의 진입점을 하나로 통합하여, 요청에 따른 처리 과정을 효율적으로 관리할 수 있습니다.
디스패처 서블릿은 스프링 MVC(모델-뷰-컨트롤러) 패턴의 핵심 구성 요소 중 하나로, 다음과 같은 주요 기능을 수행합니다:
1. Http Request 매핑: 들어오는 요청의 URL을 분석하여 해당 요청을 처리할 컨트롤러를 찾습니다. 컨트롤러 내의 메소드와 요청 URL 간의 매핑 정보는 주로 @RequestMapping 어노테이션을 통해 정의됩니다.
2. Request 처리 위임: 매핑된 컨트롤러가 요청을 처리하도록 합니다. 컨트롤러는 비즈니스 로직을 수행하고, 결과 데이터와 함께 응답으로 사용될 뷰를 반환합니다.
3. 뷰 리졸빙: 컨트롤러가 반환한 뷰 이름을 바탕으로 실제 뷰 객체를 찾아낼 수 있도록 해줍니다. 뷰 리졸버(View Resolver)를 사용하여 뷰 이름을 실제 뷰 경로로 변환합니다.
4. 응답 생성: 최종적으로 뷰와 모델 데이터를 결합하여 사용자에게 보여질 최종 웹 페이지를 생성합니다. HTML, JSON, XML 등 다양한 형태의 응답을 생성할 수 있습니다.
디스패처 서블릿을 사용함으로써 웹 애플리케이션의 요청 처리 과정을 일관되고 체계적으로 관리할 수 있으며, 스프링 프레임워크의 다양한 기능들과 원활하게 통합하여 사용할 수 있습니다. 스프링의 설정 파일(web.xml 또는 Java 기반 설정)에서 디스패처 서블릿을 등록하고, URL 패턴을 통해 모든 요청을 이 서블릿으로 전달하도록 구성합니다.
Spring MVC가 request을 받아 response을 반환할 때까지의 처리 흐름은 다음 다이어그램에 나타나 있습니다.
1. DispatcherServlet이 요청을 받습니다.
2. DispatcherServlet이 적절한 컨트롤러를 선택하는 작업을 HandlerMapping에게 위임합니다. HandlerMapping은 들어오는 요청 URL에 매핑된 컨트롤러를 선택하고 선택된 핸들러와 컨트롤러를 DispatcherServlet에게 반환합니다.
3. DispatcherServlet이 컨트롤러의 비즈니스 로직 실행 작업을 HandlerAdapter에게 위임합니다.
4. HandlerAdapter가 컨트롤러의 비즈니스 로직 처리를 호출합니다.
5. 컨트롤러가 비즈니스 로직을 실행하고, 처리 결과를 모델에 설정한 후 뷰의 논리적 이름을 HandlerAdapter에게 반환합니다.
6. DispatcherServlet이 뷰 이름에 해당하는 뷰를 해결하는 작업을 ViewResolver에게 위임합니다. ViewResolver는 뷰 이름에 매핑된 뷰를 반환합니다.
7. DispatcherServlet이 반환된 뷰에 렌더링 프로세스를 위임합니다.
8. 뷰가 모델 데이터를 렌더링하고 응답을 반환합니다.
더 상세한 처리 과정입니다.
-
doService 메소드에서부터 웹요청의 처리가 시작됩니다. DispatcherServlet에서 사용되는 몇몇 정보를 request 객체에 담는 작업을 한 후 doDispatch 메소드를 호출합니다.
-
아래 3번~13번 작업은 doDispatch 메소드안에 있습니다. Controller, View 등의 컴포넌트들을 이용한 실제적인 웹요청처리가 수행됩니다.
-
getHandler 메소드는 RequestMapping 객체를 이용해서 요청에 해당하는 Controller를 얻게 됩니다.
-
요청에 해당하는 Handler를 찾았다면 Handler를 HandlerExecutionChain 객체에 담아 리턴하는데, 이때 HandlerExecutionChain는 요청에 해당하는 interceptor들이 있다면 함께 담아 리턴합니다.
-
실행될 interceptor들이 있다면 interceptor의 preHandle 메소드를 차례로 실행합니다.
-
Controller의 인스턴스는 HandlerExecutionChain의 getHandler 메소드를 이용해서 얻습니다.
-
HandlerMapping과 마찬가지로 여러개의 HanlderAdaptor를 설정할 수 있는데, getHandlerAdaptor 메소드는 Controller에 적절한 HanlderAdaptor 하나를 리턴합니다.
-
선택된 HanlderAdaptor의 handle 메소드가 실행되는데, 실제 실행은 파라미터로 넘겨 받은 Controller를 실행합니다.
-
계층형 Controller인 경우는 handleRequest 메소드가 실행됩니다. @Controller인 경우는 HanlderAdaptor(AnnotationMethodHandlerAdapter)가 HandlerMethodInvoker를 이용해 실행할 Controller의 메소드를 invoke()합니다.
-
interceptor의 postHandle 메소드가 실행됩니다.
-
resolveViewName 메소드는 논리적 뷰 이름을 가지고 해당 View 객체를 반환합니다.
-
Model 객체의 데이터를 보여주기 위해 해당 View 객체의 render 메소드가 수행됩니다.
web.xml에 DispatcherServlet 설정하기
Spring MVC Framework을 사용하기 위해서는 web.xml에 DispatcherServlet을 설정하고, DispatcherServlet이 WebApplicationContext를 생성할수 있도록 빈(Bean) 정보가 있는 파일들도 설정해주어야 합니다.
기본 설정
<web-app>
<!-- easycompnay라는 웹어플리케이션의 웹요청을 DispatcherServlet이 처리한다.-->
<servlet>
<servlet-name>easycompany</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
</web-app>
servlet-name은 DispatcherServlet이 기본(default)으로 참조할 빈 설정 파일 이름의 prefix가 되는데, (servlet-name)-servlet.xml 같은 형태입니다.
위 예제와 같이 web.xml을 작성했다면 DispatcherServlet은 기본으로 /WEB-INF/easycompany-servlet.xml을 찾게 됩니다.
contextConfigLocation을 이용한 설정
빈 설정 파일을 하나 이상을 사용하거나, 파일 이름과 경로를 직접 지정해주고 싶다면 contextConfigLocation 라는 초기화 파라미터 값에 빈 설정 파일 경로를 설정합니다.
...
<servlet>
<servlet-name>easycompany</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/easycompany-web.xml
</param-value>
</init-param>
</servlet>
ServletContextListener
ServletContextListener는 Java Servlet API의 일부로, 웹 애플리케이션의 생명주기 이벤트를 감지하는 리스너 인터페이스입니다. 이 리스너를 사용하여 애플리케이션의 시작과 종료 시점에 특정 작업을 수행할 수 있습니다. ServletContextListener는 웹 애플리케이션이 컨텍스트를 초기화하거나 종료할 때 발생하는 이벤트에 반응하여, 리소스를 할당하거나 해제하는 등의 초기화 및 정리 작업을 실행하는 데 유용합니다.
주요 메소드: ServletContextListener 인터페이스는 두 가지 주요 메소드를 정의합니다.
1. contextInitialized(ServletContextEvent sce): 이 메소드는 웹 애플리케이션의 ServletContext가 초기화될 때 호출됩니다. 즉, 웹 애플리케이션이 시작될 때 실행되어 필요한 초기 설정이나 리소스 로딩 등을 수행할 수 있습니다.
2. contextDestroyed(ServletContextEvent sce): 이 메소드는 웹 애플리케이션의 ServletContext가 종료되거나 웹 서버에서 언로드될 때 호출됩니다. 애플리케이션이 종료될 때 필요한 정리 작업, 열려 있는 리소스의 해제 등을 수행할 수 있습니다.
사용 방법:
ServletContextListener를 사용하기 위해서는 먼저 리스너 클래스를 정의하고, 이를 web.xml 파일이나 애너테이션을 통해 웹 애플리케이션에 등록해야 합니다.
web.xml을 통한 등록 예시:
<web-app ...>
<listener>
<listener-class>com.example.MyServletContextListener</listener-class>
</listener>
</web-app>
어노테이션을 통한 등록 예시:
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 초기화 코드
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 정리 코드
}
}
이러한 방법으로 등록된 리스너는 웹 애플리케이션의 생명주기에 따라 자동으로 호출되며, 개발자는 이를 통해 애플리케이션의 시작과 종료 시점에 필요한 작업을 정의할 수 있습니다. ServletContextListener는 애플리케이션의 전반적인 상태를 관리하고, 전역 리소스의 초기화 및 정리에 특히 유용합니다.
ContextLoaderListener
ContextLoaderListener는 Spring Framework의 웹 애플리케이션 지원의 일부로, 웹 애플리케이션의 생명주기 동안 Spring 애플리케이션 컨텍스트를 초기화하고 관리하는 역할을 하는 리스너입니다. ContextLoaderListener는 javax.servlet.ServletContextListener를 구현하여 웹 애플리케이션의 시작과 종료 시점에 특정 이벤트를 감지하고, 이에 대응하는 액션을 수행합니다.
기능 및 작동 원리
- 애플리케이션 컨텍스트 초기화: 웹 애플리케이션이 시작될 때, ContextLoaderListener는 Spring의 루트 웹 애플리케이션 컨텍스트를 초기화합니다. 이 컨텍스트는 애플리케이션의 모든 서블릿과 필터에 공통적인 빈(Bean)을 제공합니다.
- 루트 컨텍스트와 서블릿 컨텍스트 분리: ContextLoaderListener는 루트 애플리케이션 컨텍스트(주로 서비스 계층, 리포지토리 등)를 관리하며, DispatcherServlet과 같은 서블릿 기반의 서블릿 컨텍스트는 DispatcherServlet에 의해 관리됩니다. 이를 통해 루트와 서블릿 계층을 분리하고 모듈화된 구성을 지원합니다.
- 컨텍스트 파라미터 사용: 이 리스너는 web.xml에 정의된 contextConfigLocation 컨텍스트 초기화 파라미터를 사용하여, Spring 구성 파일의 위치를 찾습니다. 이 구성 파일(예: applicationContext.xml)은 애플리케이션의 빈 정의와 설정을 포함합니다.
- 자원 정리: 웹 애플리케이션이 종료될 때, ContextLoaderListener는 초기화된 애플리케이션 컨텍스트를 닫고, 사용된 모든 자원을 정리합니다.
설정 및 사용
ContextLoaderListener를 사용하기 위해 web.xml 파일에 리스너를 등록하고, 필요에 따라 Spring 구성 파일의 위치를 지정할 수 있습니다.
web.xml에서의 설정 예:
<web-app ...>
<!-- Spring 구성 파일 지정 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- ContextLoaderListener 등록 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
이 설정은 ContextLoaderListener가 지정된 Spring 구성 파일(/WEB-INF/spring/root-context.xml)을 사용하여 애플리케이션 컨텍스트를 초기화하도록 합니다.
주요 이점
- 애플리케이션 레벨의 빈 관리: ContextLoaderListener는 애플리케이션 전체에서 사용될 공통 빈을 관리하는 데 중요한 역할을 합니다.
- 모듈성 및 계층화: 루트 컨텍스트와 서블릿-특정 컨텍스트 간의 계층 구조를 통해, 애플리케이션의 구성을 모듈화하고 관리하기 쉽게 만듭니다.
- 통합 지원: 다양한 Spring 프로젝트(예: Spring Security, Spring Data 등)와의 통합을 용이하게 합니다.
ContextLoaderListener는 Spring 기반 웹 애플리케이션의 설정과 부트스트래핑 과정에서 핵심적인 역할을 하며 Spring 컨테이너의 생명주기와 웹 애플리케이션의 생명주기를 효과적으로 관리합니다.
※ Spring Boot에서는 내장형 톰캣(Embedded Tomcat) 서버와 같은 내장형 서블릿 컨테이너를 사용할 때, 전통적인 Spring MVC 애플리케이션에서 ContextLoaderListener를 사용하는 것과는 다른 접근 방식을 취합니다. Spring Boot는 자동 구성(auto-configuration) 메커니즘을 통해 대부분의 설정을 자동으로 처리하므로, 개발자가 명시적으로 ContextLoaderListener를 등록할 필요가 없습니다.
Debugging Point:
package org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext 클래스의
prepareWebApplicationContext 메서드와 postProcessBeanFactory 메서드
내장형 톰캣 서버를 사용하는 Spring Boot에서의 ContextLoaderListener
전통적인 Spring MVC 웹 애플리케이션에서는 web.xml 파일이나 WebApplicationInitializer 구현을 통해 ContextLoaderListener를 사용하여 Root WebApplicationContext를 초기화합니다. 하지만 Spring Boot에서는 이러한 설정이 자동으로 이루어지므로, ContextLoaderListener를 명시적으로 사용할 필요가 없습니다.
Spring Boot는 내부적으로 ServletWebServerApplicationContext라는 특별한 유형의 ApplicationContext를 사용합니다. 이 컨텍스트는 내장형 서버 설정과 함께 Spring MVC와 Spring Boot의 자동 구성을 처리합니다.
아래처럼, 최초 Http Request를 톰캣 서버에 전송하면,
DispatcherServlet의 스프링 IoC 컨테이너가 생성되고, dispatcherservlet 이름의 서블릿이 생성된다.
요약
따라서, Spring Boot에서 내장형 톰캣 서버를 사용하는 경우, 개발자가 직접 ContextLoaderListener를 구성하거나 사용할 필요가 없습니다. Spring Boot의 자동 구성 기능이 이를 대신 처리하며, 필요한 모든 컨텍스트 초기화와 구성을 자동으로 수행합니다.
디스패처서블릿(DispatcherServlet)은 실제로 Servlet 인터페이스를 구현한 구체 클래스입니다. 스프링 프레임워크에서 제공하는 DispatcherServlet 클래스는 HttpServlet 클래스를 상속받아 구현되어 있습니다. HttpServlet 클래스는 Java Servlet API의 일부로서, HTTP 프로토콜을 사용하는 웹 애플리케이션을 개발하기 위한 기본 클래스입니다. 이는 다시 GenericServlet 클래스를 확장하며, GenericServlet은 Servlet 인터페이스를 구현합니다.
스프링의 DispatcherServlet은 Servlet API에 정의된 init(), service(), destroy()와 같은 생명주기 메소드들을 내부적으로 관리하며, 스프링 MVC 프레임워크의 구성 요소들과 통합하여 웹 애플리케이션의 요청 처리 흐름을 제어합니다. 이를 통해 개발자는 스프링의 강력한 기능들을 활용하여 효율적이고 유연한 웹 애플리케이션을 구축할 수 있습니다.
스프링 프레임워크에서 DispatcherServlet은 스프링 IoC(Inversion of Control) 컨테이너에서 하나의 빈(bean)으로 등록됩니다. DispatcherServlet은 웹 애플리케이션의 프론트 컨트롤러로서, 스프링 애플리케이션 컨텍스트의 일부가 되며, 스프링 IoC 컨테이너로부터 구성 요소들을 주입받아 사용합니다.
스프링 웹 애플리케이션에서 DispatcherServlet가 초기화될 때, 이는 자신만의 웹 스프링 애플리케이션 컨텍스트를 생성합니다. 이 컨텍스트는 웹 애플리케이션에 특화된 빈들을 포함하고 있으며, 주로 웹 요청을 처리하는 데 필요한 컨트롤러, 뷰 리졸버, 핸들러 매핑 등의 빈들이 여기에 포함됩니다.
DispatcherServlet의 "Servlet" WebApplicationContext는 일반적으로 "Root" WebApplicationContext의 자식 컨텍스트로 동작합니다. 루트 웹 애플리케이션 컨텍스트는 애플리케이션 전반에 걸쳐 사용되는 서비스, 리포지토리 등의 빈들을 포함하고, 이는 웹 계층 외의 다른 계층에서도 사용될 수 있습니다. 반면, DispatcherServlet의 서블릿 웹 애플리케이션 컨텍스트는 주로 웹 계층과 관련된 빈들을 관리합니다.
DispatcherServlet을 스프링 IoC 컨테이너에 등록하는 것은 보통 web.xml 파일 또는 스프링의 Java 기반 설정을 통해 이루어집니다. DispatcherServlet의 빈 정의는 내부적으로 필요한 여러 구성 요소들과 함께 스프링 애플리케이션 컨텍스트에 추가됩니다. 이러한 방식으로 DispatcherServlet은 스프링 IoC 컨테이너의 관리 하에 설정되고, 실행됩니다.
'Spring Framework > Web on Servlet Stack' 카테고리의 다른 글
@RequestHeader (0) | 2024.10.09 |
---|---|
Annotated Controllers[1] (1) | 2024.10.06 |
Filter (0) | 2023.05.19 |
Dispatcher Servlet (0) | 2023.05.02 |
WebDataBinder (0) | 2023.05.01 |