Java와 함께하는 Web

서론

Javascript를 이용한 Node.js로 서버프로그래밍을 하다가 최근 Java를 이용한 Spring Framework를 사용하게 되면서 JavaEE, Servlet, JSP, Tomcat, MVC, WAS와 같은 용어들을 마주하게 되었습니다. 이번 포스팅을 통해서 Java를 이용한 웹 개발의 히스토리와 여러 용어들을 정리해보고자 합니다.

시작은 JavaEE

기존에는 기업용 서버 소프트웨어 개발이라는 것이 C나 C++을 사용해서 다양한 회사의 미들웨어(middleware) 제품들을 사용해서 개발하는 방식이었습니다. 그러나 이 경우 개발자들은 운영체제와 사용하는 미들웨어 제품에 종속될 수 밖에 없는데, 자바의 플랫폼 독립적 특성을 활용해서 미들웨어에 필요한 공통 API를 제공하면 그런 문제를 해결할 수 있을 것이라는 생각을 했습니다.
그래서 서버 개발에 필요한 기능을 모아서 J2EE라는 표준을 만들었습니다. 그리고 이 J2EE는 버전 5.0 이후로 Java EE로 개칭됩니다.

자바 플랫폼, 엔터프라이즈 에디션(Java Platform, Enterprise Edition; Java EE)은 자바를 이용한 서버측 개발을 위한 플랫폼입니다. Java EE 플랫폼은 PC에서 동작하는 표준 플랫폼인 Java SE에 부가하여, 웹 애플리케이션 서버에서 동작하는 장애복구 및 분산 멀티티어를 제공하는 자바 소프트웨어의 기능을 추가한 서버를 위한 플랫폼입니다. 이러한 Java EE 스펙에 따라 제품으로 구현한 것을 웹 애플리케이션 서버 또는 WAS라 부릅니다.

WAS란?
인터넷 상에서 HTTP를 통해 사용자 컴퓨터나 장치에 애플리케이션을 수행해 주는 미들웨어(소프트웨어 엔진)이다. 웹 애플리케이션 서버는 동적 서버 콘텐츠를 수행하는 것으로 일반적인 웹 서버와 구별이 되며, 주로 데이터베이스 서버와 같이 수행이 된다.

< 사용자 요청(웹 브라우저) -> 웹 서버 -> WAS(동적 처리) -> 웹 서버 -> 사용자 응답 메세지(웹 브라우저) >
예로, 웹 서버에서 JSP를 요청하면 톰캣에서는 JSP 파일을 서블릿으로 변환하여 컴파일을 수행하고, 서블릿의 수행결과를 웹 서버에서 전달하게 된다.

그렇게 시작된 자바EE는 출발부터 많은 관심을 받았고, 특히 웹 개발을 위해 자바EE 표준에 포함된 **서블릿(Servlet)**과 JSP는 당시 막 유행하던 PHP나 ASP와 함께 CGI를 몰아내며 자바 언어가 인기를 얻는데 한 몫을 담당했습니다.

CGI란? (인용 - http://www.terms.co.kr/CGI.htm)
CGI는 웹서버에 있어 사용자의 요구를 응용프로그램에 전달하고 그 결과를 사용자에게 되돌려주기 위한 표준적인 방법이다. 사용자가 하이퍼링크를 클릭 하거나 웹사이트의 주소를 입력함으로써 웹 페이지를 요청하면, 서버는 요청된 페이지를 보내준다. 그러나, 사용자가 웹페이지의 양식에 내용을 기재하여 보냈을 때, 그것은 보통 응용프로그램에 의해 처리될 필요가 있다. 웹 서버는 그 양식 정보를 조그만 응용프로그램에 전달하는데, 이 프로그램은 데이터를 처리하고 필요에 따라 확인 메시지를 보내주기도 한다. 이렇게 서버와 응용 프로그램간에 데이터를 주고받기 위한 방법이나 규약들을 CGI라고 부른다. 이것은 웹의 HTTP 프로토콜의 일부이다.
만약 웹사이트를 만들 때 어떠한 제어를 위해 CGI 프로그램을 사용하기 원하면, HTML 파일 내에 있는 URL 내에 그 프로그램의 이름을 기술하면 된다. 만약 폼을 만들려고 할 때, 이 URL은 FORMS 태그의 일부로서 기술될 수 있는데, 예를 들어 아래와 같이 쓸 수 있을 것이다.
이 태그의 결과로서 mybiz.com에 있는 서버는 입력된 데이터를 저장하기 위해 제어권을 “formprog.pl”이라는 CGI 프로그램에 넘기고, 확인 메시지를 되돌려준다 (여기서 .pl은 Perl로 작성된 프로그램이라는 것을 가리키지만, CGI는 다른 언어로도 작성될 수 있다).

1
<FORM METHOD=POST ACTION=http://www.mybiz.com/cgi-local/formprog.pl>

자바EE의 핵심은 EJB(Enterprise Java Beans)라는 기술이었습니다. EJB는 자바EE가 대체하는 미들웨어에서 구동되던 기업의 핵심 서비스를 만들기 위한 분산처리 및 트랜잭션, 보안 등을 지원하는 컴포넌트 모델을 제공하는 기술입니다. 이러한 EJB는 주목을 받으며 널리 쓰이게 되었지만 시간이 지남에 따라 몇 가지 심각한 문제들로 비판을 받게 되었고, 이러한 문제점을 개선하기 위해 Spring Framework가 처음 개발되었습니다. 특히 고가의 풀스택 자바EE 서버가 아닌 톰캣과 같은 일반 서블릿 컨테이너에서도 구동된다는 것이 큰 강점으로 작용했습니다.

다시 말하면, 이는 Spring을 통해 비싼 자바EE 서버를 구매하지 않아도 EJB보다 훨씬 간편한 방식으로 EJB가 제공하던 선언적 트랜잭션 및 보안 처리, 분산 환경 지원 등 주요 기능을 모두 사용할 수 있게 되었음을 뜻하며, 무엇보다 이제는 더 이상 각 자바EE 서버 제품에 특화된 설정을 따로 공부하거나 서버 제품을 바꿀 때마다 포팅 작업이 필요없이 Spring만 이용하면 톰캣이든 레진(Resin)이든 기존의 풀스택 자바EE 서버이든 관계없이 간단하게 배포가 가능하다는 뜻입니다.

Servlet

Servlet은 Java 기반의 확장된 CGI로서 동일하게 동적인 웹 애플리케이션을 작성할 수 있는 기술입니다. CGI와 비슷하게 클라이언트의 요청을 받아 해당하는 프로그램을 실행시켜주지만 CGI와는 조금 다른 동작 형태를 보입니다.
Servlet은 CGI와 달리 효율적입니다. CGI의 멀티 프로세스 동작이 아닌 멀티 스레드 방식의 동작으로 서블릿이 생성되면 서버가 종료되지 않는 이상 메모리로 남게 됩니다. 따라서 이후에 오는 요청에 대해서는 서블릿을 새로 생성하지 않고 동작을 이어갈 수 있기 때문에 시스템 자원(메모리)에서 큰 이점이 있습니다. 그로인해 Servlet은 CGI 보다 적은 시스템 자원으로 많은 요청을 처리할 수 있는 구조를 가지고 있습니다.

멀티 프로세스 동작 방식


클라이언트의 요청을 받아 웹 애플리케이션을 직접 실행하는 구조로 각각의 요청에 대해 프로세스를 생성하고 응답한 뒤 종료하는 형태이다. 이는 각각의 많은 요청이 들어오는 경우 프로세스를 계속 생성하므로(프로세스를 생성하는 작업은 필요이상의 부담을 주게 된다) 시스템 부하가 커지게 되 안정적인 서비스가 힘들다.

멀티 스레드 동작 방식


클라이언트의 요청을 받으면 웹 애플리케이션을 거치지 않고 웹 컨테이너로 요청이 전달된다. 그리고 웹 컨테이너가 요청을 처리할 스레드를 생성하는 형태이다. 멀티스레드 방식은 최초 요청 시 웹 애플리케이션을 실행한 후 종료하지 않은 상태에서 같은 요청이 여러 번 오는 경우, 실행되고 있는 웹 애플리케이션의 스레드를 생성해 요청을 처리하는 방법이다. CGI에서 사용하는 멀티프로세스 방식보다 시스템 부하를 줄여 안정적인 서비스를 제공할 수 있다.

컨테이너란? (인용 - 컨테이너란?? 무엇일까?)
컨테이너는 Servlet을 실행하고 관리하는 역할을 합니다. 개발자가 해야하는 역할을 대신 함으로써, 개발자가 해야하는 일을 대폭 줄여줍니다. 컨테이너는 개발자가 웹서버와 통신하기 위하여 소켓을 생성하고, 특정 포트에 리스닝하고, 스트림을 생성하는 등의 복잡한 일들을 대신합니다. 또한 Servlet의 생성부터 소멸까지 일련의 과정을 관리하며, 요청이 들어올 때마다 새로운 자바 스레드를 하나 생성합니다.
톰캣을 예로 들면 아파치와 같은 웹서버가 사용자로부터 Servlet에 대한 요청을 받으면 이것을 바로 호출하는 것이 아니라 컨테이너에게 이 요청을 넘겨주고 이 컨테이너는 request와 response 객체를 생성하고 해당하는 Servlet의 스레드를 생성하여 앞의 두 객체를 인자로 넘깁니다.
스레드 생성 후 이 스레드의 service() 메소드를 호출하고 처음에 사용자로부터 요청받은 방식이 get인지 post인지에 따라 doGet()과 doPost() 메소드 중 선택 생성합니다. 만약 doPost가 생성되었다고 가정하면, 이 doPost() 메소드는 독 페이지를 생성하고, 이것을 처음 받은 response 객체에 실어서 컨테이너에게 보냅니다. 컨테이너는 이 객체를 HTTPResponse로 변환하여 클라이언트에게 보냅니다. 그런 다음 처음에 생성한 객체 Request와 Response를 소멸시킵니다.

Servlet 동작 방식

  1. 사용자가 URL을 클릭하면 HTTP Request를 Servlet Container에 보낸다.
  2. Servlet Container는 HttpServletRequest, HttpServletResponse 두 객체를 생성한다.
  3. 사용자가 요청한 URL을 분석하여 어느 서블릿에 대한 요청인지 찾는다. (DD를 참조하여 분석)
  4. 컨테이너는 서블릿 service() 메소드를 호출하며, POST, GET여부에 따라 doGet() 또는 doPost()가 호출된다.
  5. doGet() or doPost() 메소드는 동적인 페이지를 생성한 후 HttpServletResponse객체에 응답을 보낸다.
  6. 응답이 완료되면 HttpServletRequest, HttpServletResponse 두 객체를 소멸시킨다.

DD (배포서술자, Deployment Descriptor) = web.xml

  • Servlet, Error Page, Listener, Fillter, 보안 설정등 Web Application의 설정 파일이다.
  • URL과 실제 서블릿의 매핑 정보도 담고 있다.
  • 하나의 웹 어플리케이션에 하나만 존재한다.
  • 보통 Web Document Root 디렉토리에 WEB-INF 폴더 아래 web.xml 파일로 존재한다.

한마디로 정리하자면 톰캣과 같은 WAS 가 java 파일을 컴파일해서 Class로 만들고 메모리에 올려 Servlet 객체를 만들게 되고 이 Servlet 객체doPost, doGet을 통해 요청에 응답합니다. 초기화 과정을 더 자세히 보면 다음과 같습니다.

init, Service, destory 이런 콜백이 각 시점에 불리는걸 볼 수 있습니다. init은 Servlet이 메모리에 로드 될때 실행됩니다. destory는 마찬가지로 언로드되기 전에 수행되는 콜백입니다. service 메소드는 HTTP Method 타입에 따라 doGet 혹은 doPost를 호출합니다.
기억해야 할 점은, 초기화된 Servlet이 클라이언트의 요청이 있을 때 마다 Thread를 생성해서 병렬적으로 service를 수행한다는 것. 서블릿 객체는 여러개 생성되지 않습니다.

Servlet 예시 코드

다음은 Servlet의 예시 코드입니다. 자바 코드 안에 HTML을 넣기 굉장히 불편합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HelloServlet extends HttpServlet {

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException,IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.println("<HTML>");
out.println("<BODY>");
out.println("Hello World!!");
out.println("</BODY>");
out.println("</HTML>");
out.close();
}
}

JSP

HTML을 넣기 불편한 구조로 인해 JSP가 등장하게 됬습니다. Servlet의 확장된 기술로 브라우저에 표현하기 위한 HTML 코드에 JAVA 코드를 혼용하여 사용할 수 있게 합니다. 이로써 디자인과 로직 개발을 분업화시켜 효율적인 코드를 생산해 낼 수 있게됩니다.

JSP 동작 방식

JSP 라는 새로운 개발 방법이 나왔지만, 사실 이 JSP 도 내부적으로는 아래 그림 처럼 TomcatServlet으로 바꾸어서 돌립니다.

  1. 클라이언트가 브라우저를 통해 서버에 HTTP 프로토콜로 요청한다.
  2. 서버는 컨테이너에게 처리를 요청하고 컨테이너는 해당 파일을 찾는다.
  3. 찾은 파일을 서블릿으로 변환한다. 만약 이미 변환 되어있는 파일이 있다면 그 파일을 바로 실행⑤한다.
  4. 서블릿 파일을 실행가능한 class파일로 컴파일 한다.
  5. 컴파일된 class파일을 메모리에 적재하고 실행한 결과를 웹서버에 넘겨준다.
  6. 웹서버는 브라우저가 인식할수 있는 정적페이지를 구성하여 클라이언트에게 응답한다.

JSP 예시 코드

HTML 내부에 Java 코드가 있어 HTML 코드를 작성하기 쉽습니다. 그러나 로직과 디자인이 한 파일내에 섞여있어 유지보수가 어렵습니다. 하나가 편한대신, 다른 불편한 점들이 생긴것 입니다. 그래서 이를 해결하기 위해 MVC Model이 등장하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@page import="java.util.Calendar" %>
<%@ page contentType="text/html; charset=UTF-8"%>
<%
String str=String.format("%tF",Calendar.getInstance());
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
오늘은 <%=str%><br/>
</body>
</html>

MVC Model 1


Model1 방식은 사용자로부터 요청을 JSP가 받아(더 정확히는 JSP 에서 사용자가 요청을 합니다.) Java Bean(DTO, DAO)을 호출해 처리합니다.
이 방식은 개발 속도가 빠르고 배우기 쉽지만 프레젠테이션 로직과 비즈니스 로직이 혼재하기 때문에 JSP 코드가 복잡해져 유지 보수가 어려워진다는 단점이 있습니다.

MVC Model 2


단순히 JSP만 사용하거나, Servlet만 사용하는 것이 아니라 두개의 장단점을 모두 취해 ViewJSP로, ControllerServlet을 사용한 것이 Model2 입니다. 보여지는 부분은 HTML이 중심이 되는 JSP, 다른 자바 클래스에게 데이터를 넘겨주는 부분은 Java 코드가 중심이 되는 Servlet이 담당하게 됩니다. 그리고 Model 영역 에서는 DTO, DAO를 통해 Mysql 과 같은 데이터베이스에 접근합니다.

참고