본문 바로가기

Dot Programming/Java

[Java] 자바 서블릿과 서블릿 컨테이너 1 - 웹 서버 한계와 WAS 등장

    HTTP 웹 서버 문제점

    1. 서비스 로직보다 HTTP 요청, 응답 헤더에 너무 많은 개발 시간을 소요한다.
    2. 동적 HTML 지원 한계. 많은 코딩이 필요하다. 
    3. 사용자가 입력한 데이터가 서버를 재시작하면 사라진다. 서버를 껐다 켜도 데이터를 유지하고 싶다.

     

    WAS (Web Application Server)

    WAS는 위의 HTTP 웹 서버 문제점을 해결하기 위해 나온 애플리케이션 서버다. HTTP 마크업 언어를 통해 사용자 컴퓨터나 장치에 애플리케이션을 수행해 주는 미들웨어로 볼 수 있다. 대표적으로 apache tomcat 서버가 있다.

    WAS 특징

    • 프로그램 실행 환경과 데이터베이스 접속 기능을 제공한다.
    • 여러 개의 트랜잭션을 관리한다.
    • 업무를 처리하는 비즈니스 로직을 수행한다.

     

    1번과 2번의 문제를 해결하기 위해 WAS에 자바 진영에서 표준으로 정한 것이 서블릿 컨테이너 서블릿/JSP이다.

    • 기존 웹 서버에서는 정적 페이지 로딩만 가능했다면 WAS는 서블릿을 사용해 동적 페이지이 로딩이 가능해졌다.

     

    3번 문제를 해결하기 위해 데이터베이스 서버를 도입해 데이터를 영구적(persistency)으로 저장할 수 있도록 고안해냈다. 자바에서는 JDBC(Java Database Connectivity)라는 표준을 통해 데이터베이스와 통신을 한다.

    • JDK에서 제공하는 java.sql 패키지의 JDBC 소스코드는 열어보면 구현 코드는 거의 없고, 인터페이스만 정의해 제공하고 있다.
    • 즉, JDBC는 데이터베이스 통신을 위한 규약만 정하고 이에 대한 구현체는 DB를 만들어 서비스하는 회사가 제공하도록 하고 있다. 
    • JDBC에 관한 이야기는 나중에 ORM, JPA와 연관지어 따로 정리할 예정이다

     

    서블릿 컨테이너 특징

    생명주기 관리

    서블릿 컨테이너가 기동되는 순간 서블릿 클래스를 로딩해서 인스턴스화하고, 초기화 메서드를 호출하고, 요청이 들어오면 적절한 서블릿 메소드를 찾아서 호출한다. 만약 서블릿의 생명이 다하는 순간 가비지 컬렉션을 진행한다. 이는 아래서 더 구체적으로 다룬다.

    통신 지원

    서블릿과 웹 서버가 통신할 수 있는 손쉬운 방법을 제공한다. 우리가 통신을 한다고 생각할 때 소켓을 만들고, 특정 포트를 리스닝하고, 연결 요청이 들어오면 스트림을 생성해서 요청을 받는다고 알고있는데 이 과정을 서블릿 컨테이너가 대신 해주는 것이다. 서블릿 컨테이너는 이런 통신 과정을 API 로 제공하고 있기 때문에 우리가 쉽게 사용할 수 있다.

    멀티스레딩 관리

    서블릿 컨테이너는 해당 서블릿의 요청이 들어오면 스레드를 생성해서 작업을 수행한다. 즉 동시의 여러 요청이 들어온다면 멀티스레딩 환경으로 동시다발적인 작업을 관리한다.

    선언적인 보안관리

    서블릿 컨테이너는 보안 관련된 기능을 지원한다. 따라서 서블릿 코드 안에 보안 관련된 메소드를 구현하지 않아도 된다.

    JSP 지원

    서블릿 컨테이너는 HTML안에 자바 코드가 들어간 동적 웹페이지 JSP(Java Server Page)를 지원한다. 이는 동적 웹페이지를 구현시키기 위함도 있지만 갈수록 웹 페이지의 크기가 커지면서 HTTP 응답 바디에 HTML을 직접 들고 다니기 버거위지면서 클라이언트의 무게를 줄여주고 서버에 무게를 나눠주기 위함이다. 서블릿과 JSP의 차이는 아래서 더 구체적으로 다룬다.

     

    서블릿 컨테이너가 관리하는 서블릿 생명주기

    서블릿 컨테이너는 서블릿의 생명주기를 관리해준다. 생명주기는 서블릿의 생성, 초기화, 서비스, 소멸 과정을 거치는 전체 과정을 나타낸다.

    서블릿 생명주기

     

    init() - 초기화 및 생성

    서블릿 컨테이는 다음과 같은 과정으로 서블릿 초기화를 완료한 후 대기상태로 가만히 있는다.

    1. 서블릿 컨테이너 시작
    2. 클래스패스에 있는 Servlet 인터페이스를 구현하는 서블릿 클래스를 찾음
    3. @WebServlet 설정을 통해 요청 URL과 서블릿 매핑
    4. 서블릿 인스턴스 생성
    5. init() 메서드를 호출해 초기화

    service() - 서비스 실행

    그리고 클라이언트 요청이 있을 경우 요청 URL에 해당하는 서블릿을 찾아 service() 메서드를 호출한다.

    • GET 요청이면 doGet(), POST 요청이면 doPost() 등 service각 요청을 분기하여 처리한다

    destroy() - 서블릿 파괴

    서비스를 하다 서블릿 컨테이너를 종료하면 서블릿 컨테이너가 관리하고 있는 모든 서블릿의 destroy() 메소드를 호출해 소멸 작업을 진행한다.

     

    서블릿 동작과정

    1. 클라이언트 요청 GET http://loosie.tistory.com/1
    2. 웹서버는 요청된 서블릿 확인후 서블릿 컨테이너로 요청을 보낸다.
    3. 서블릿 컨테이너는 request와 response 객체 생성후 pom.xml(초기엔 web.xml) 참조 후 @WebServlet 서블릿 위치를 찾아 해당 서블릿의 스레드 생성
    4. service 메소드 호출 후 doGe 메서드 실행 (요청에 따라 doGet, doPost 등 호출)
    5. 각 doGet 메서드에서 응답 생성 후 클라이언트에게 전송
    6. request, response 객체를 소멸(http 비연결성, 무상태이기 때문)시키고 스레드 종료 

     

    WAS 아키텍처

     

    서블릿과 JSP

    이 둘은 방식의 차이만 있다. 둘 다 자바로 동적 웹페이지를 코딩하여 클라이언트에게 보내는 역할은 같다.

    • 서블릿: 자바 코드 안에 HTML을 포함하고 있다.
    • JSP: HTML 문서 안에 자바 코드를 포함하고 있다. Servlet을 보완하고 기술을 확장하기 위해 나온 스크립트 표준 방식이다.

     

    자바 서블릿 (위키 참고)

     

    정적 웹페이지로 한계를 느끼고 동적 웹페이지가 도입 되면서 자바 개발자들은 서블릿 인스턴스 안에 직접 HTML 마크업 언어를 하드 코딩을 하기 시작했다. 즉 자바 코드안에 HTML를 넣은 것이다. JSP가 도입되기 전 다음 코드처럼 서블릿 인스턴스 자바 코드안에 HTML을 하드 코딩해서 바이트코드로 내보냈다.

    // 수도 코드입니다.
    public class ListUserController extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setContentType("text/html");
            printWriter out = resp.getWriter();
            out.println(listBuilder());
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        }
        
        private String listBuilder() {
           StringBuilder sb = new StringBuilder();
           sb.append("<table border='1'>\n");
           sb.append("<thead>\n");
           sb.append("<tr>\n");
           sb.append("<th>#</th> <th>사용자 아이디</th> <th>이름</th> <th>이메일</th>\n");
           sb.append("</tr>\n");
           sb.append("</thead>\n");
    
           sb.append("<tbody>\n");
           AtomicInteger index = new AtomicInteger();
           Collection<User> allUsers = DataBase.findAll();
           allUsers.forEach((user) ->
              sb.append("<tr>\n")
                 .append("<th scope=\"row\">").append(index.getAndIncrement())
                 .append("</th> <td>").append(user.getUserId())
                 .append("</td> <td>").append(user.getName())
                 .append("</td> <td>").append(user.getEmail())
                 .append("</td> </tr>\n"));
           sb.append("</table>\n</tbody>\n").append("</table>\n");
           return sb.toString();
    	}
    }

     

    자바 속에 HTML 코드라니 보기만 해도 힘들다. 그래서 HTML 문서 안에 자바 코드를 포함하는 JSP(Java Server Page)를 지원하기 시작했다. 다음 코드는 MVC JSP로 보기 좋아진 것이다. 원래 초기 JSP는 <% ~ %> 안에 자바 코딩을 넣어줬어야 했다. (참고)

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    
    <%-- 생략...  --%>
    
    <table class="table table-hover">
        <thead>
        <tr>
            <th>#</th> <th>사용자 아이디</th> <th>이름</th> <th>이메일</th><th></th>
        </tr>
        </thead>
        <tbody>
        <c:forEach items="${users}" var="user" varStatus="status">
            <tr>
                <th scope="row">${status.count}</th>
                <td>${user.userId}</td>
                <td>${user.name}</td>
                <td>${user.email}</td>
                <td><a href="/users/updateForm?userId=${user.userId}" class="btn btn-success" role="button">수정</a>
                </td>
            </tr>
        </c:forEach>
        </tbody>
    </table>
    
    <%-- 생략...  --%>

     

    JSP만 사용하기

    "JSP에 서블릿이 꼭 필요한가? JSP로 요청이 되어도 컨테이너는 JSP 파일을 서블릿 형태의 자바코드로 변환한 후 서블릿 생명주기를 거쳐 요청을 처리함. 어차피 동작과정은 같아서 서블릿을 거칠 필요가 없음." 라고 주장하며 JSP만 사용하는 프로그램도 있었다고 한다. (직접 보진 못함)

     

    JSP만 사용하기

    동작 과정

    • 1) 클라이언트가 서버에 요청을 보낸다.
    • 2-1) 요청을 받은 JSP는 Java Bean(DTO, DAO)을 호출하여 데이터를 요청한다.
    • 2-2) 요청받은 Java Bean 객체는 DB에 적절한 정보를 조회한다.
    • 2-3) DB에서 받은 데이터를 Java Bean 객체에 저장한다.
    • 2-4) Java Bean은 JSP의 요청에 맞는 데이터를 제공한다. 
    • 3) 동적 처리가 완료되었으면 클라이언트에게 응답한다.

    장점

    • 개발 속도가 빠르다.
    • 배우기 쉽다.

    단점

    • 프레젠테이션 로직(View)과 비즈니스 로직(Controller)이 혼재한다.
    • JSP 코드가 복잡해져 유지 보수가 어려워진다.

     

     

    JSP와 Servlet 같이 사용하기

    웹 페이지가 점점 커져가고 여러 기능을 가진 동적 웹페이지가 나오기 시작하면서 JSP안에 모든 것을 감당하기에는 벅찼다. 그래서 자바 개발자들은 MVC 패턴이 도입하기 시작했다. Servlet을 HTTP 웹 서버에서 사용하던 Controller에 대입시키고 JSP는 View에 대입시킴으로써 JSP의 역할을 분리시켰다.

     

    MVC Architecture

    동작 과정

    • 1) 클라이언트가 서버에 요청을 보낸다.
    • 2-1) 서블릿에 요청이 전달되어 DB와 매핑된 Java Beans객체를 생성한다.
    • 2-2) Java Bean은 DB에서 적절한 정보를 가져와 저장한다.
    • 2-4) 다시 Servlet으로 넘어가서 추가적인 비즈니스 로직 과정을 수행한다
    • 3) 적절한 View를 선택한다.
    • 4) 선택된 JSP 페이지는 Java Bean과 통신하여 정보를 전달받는다.
    • 5) 동적 처리가 완료되었으면 클라이언트에게 응답한다.

     

    MVC 내용은 다음 편에서 계속...


    참고

    자바 웹 프로그래밍 Next Step

    https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EC%84%9C%EB%B8%94%EB%A6%B