본문 바로가기
Java/Spring | SpringBoot

[Servlet, JSP and Spring] Servlet & Servlet container

by wiggleji 2022. 11. 14.

 

시리즈 목차

- Servlet & JSP

- Servlet & Servlet container

- 그리고 Spring

 


제주 보롬왓

 

지난 글에 이어, 이번 글에서는 Servlet, JSP 그리고 Spring 의 관계를 정리해본다.

이번 글과 다음 글의 내용은 매우 길 것으로 예상된다.

 

지난 글의 내용을 한 문장으로 정리하면,

Servlet과 JSP는 동적처리가 가능한 웹페이지를 만들기 위해 등장하게 되었다.

// Servlet
public class HelloWorldServlet extends HttpServlet {
    private String message;

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

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

        // HTML 코드 반환 부분
        PrintWriter out = response.getWriter();
        out.println("<h1>" + message + "</h1>");
   }

   public void destroy() {}
}

// JSP
<!--https://www3.ntu.edu.sg/home/ehchua/programming/java/JSPByExample.html-->
<html>
<head><title>First JSP</title></head>
<body>
  <%
    double num = Math.random();
    if (num > 0.95) {
  %>
      <h2>Have a luck day!</h2><p>(<%= num %>)</p>
  <%
    } else {
  %>
      <h2>Well, life goes on ... </h2><p>(<%= num %>)</p>
  <%
    }
  %>
  <a href="<%= request.getRequestURI() %>"><h3>Try Again</h3></a>
</body>
</html>

이전 글에서 본 Servlet, JSP 의 코드이다. 두 경우 모두 모두 로직과 View 가 한 곳에서 처리되는 코드를 보여준다. 둘은 코드형태가 다를 뿐 결국 Servlet으로 작동한다.

 

여담이지만, 최근 트렌드처럼 Front/Back 을 나누어 관리하는 것이 아닌, HTML와 Java 코드 섞인 구조는 유지보수 면에서 불리한 구조라고 한다. 정말 작은 서비스라면 상관이 없지만 수십명의 프론트/백엔드 개발자가 같이 개발하는 환경에서는 협업에 어려움이 생길 수 밖에 없다.

서비스 스펙 변경 시, 페이지와 데이터를 처리하는 코드를 같이 수정해야할 것이고, 더 많은 개발자들의 노고가 소요될 것이다. 하나의 Servlet에 프론트/백 한명씩 붙는다고 생각하면.. 😱

from. Wikipedia

JSP와 Servlet 이 최초로 등장한 시기가 1996, 1999 년이고 형상관리 도구 Git은 2005년이니(SVN은.. 패ㅡㅅ)
리누스 토발즈 형님 그저 갓

 


Servlet

 

Oracle 문서에 쓰여진 Servlet의 장점은 아래와 같다.

[ https://docs.oracle.com/cd/B14099_19/web.1012/b14017/overview.htm ]

  • 동적 HTML을 생성하는데 사용되며, 각 요청마다 서로 다른 thread를 만들어 처리하기 때문에 이전 SSI(server-side includes)(https://httpd.apache.org/docs/2.4/ko/howto/ssi.html) 와 CGI 스크립트보다 훨씬 뛰어난 성능을 보인다.
  • 확장성, 보안성이 좋고, 플랫폼에 종속적이지 않은 상태에서 견고(robust)하게 객체지향형적인 Java 의 이점을 제공한다.
  • Java 언어와 fully integrate 하며, DB 연결을 위한 JDBC 와 같은 표준 API를 제공한다.
  • 컴포넌트 관리를 위한 JNDI, transaction 관리를 위한 JTA, 인증에 필요한 JAAS, 분산 어플리케이션을 위한 RMI, 메시지 전송에 JMS 와 같은 기능을 제공하는 J2EE 프레임워크에 fully integrate 하다.
  • 동시 요청을 처리할 수 있고, servlet의 완고한 lifecycle 을 가진다.
  • servlet의 request, response 객체가 클라이언트의 HTTP 요청과 응답을 편리하게 처리할 수 있다.

 

한줄정리: 웹개발에 필요한 대부분의 기능을 제공하고, 서비스 로직 개발에 집중할 수 있는 환경을 제공한다!

 

무엇보다 JVM 위에서 작동하기에, JVM을 가진 모든 플랫폼에서 작동이 가능하다.

(사실, 최근에는 Docker 덕분에 이러한 장점의 비중은 많이 사라졌다고 생각하지만, Docker 또한 종속적인 부분이 존재하니..)

 


Servlet Container

다른 Java 어플리케이션과 다르게 Servlet은 static main() 메소드가 존재하지 않는다.

즉, JRE 위에서 Servlet 코드가 직접 실행되는 형태가 아니라, 외부 container의 관리 아래 실행된다. 이를 관리해주는 것이 Servlet Container 다. Servlet container 로 우리가 잘 아는 Apache Tomcat, Jetty 등이 있다.

 

https://en.wikipedia.org/wiki/Web_container
(다른 web container도 존재한다..)

 

 

https://docs.oracle.com/cd/B14099_19/web.1012/b14017/overview.htm

Servlet Container 는 servlet 메소드를 호출하고, servlet 이 필요한 서비스를 제공한다. 주로 Java 코드로 쓰여져 있고, 웹서버의 일부가 된다.

container는 servlet 이 HTTP 요청과 header, parameter 에 쉽게 접근할 수 있게 도와주며, HTTP 요청을 URL에 맞춰 미리 정의된 설정한 web.xml 에 알맞는 servlet에 전달한다.

 

 

<web-app>
    <!-- Servlet aliases -->
    <servlet>
        <servlet-name>welcome</servlet-name>
        <servlet-class>servlets.WelcomeServlet</servlet-class>
    </servlet>

    <!-- Servlet URL mapping -->
    <servlet-mapping>
        <servlet-name>welcome</servlet-name>
        <url-pattern>/welcome</url-pattern>
    </servlet-mapping>
</web-app>

 

이전 글에 설명한 Servlet Interface 의 init, service, destroy 메소드를 통해 Servlet 을 생성, 호출, 제거 등 관리하며 처리 순서는 아래와 같다.

  • servlet의 instance 를 생성하고, init() 메소드를 호출하여 초기화한다.
  • request(HttpServletRequest) 객체를 만들어 servlet에게 전달한다. 그 안에는 클라이언트로부터 전달된 정보가 포함된다.
    • HTTP header
    • Parameter (URL query string)
    • servlet 요청 URI
  • response(HttpServletResponse) 객체를 만들어 servlet에게 전달한다.
  • servlet의 service() 메소드를 호출한다. 일반적으로 HTTP servlet의 메소드는 HttpServlet 을 override한다. 메소드는 HTTP header GET/POST 등에 따라 doGet(), doPost() 메소드에게 요청을 보낸다.
  • servlet이 응답까지 처리가 완료되고 메소드가 종료되면, destroy() 메소드를 호출하여, 추후 GC가 처리할 수 있게 제거한다. 보통 servlet container 는 처리가 완료된 servlet을 제거하지 않고, 재사용을 위해 servlet instance를 메모리에 보관한다. 웹서버 종료와 같은 호출 빈도가 적은 이벤트는 제거할 수 있다.

한줄정리: servlet container → servlet {

    init() → [GET/POST] service() → destroy() | maintain

}

 

 

Servlet container와 Servlet의 life cycle 에 대해 얘기해봤다. 그렇다면 이번엔 Servlet Container 내부에서 어떻게 Servlet 을 관리하는지 알아보자.

 

 


ServletContext

Tomcat, Jetty 등 container 를 실행하면, ServletContext 객체 생성한다. ServletContext는 Servlet과 Servlet Container를 연결하기 위한 인터페이스이고, container가 실행될 때, 웹서버에 등록한 웹 어플리케이션 단위마다 1개씩 자동으로 생성된다.

 

각 ServletContext 객체가 결국 웹 어플리케이션의 life cycle과 동일하고, 웹 어플리케이션이 중지되면 소멸한다.

 

context(맥락) 이라는 단위는 하나의 웹 어플리케이션을 단위로 각각의 자원을 관리한다는 뜻이다. 그렇기에 JVM 상의 동일한 webapp의 Servlet과 Servlet container 간 연동을 통해 각각의 정보를 추출할 수 있다.

Servlet 2.2 버전 이상부터는 단일 호스트 내 복수의 ServletContext 제공가능 → 서로 다른 어플리케이션마다 ServletContext를 가질 수 있다

 

public interface ServletContext {
    String getContextPath();

    ServletContext getContext(String var1);
		...

    /** @deprecated */
    Servlet getServlet(String var1) throws ServletException;
		...
		String getInitParameter(String var1);
		...

    Object getAttribute(String var1);

    Enumeration getAttributeNames();

    void setAttribute(String var1, Object var2);

    void removeAttribute(String var1);

    String getServletContextName();
}

ServletContext 인터페이스를 간단히 살펴보자.

  • getAttribute : var1 name의 attribute 반환
  • getInitParameter : context-wide 초기화 단계의 parameter 반환. ServletContext와 연결된 웹 어플리케이션에 설정 정보에 접근할 수 있어 유용하다.
  • getResource : path 와 mapping 된 URL instance를 반환.
  • getServerInfo : Servlet container 이름과, 버전 반환
  • getServletContextName : web.xml 의 <display-name>에 명시된 Servlet container 가 연결된 웹 어플리케이션명 반환
Servlet과 ServletContext 간 정보를 공유하는 메소드가 대부분이다. getServlet 같은 deprecated 된 메소드도 보인다. 문서에 별도로 명시된 걸 찾지 못했지만, 아마 요청마다 Servlet 을 처리하고 반환하기 때문에 그 과정에서 Servlet 정보를 가져와도 큰 영향이 없다 판단하여 없애지 않았을까…

 

 

 

https://www.javatpoint.com/servletcontext

정리하면 ServletConext 은 servlet container 가 돌아가는 문맥에서 container 와 Servlet 사이에서 필요한 정보를 주고 받게 도와주는 존재이다.

 

 

🤔🤔🤔 좀 더 풀어서 얘기해보자 🤔🤔🤔

 

각 요청마다 Servlet이 호출되고, HttpServletRequest, HttpServletResponse로 요청을 처리한 후 소멸/반납된다. 즉, 각 요청은 독립적으로 처리된다.

동일한 유저의 다수 요청에 대해서는 HttpSession 이 처리한다. HttpSession이 사용자 세션에 결합되어 클라이언트로부터 cookie를 전달받고, JSESSIONID 등 고유값으로 식별한다.

<session-config>
    <session-timeout>10</session-timeout>
</session-config>
web.xml 에서 session timeout 등을 설정할 수 있다.

 

Servlet 요청, Session 과 비교했을 때, Servlet 입장에서 ServletContext이 가장 lifespan(수명)이 길다. servlet, filter, listener 등의 instance는 웹 어플리케이션이 가동하는 동안 유지되야 하기에 ServletContext 에서 관리가 필요하다.

 

다시 ServletContext의 필요성은 Servlet 간 주고 받기 보다 web.xml로부터 DB connection 설정, 전역 상수 등을 공유하기 위해 필요한 존재라고 생각하면 될 것 같다. (다른 예시로 전체 웹 사이트 방문자 수도 있다)

 

 

Java는 멀티스레드가 가능하다. HttpServletRequest 객체는 각각의 servlet instance 의 스레드에서 작동한다.

만약, servlet 단에서 attribute 를 공유한다면 무슨일이 발생할까

// https://www.baeldung.com/java-servlets-containers-intro
public class ExampleThree extends HttpServlet {
    
    private String instanceMessage;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
        String message = request.getParameter("message");
        instanceMessage = request.getParameter("message");
        request.setAttribute("text", message);
        request.setAttribute("unsafeText", instanceMessage);
        request.getRequestDispatcher("/jsp/ExampleThree.jsp").forward(request, response);
    }
}

messagedoGet() 메소드 내에 정의되어 있고, instanceMessage 는 Servlet 클래스에 정의되어 있다. 두 변수의 context가 다르다. 위 코드로 작동 시, message 는 각 요청마다 고유하게 존재하고, instanceMessage 는 한 세션의 모든 요청끼리 공유하는 사태가 발생한다.

결국, Servlet 간 공유가능한 기능이지만 부적절하게 사용 시, 의도하지 않은 오류가 발생할 수 있다는 뜻이다.

 

 

지금까지의 설명을 다시 그림으로 정리하면 아래와 같다. (Servlet container 미포함)

https://www.oreilly.com/library/view/head-first-servlets/9780596516680/ch05s10.html

 


요약정리

 

우리는 바쁜 사회인이기에 짧게 정리해보자

 

Servlet : 외부 요청/응답을 처리하는 Java 클래스. 주로 HttpServlet 을 사용하여 동적 웹서비스 로직 제공 (중요: Servlet은 싱글톤이 아니다!)
Servlet container : Servlet 의 lifecycle 을 관리하고, URL에 mapping 된 Servlet으로 요청을 전달. 웹 어플리케이션마다 1개씩 존재.
ServletContext : Servlet, Servlet container 등 필요한 정보를 연결시켜주고, 주로 container 별 전역정보(DB connection 정보, 서버 방문자 등) 를 전달, 관리

 

다음 글은 가장 많이 사용되고 있는(자바공화ㄱ..) Spring 과 Servlet 을 같이 정리한다.

 

 

 

Reference

https://docs.oracle.com/cd/B14099_19/web.1012/b14017/overview.htm

https://www.baeldung.com/java-servlets-containers-intro

 

'Java > Spring | SpringBoot' 카테고리의 다른 글

[Servlet, JSP and Spring] Servlet & JSP  (0) 2022.11.06