* 개요
웹 자바 프로그래밍을 접하면, Servlet의 개념이 가장 기초가 됩니다. Servlet 자체가 웹 어플리케이션을 만들기 위해서 나온 기술이기 때문입니다. 하지만, 제대로 개념을 정리하거나 명쾌하게 답할 수가 없었습니다. 이 글을 통해서 개념을 정리해보겠습니다.
* Servlet 소개
Servlet은 문맥에 따라서 여러 의미를 가집니다.
- 웹 어플리케이션을 만들기 위해 Http 요청, 응답을 지원하는 기술입니다.
- 동적 웹 페이지를 만들기 위해서 서버에 배포되는 컴포넌트입니다.
- Web Application Context(WAS)(tomcat, jeus, weblogic)에서 기동되는 자바 클래스입니다. 참고로, WAS는 서블릿을 관리하기 위해 서블릿 컨테이너를 가지고 있습니다.
- 각종 인터페이스와 클래스를 제공하는 API로 서비스를 제공하기 위해 필수로 구현되어야 합니다.
- 서버에서 객체 생성 이후에 멀티 쓰레드로 동작합니다.
- 자바 기반이므로 이식성이 좋습니다.
- JSP는 자바 서블릿으로 변환되어 WAS에서 멀티 쓰레드로 동작합니다.
웹 어플리케이션이란 웹에서 접근 가능한 어플리케이션입니다. 웹 어플리케이션은 서블릿, JSP, Filter 등등의 웹 컴포넌트와 HTML, CSS, JavaScript같은 다른 요소들로 이루어져 있습니다. 웹 컴포넌트는 보통 웹 서버에서 실행되고 HTTP 요청에 응답합니다.
* Servlet vs CGI
CGI 기술은 웹 서버가 외부 프로그램을 호출해 HTTP 요청을 처리하도록 하며, 요청이 올때마다 새로운 프로세스를 할당합니다.
하지만 CGI의 단점이 있는데, 사용자가 증가하면 응답하기 위한 시간이 길어집니다. 또한, 요청마다 프로세스를 할당하기 때문에 웹 서버는 프로세스 작업에 제한이 많습니다. C, C++, perl 같은 플랫폼 종족적인 언어를 사용합니다.
Servlet의 장점은, 웹 컨테이너가 서블릿에 여러 요청을 관리하기 위해 쓰레드를 사용합니다.
쓰레드는 가볍고, 공통 메모리 영역을 공유하고, 쓰레드간에 통신비용이 적기 때문에 프로세스에 비해 유리합니다.
즉, 성능이 뛰어나고 자바 언어를 사용하며 JVM으로 관리가 되기 때문에 메모리누수, 가비지 컬렉션 등을 자동으로 처리해줍니다.
* Servlet interface
javax.servlet과 javax.servlet.http 패키지가 서블릿 API의 인터페이스와 클래스를 대표합니다.
javax.servlet 패키지는 많은 서블릿에 의해 사용되는 많은 인터페이스와 클래스를 포함합니다.
javax.servlet.http 패키지는 http 요청을 처리하는 인터페이스와 클래스를 포함합니다.
* Servlet 인터페이스의 5가지 메서드
Method | Description |
Method Description public void init(ServletConfig config) | 서블릿을 초기화한다.웹 컨테이너에 의해 1번만 호출된다 |
public void service(ServletRequest request,ServletResponse response) | 들어 온 요청을 처리한다. 요청시마다 웹 컨테이너가 의해 호출된다 |
public void destroy() | 한번만 호출되며, 서블릿이 파괴되었다는 뜻이다 |
public ServletConfig getServletConfig() | ServletConfig 객체를 반환한다 |
public String getServletInfo() |
writer, copyright, version 등 서블릿 정보를 반환한다 |
* 예시
import java.io.*;
import javax.servlet.*;
public class First implements Servlet{
ServletConfig config=null;
public void init(ServletConfig config){
this.config=config;
System.out.println("servlet is initialized");
}
public void service(ServletRequest req,ServletResponse res)
throws IOException,ServletException{
res.setContentType("text/html");
PrintWriter out=res.getWriter();
out.print("<html><body>");
out.print("<b>hello simple servlet</b>");
out.print("</body></html>");
}
public void destroy(){System.out.println("servlet is destroyed");}
public ServletConfig getServletConfig(){return config;}
public String getServletInfo(){return "copyright 2007-1010";}
}
GenericServlet 클래스는 Servlet, ServletConfig, Serializable 인터페이스를 구현합니다. service 메서드를 제외하고는 이 인터페이스들의 모든 메서드의 구현을 제공합니다.
HttpServlet 클래스는 GenericServlet 클래스를 상속하고 Serializable 인터페이스를 구현합니다. doGet, doPost, doHead, doTrace와 같은 http 특화된 메서드를 제공합니다.
대략 구조가 다음과 같습니다.
* Servlet의 라이프 사이클
웹 컨테이너는 서블릿 객체의 생명주기를 관리합니다.
서블릿은 3가지 상태가 있습니다. new, ready, end. 서블릿은 서블릿 객체가 생성되면, new 상태가 됩니다. init() 메서드라 호출되고나면, 서블릿은 ready 상태가 됩니다. 이 상태에서 service()의 모든 작업을 수행합니다. 웹 컨테이너가 destory() 메서드를 호출하면, end 상태로 전환됩니다.
1. 서블릿 클래스가 로드됩니다.
클래스로더는 서블릿 클래스를 로드하는 역할을 합니다. 서블릿 클래스는 웹 컨테이너에 의해 서블릿의 첫 요청을 받았을 때 로드됩니다.
2. 서블릿 객체가 생성됩니다.
웹 컨테이너는 서블릿 클래스가 로드되고 난 후에 서블릿 객체를 생성합니다. 서블릿 객체는 서블릿 생명주기에서 딱 한번만 생성됩니다.
3. init 메서드가 호출됩니다.
웹 컨테이너는 서블릿 객체가 생성되고 나면 딱 한번 init 메서드를 호출합니다. init 메서드는 서블릿을 초기화하기 위해 사용됩니다. 이후 서블릿이 메모리에 로드됩니다. java.servlet.Servlet 인터페이스의 생명주기 메서드입니다.
public void init(ServletConfig config) throws ServletException
4. service 메서드가 호출됩니다.
웹 컨테이너는 서블릿 요청을 수신할 때마다 service 메서드를 호출합니다. 서블릿이 초기화되어 있지 않다면, 먼저 위의 3가지 순서를 실행합니다. 서블릿은 한번만 초기화된다는 것에 유의합니다.
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException
5. destory 메서드가 호출됩니다
웹 컨테이너는 service로부터 서블릿 인스턴스를 제거하기 전에 destory 메서드를 호출합니다. 메모리나 쓰레드 자원을 비우기 위함입니다.
public void destroy()
* Servlet이 작동하는 Web Server & Servlet Container 역사
* Web Server
웹서버는 HTTP 프로토콜을 사용해서 데이터를 전송합니다. 간단하게, 사용자는 URL을 입력하고, 웹 페이지를 반환받습니다. 따라서, 서버가 하는 일은 웹 페이지를 사용자에게 전달해주는 것입니다. 변환은 요청 및 응답 메시지의 형식을 지정하는 HTTP 프로토콜에 있습니다.
*Servlet Container
웹 컨테이너와 동의어로 사용됩니다.
서블릿 컨텍스트는 JSP와 Servlet을 만들고 실행하는 규칙과 구현, EJB(Enterprise JavaBeans)의 분산 컴포넌트, 웹 서비스 규칙 등을 추가로 가지고 있습니다.
사용자는 단순히 서버로부터 정적이 페이지만 요청할 수 있었지만, 사용자는 입력 값에 따른 웹 페이지를 확인하고 싶었습니다.따라서, 서블릿 컨테이너를 통해 자바를 이용해서 서버쪽에서 웹페이지를 동적으로 생성하도록 발전했습니다. 따라서 서블릿 컨테이너는 서블릿을 상호작용 시키는 웹 서버의 일부분 입니다. 서블릿 컨테이너는 웹 서버의 한 부분으로 분리되어 프로세스로 실행될 수 있습니다.
서블릿 컨테이너의 역할은 다음과 같습니다.
1. 서블릿 생명주기 관리
서블릿 컨테이너는 클라이언트 요청에 따라 서블릿 클래스를 로드하고, 인스턴스화하고, 초기화 및 요청 작업 처리와 반환 및 삭제하는 생명주기를 관리합니다.
2. 멀티 쓰레드 지원
서블릿 컨테이너는 서블릿 요청이 들어오면 쓰레드를 단위로 작업을 합니다. 동시여 여러 요청이 들어오면, 멀티 스레딩 환경에서 동시다발적으로 일을 처리할 수 있습니다. 한번 메모리에 올라간 스레드는 다시 생성할 필요가 없기 때문에 효율적입니다.
3. 보안 지원
서블릿 컨테이너는 보안 관련 기능을 제공합니다. 그렇기 때문에 자바 코드 안에 따로 보안 관련 코드를 만들 필요가 없습니다. 대부분 보안 이슈는 XML 배포 서술자에 기록합니다.
Apache Tomcat, Jetty 등의 컨테이너 관점에서 살펴보겠습니다. 맨 처음에, 컨테이너는 ServletContext 객체를 생성합니다. ServletContext의 역할은 서버 또는 컨테이너의 메모리로 작동하고 web.xml에 정의된대로 웹 어플리케이션과 관련한 모든 서블릿, 필터, 리스너들을 기억하는 것입니다. 컨테이너를 종료할때까지 ServletContext는 그 상태를 유지합니다.
서블릿의 load-on-startup 매개변수는 중요한 역할을 합니다. 이 매개변수 값이 0보다 크다면, 서버가 시작 시에 초기화 됩니다. 매개변수가 명시되지 않는다면, 맨 처음 요청이 왔을 때 서블릿의 init() 메서드가 호출됩니다.
* Servlet 실행 환경 및 개발환경
- 실행환경
1. Java Runtime Environment
- JDK 또는 JRE가 설치된 서버
2. Web Server + Servlet Container(Servlet Engine) = WAS(Web Application Server)
ex) Tomcat, Application Server(Jeus, WebLogic, JBoss)
- 개발환경
1. Java EE API
- 컴파일 시 필요합니다.
- 임포트 필요 : javax.servlet.*, javax.http.*
다시 정리하자면, 초기에는 정적페이지만 반환하는 Web Server를 사용했습니다. 하지만 동적 페이지를 사용하기 위해서 서블릿이 등장하였고, 이는 Web Application Server(WAS)에서 실행되는 서블릿 컨테이너에서 관리가 됩니다.
* Servlet 처리 과정
웹 컨테이너는 서블릿 요청을 다음과 같이 처리합니다.
1. web.xml 파일에 있는 서블릿 요청을 매핑합니다.
2. 요청에 대한 요청, 응답 객체를 생성합니다.
3. 쓰레드에 있는 service 메서드를 호출합니다
4. public service 메서드가 내부에 있는 protected service 메서드를 호출합니다
5. protected service 메서드는 요청에 따라서 doGet 메서드를 호출합니다
6. doGet 메서드는 응답을 생성하고 클라이언트에게 전달합니다
7. 응답을 보낸 후, 웹 컨테이너는 요청과 응답 객체를 삭제합니다.
쓰레드는 쓰레드풀에 있거나 구현에 따라서 삭제됩니다.
* public service 메서드는 어떻게 정의되어 있을까요?
public service 메서드는 ServletRequest, ServletResponse 객체를 각각 HttpServletRequest, HttpServletResponse 객체로 전환합니다. 그리고나서, 이 객체들을 통과하는 service 메서드를 호출합니다.
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try
{
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
}
catch(ClassCastException e)
{
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
* protected service 메서드는 어떻게 구성되어 있을까요?
protected service 메서드는 request의 종류를 확인하여, get인 경우 doGet을, post인 경우 doPost 메서드를 호출합니다.
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if(method.equals("GET"))
{
long lastModified = getLastModified(req);
if(lastModified == -1L)
{
doGet(req, resp);
}
....
//rest of the code
}
}
* JVM의 역할
서블릿을 사용한다는 것은 JVM이 자바 스레드로 각각 요청을 처리한다는 것과 같습니다. 이것이 서블릿 컨테이너의 주요한 장점 중 하나입니다. 각각 서블릿은 HTTP 요청에 응답하는 특별한 요소를 가진 자바 클래스입니다. 서블릿 컨테이너의 주요 특징은 작업을 처리하기 위해서 요청을 알맞은 서블릿으로 매핑시키는 것입니다. 그리고 JVM이 그것들을 모두 처리하고 나면, 동적으로 생성된 결과를 알맞은 경로로 반환합니다. 대부분의 경우, 서블릿 컨테이너는 하나의 JVM에서 실행되지만, 컨테이너가 여러개의 JVM이 필요하다면 여러가지 방법이 있습니다.
* Servlet 등록하기
<web-app>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>servlets.WelcomeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>welcome</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
</web-app>
<servlet>...</servlet> 에는 서블릿 이름과 서블릿 파일정보를 기록합니다.
<servlet-mapping>...</servlet-mapping> 에는 요청온 url 정보와 그에 맞는 서블릿 이름을 기록합니다.
<url-pattern>/welcome</url-pattern> 은, /welcome 요청이 올 때를 위한 매핑정보입니다.
<servlet-name>welcome</servlet-name>은 /welcome 요청이 올 때 welcome이라는 서블릿 이름의 파일로 이동시킵니다.
<servlet-name>welcome</servlet-name>은 <servlet-mapping> 에서, welcome이라고 이름이 붙여진 서블릿 매핑 정보를 식별합니다. 이 경우, 해당 요청을 처리하기 위해 <servlet-class>servlets.WelcomeServlet</servlet-class> 클래스로 이동시킵니다.
자바 어노테이션 @WebServlet을 통해서 한번에 설정할 수도 있습니다.
@WebServlet("/welcome")
public class welcome extends HttpServlet {
protected service(HttpServletRequet req, HttpServletResponse res)
throws ServletException, IOException {
response.setContentType("text/html; charset=utf-8");
response.getWriter().println("Servlet at: " + req.getContextPath());
response.getWriter().println("hello servlet");
}
}
* ServletContext
서블릿 컨텍스트는 웹 어플리케이션 내에 있는 모든 서블릿들을 관리하며 정보를 공유할 수 있게 도와주는 역할을 합니다.
하나의 웹 어플리케이션에는 하나의 서블릿 컨테스트가 존재하고 생성됩니다. 이 안에는 웹 어플리케이션에서 공유하는 공유자원들이 있는데 여러 서블릿들이 공유하여 사용합니다.
서블릿 컨텍스트 클래스는 서블릿이 서블릿 컨테이너와 통신하기 위해 사용되어지는 메서드들을 가지고 있습니다. 예를 들어, 파일의 MIME 타입을 조회하고, 요청을 분기처리하고 로그파일을 작성합니다.
ServletConfig 객체는 서블릿 초기화에 필요한 정보를 전달하기 위한 객체이고 각 서블릿마다 가지고 있습니다.
사진에서처럼, Servlet A,B,C 등 여러개의 서블릿이 있는데 이는 Servlet Config라는 설정정보가 있고 모두 서블릿 컨텍스트에서 공유가 됩니다. WAS는 서블릿을 생성하기 전에 ServletContext클래스를 먼저 생성하고, ServletContextListener를 생성한 후 컨텍스트 초기화 이벤트인 contextinitialized() 함수를 실행합니다.
ServletContext는 ServletConfig의 getServletContext() 사용하여 얻습니다. 서블릿은 HttpServlet을 상속하고
HttpServlet은 ServletConfig를 구현하고 있기 때문에 getServletContext() 메서드를 바로 이용 할 수 있습니다.
* ServletContextListener
- 스프링에 ContextLoaderListener가 있다면, 서블릿에는 ServletContextListener가 있습니다. 웹사이트가 시작되는 경우 어떤 일을 하고자 할 때 ServletContextListener 인터페이스를 구현하면 됩니다.
contextDestoryed() 메서드는 더이상 웹 요청을 받을 수 없을 때, 즉 톰캣 등이 종료될 때 호출되는 이벤트이고, contextInitialized() 메서드는 톰캣 등이 시작됨으로서 컨테스트 요청을 받아들일 수 있을 때 호출되는 이벤트입니다.
결국 웹 어플리케이션 시작, 웹 어플리케이션 종료 이벤트 때 어떤 일을 하고자 할 때 사용하면 됩니다.
*ServletContextListener 실습하기
@WebListener //@WebServletContextListener
public class MyListener implements ServletContextListener {
private ServletContext ctx = null;
public void contextDestoryed(ServletContextEvent sce) {
Sytstem.out.println(">>>>>>>> Context Destroyed....");
}
public void contextInitialized(ServletContextEvent sec) {
ctx = sce.getServletContext();
Sytstem.out.println("Context Name >>>> " + ctx.getServletContextName());
Sytstem.out.println(">>>>>>>> Context Init....");
}
}
톰캣을 실행하면 contextInitialized() 메서드가 생성됩니다. 톰캣을 바로 종료하면 contextDestoryed() 메서드를 확인할 수 없기 때문에, 코드를 고치고 reload가 되도록 하면, contextDestoryed()로 종료되고 다시 contextInitialized() 메서드가 호출되는 것을 확인 할 수 있습니다.
* 참고
https://codevang.tistory.com/191
https://jordy-torvalds.tistory.com/14
https://www.youtube.com/watch?v=vpdwXEhOzvw&list=PLxU-iZCqT52D74tqQ2mviOmA-lrS-z85z
https://coding-factory.tistory.com/742
https://gmlwjd9405.github.io/2018/10/29/web-application-structure.html
'Spring' 카테고리의 다른 글
@Controller, @Service, @Repository 차이 (0) | 2022.08.25 |
---|---|
DI(Dependency Injection) (0) | 2022.07.14 |
ApplicationContext (0) | 2022.07.08 |
Spring framework 로그(log) 알아보기 (1) | 2021.12.16 |
네이버 지역검색 API를 활용한 맛집 List 제작 - (4) (0) | 2021.08.29 |