 |
|
난이도 : 중급 Michael Galpin, 개발자, eBay
2008 년 3 월 11 일 아파치 제로니모(Apache Geronimo) 팀은 새로운 자바 EE(Java™
Platform, Enterprise Edition) 5.0 스펙을 성공적으로 구현했습니다. 자바 EE 5에는 주목할 만한 기능이 많이 있지만 그 중 하나가 새로운 자바 표준 태그 라이브러리(Java Standard Tag Library, JSTL) 1.2 스펙입니다. JSTL 1.2의 핵심인 통합 표현식 언어 덕분에, JSTL의 기능들을 JSF(JavaServer Faces)와 함께 쓸 수 있게 되었습니다. 이 기사에서는, 자바 웹 기술의 역사를 통해 JSTL 1.2의 중요성을 알아보고, 또한 제로니모 팀이 JSTL 1.2를 지원하기 위해 글래스피시(GlassFish) JSTL 1.2 구현에 어떤 영향을 주었는지를 알아봅니다.
자바 웹 기술의 진화
기업용 자바 언어는 항상 웹 기술을 포함하고 있다. 서블릿에서 출발해 진화해 나가고 있다.
서블릿
원래 서블릿은 HTTP 요청에 대한 응답을 위해 사용되었다. 서블릿을 작성하는 것은 꽤 지저분한 일이었다. Listing 1의 예를 보자.
Listing 1. HTML을 생성하는 서블릿
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ServletOutputStream out = response.getOutputStream();
out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0
Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
out.println("<head>");
out.println("<meta http-equiv=\"Content-Type\" content=\"text/html;
charset=ISO-8859-1\" />");
out.println("<title>All Users</title>");
out.println("</head>");
out.println("<body>");
out.println(" <table>");
out.println(" <tr>");
out.println(" <td>UserID</td>");
out.println(" <td>UserName</td>");
out.println(" <td>Name</td>");
out.println(" </tr>");
UserDao dao = new UserDao();
List users = dao.getAllUsers();
for (int i=0;i<users.size();i++){
User user = (User) users.get(i);
out.println(" <tr>");
out.println(" <td>"+user.getId()+"</td>");
out.println(" <td>"+user.getUserName()+"</td>");
out.println(" <td>"+user.getFirstName()+'
'+user.getLastName()+"</td>");
out.println(" </tr>");
}
out.println(" </table>");
out.println("</body>");
out.println("</html>");
}
|
서블릿은 자바 코드에 임베드되는 많은 HTML을 포함했다. 서블릿 개발보다 더 어려운 일은 그것들을 관리하는 것이었다. Listing 1의 코드에서 테이블의 테두리를 바꾸기 원한다고 상상해보라. 자바 코드를 변경하고 서블릿을 다시 컴파일해야 한다. 다행히도 서블릿은 곧바로 자바 서버 페이지(JavaServer Pages, JSP) 기술로 확장되었다.
자바 서버 페이지
JSP 기술은 자바 서블릿을 강화했다. JSP 컴포넌트는 서블릿의 많은 부분을 개선했으며, 고유의 마크업을 자바 코드와 혼합해 사용할 수 있도록 했다. Listing 2는 Listing 1의 서블릿과 동일한 JSP 컴포넌트를 보여준다.
Listing 2. JSP 1.0 버전
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import="org.developerworks.*" %>
<%@ page import="java.util.List" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>All Users</title>
</head>
<body>
<table>
<tr>
<td>UserID</td>
<td>UserName</td>
<td>Name</td>
</tr>
<%
UserDao dao = new UserDao();
List users = dao.getAllUsers();
for (int i=0;i<users.size();i++){
User user = (User) users.get(i);
%>
<tr>
<td><%= user.getId() %></td>
<td><%= user.getUserName() %></td>
<td><%= user.getFirstName() %> <%= user.getLastName()
%></td>
</tr>
<%
}
%>
</c:forEach>
</table>
</body>
</html>
|
Listing 2는 서블릿에 비하면 확실히 큰 발전이다. JSP 컴포넌트도 여전히 서블릿으로 컴파일되지만, 서블릿 컨테이너(또는 빌드 과정에서 수행할 수도 있다)에서 수행되었다. 그 덕분에 JSP 컴포넌트는 서블릿과 동일한 성능을 제공할 수 있었다. JSP 문법은 액티브 서버 페이지(Active Server Page, ASP), PHP 페이지와 비슷하지만 서블릿으로 컴파일되므로 이 기술들보다 성능 면에서 눈에 띄는 장점을 갖게 되었다.
JSP 모델 2
Listing 2의 JSP 코드에는 여전히 눈에 띄는 문제가 있다. 자바 코드의 작은 조작들, 즉 스크립틀릿(scriptlet)이다. 스크립틀릿을 사용하는 것은 디자인과 실용적인 관점 모두에서 문제가 있었다. JSP 컴포넌트에 비지니스 로직(사용자의 목록을 가져오는)과 프리젠테이션이 마구 혼재되어 있었다. JSP 모델 2 아키텍처는 이 문제를 해결하려고 등장했다. 서블릿이 JSP 컴포넌트와 결합되어 사용되었다. Listing 3을 보자.
Listing 3. 모델 2 형식의 서블릿
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
UserDao dao = new UserDao();
List users = dao.getAllUsers();
request.setAttribute("users", users);
request.getRequestDispatcher("/user.jsp").forward(request, response);
}
|
우선 서블릿이 요청을 받아 비지니스 로직을 수행한 다음 결과를 HttpServletRequest 객체에 저장하고 JSP 컴포넌트로 전달한다. 이렇게 해서 Listing 4에서 볼 수 있는 것처럼 JSP 컴포넌트는 더욱 단순해졌다. 서블릿은 먼저 요청을 처리하고 비즈니스 로직을 수행한다.
Listing 4. 모델 2 형식의 JSP 컴포넌트
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import="org.developerworks.*" %>
<%@ page import="java.util.List" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>All Users</title>
</head>
<body>
<table>
<tr>
<td>UserID</td>
<td>UserName</td>
<td>Name</td>
</tr>
<%
List users = (List) request.getAttribute("users");
for (int i=0;i<users.size();i++){
User user = (User) users.get(i);
%>
<tr>
<td><%= user.getId() %></td>
<td><%= user.getUserName() %></td>
<td><%= user.getFirstName() %> <%= user.getLastName()
%></td>
</tr>
<%
}
%>
</c:forEach>
</table>
</body>
</html>
|
몇 가지 구조적인 문제는 해결되었지만 실용적인 문제는 여전히 남아있다. JSP 컴포넌트에서 자바 코드와 HTML을 섞어 쓰면 HTML을 잘 모르는 자바 개발자와 자바 언어를 잘 모르는 디자이너들이 협업하기 어려웠다.
자바 서버 페이지 표준 태그 라이브러리(JSTL)
자바 스크립틀릿을 제거해 JSP 컴포넌트를 정리하는 것이 JSP 기술의 목표가 되었고 그 결과로 JSTL이 등장했다. JSTL은 자바 객체에 접근하고 자바 프로그래밍 언어의 다양한 요소를 수행하기 위해 컬렉션 순회, 조건에 따른 처리, 테스트 포매팅 등을 위한 HTML 형식의 태그를 도입했다. Listing 5의 JSTL은 JSP 컴포넌트의 진화를 보여준다.
Listing 5. JSTL을 사용한 JSP
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>All Users</title>
</head>
<body>
<table>
<tr>
<td>UserID</td>
<td>UserName</td>
<td>Name</td>
</tr>
<c:forEach items="${users}" var="user">
<tr>
<td><c:out value="${user.id}"/></td>
<td><c:out value="${user.userName}"/></td>
<td><c:out value="${user.firstName}"/> <c:out
value="${user.lastName}"/></td>
</tr>
</c:forEach>
</table>
</body>
</html>
|
<c:forEach> 태그는 사용자 목록을 순회하며, <c:out> 태그는 데이터 출력을 위해 자바 객체에 접근한다.
태그에 사용된 ${users}와 ${user.id} 같은 표현식은 JSTL 표현식 언어(Expression Language, EL)에 의해 해석된다. 예를 들어 EL은 ${users}라는 문자열을 해석해 pageContext, request,
session, servlet (application) 컨텍스트에서 여러 개의 접근 가능한 객체 중에서 users라는 속성을 찾는다. JSP 컴포넌트는 EL을 JSTL 태그 밖에서도 사용할 수 있도록 진화했고, 그 결과 Listing 6과 같은 JSP 컴포넌트가 나왔다.
Listing 6. JSTL과 EL을 사용한 JSP
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>All Users</title>
</head>
<body>
<table>
<tr>
<td>UserID</td>
<td>UserName</td>
<td>Name</td>
</tr>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.userName}</td>
<td>${user.firstName} ${user.lastName}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
|
이것이 JSP 2.0 스펙에서 처음 등장한 현대적인 JSTL과 EL의 형태다. 모델 2 아키텍처와 결합되면(아파치 스트러츠 등의 다양한 UI 프레임워크들이 구현한), JSTL과 EL의 조합을 통해 JSP 컴포넌트를 본질적으로는 자바에 독립적으로 만들 수 있다. 자바 개발자가 아니라도 JSP 컴포넌트를 만들 수 있게 되었고 자바 개발자는 응용 프로그램의 비지니스 로직에 집중할 수 있게 되었다.
JSF 기술
그러나 JSP 기술은 단지 웹 기술일 뿐 아니라 기업용 자바 아키텍처의 일부이기도 하다. JSP 2.0 스펙이 나온 직후 JSF 기술도 등장했다. JSF는 컴포넌트 아키텍처를 바탕으로 설계되었다. 웹 페이지 상의 객체는 생명 주기와 결합된 자바 객체를 가진 컴포넌트로 표현된다. 예를 들어, 이 기사의 JSP 예제에 JSF를 사용하면 자바 객체와 뷰 컴포넌트를 직접 결합할 수 있다. 그 결과로 만들어진 JSP 컴포넌트가 Listing 7이다.
Listing 7. JSF를 사용한 JSP
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http:.//java.sun.com/jsf/core" prefix="f" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>All Users</title>
</head>
<body>
<f:view>
<h:dataTable id="users" value="#{UserBean.users}" var="user">
<h:column>
<f:facet name="header">UserID</f:facet>
<h:outputText value="#{user.id}"/>
</h:column>
<h:column>
<f:facet name="header">UserName</f:facet>
<h:outputText value="#{user.userName}"/>
</h:column>
<h:column>
<f:facet name="header">Name</f:facet>
<h:outputText value="#{user.firstName}"/> <h:outputText
value="#{user.lastName}"/>
</h:column>
</h:dataTable>
</f:view>
</body>
</html>
|
dataTable을 자바 객체와 결합하는 방법을 주목하라. 단지 테이블의 컬럼을 정의하는 것만으로 컴포넌트가 행을 순회할 수 있다. 관리되는 배킹 빈(managed backing bean, 여기서는 UserBean)이 데이터를 갖고 있다고 가정한다. dataTable 컴포넌트가 HTML을 만들어 내므로 테이블을 위한 HTML이 전혀 필요 없다. 이것이 JSF의 장점 중 하나다.
JSF와 JSP 1.2
JSF는 항상 JSP 기술을 활용하고 있다. 소개될 당시에는 JSP 1.2가 가장 널리 사용되는 JSP 버전이었기 때문에 JSF는 JSTL과 EL을 지원하지 않았다. <h:dataTable>과 <h:outputText> 태그의 value 속성을 유심히 봤다면 JSTL에서 사용되는 EL과 비슷하다는 것을 눈치챘겠지만, 이것은 단지 겉보기만 비슷할 뿐이다. JSF 1.0에서는 JSTL의 EL과 비슷하게 동작하는 고유의 EL을 갖고 있으며, 나중에는 JSP 기술을 계승했지만, 이 때까지는 JSF EL과 JSTL EL은 호환성이 없었다.
통합 표현식 언어
자바 EE 5의 핵심 중 하나는 통합 표현식 언어다. JSTL과 JSF의 EL이 합쳐져 Listing 8처럼 JSTL과 JSF를 혼합해 쓰는 것이 가능해졌다.
Listing 8. JSF와 JSTL의 혼합
<f:view>
<c:forEach items="${UserBean.groups}" var="group">
${group.groupName}
<h:dataTable id="#{group.groupId}" value="#{group.users}"
var="user">
<h:column>
<f:facet name="header">UserID</f:facet>
${user.id}
</h:column>
<h:column>
<f:facet name="header">UserName</f:facet>
${user.userName}
</h:column>
<h:column>
<f:facet name="header">Name</f:facet>
${user.firstName} ${user.lastName}"
</h:column>
</h:dataTable>
</c:forEach>
</f:view>
|
Listing 8은 JSTL을 사용하여 여러 그룹을 순회하는 예를 보여준다. 그 안에서 각 그룹을 위한 테이블을 만들고, 그 그룹에 속한 사용자 목록을 표시한다. 데이터를 참조하기 위해 JSTL 태그(<c:forEach>)와 JSP 컴포넌트에 직접 사용된 EL 표현식과 JSF 컴포넌트(<h:dataTable>)에서 쓰인 표현식의 차이를 주목하라. JSTL과 JSF EL을 혼합해 사용할 수 있게 되어 양쪽 모두 만족스러운 결과를 얻었다.
자바 EE 5의 일부로서 EL
자바 EE 5의 EL에 큰 변화가 하나 더 있다. J2EE 1.4 스펙까지는 JSTL 구현이 필수 사항이 아니었다. 웹 응용 프로그램 개발자는 사용할 JSTL 구현을 선택할 수 있었으며, JSF 구현만 선택할 수도 있었다.
자바 EE 5 스펙에서는 JSTL 구현이 필수 사항이다. 웹 응용 프로그램 개발자는 어떤 구현체를 사용할 지 고민하는 대신, 망설임 없이 JSTL을 사용할 수 있게 되었으며, JSF의 힘을 활용할 수 있게 되었고, 여기에서 파생된 통합 표현식 언어는 자바 EE 5 스펙에 포함되었다.
제로니모와 글래스피시 JSTL
앞 절에서, 과거에는 웹 응용 프로그램 개발자가 JSTL 기술을 사용할지를 선택해야만 했다는 사실을 언급했다. 사용하기로 결정했다면, 다양한 JSTL 구현체 중에서 어떤 JSTL 구현체를 사용할지도 선택해야 했다.
자바 EE 5에서는 JSTL 구현체가 애플리케이션 서버에 포함되어 있다. 즉 모든 자바 EE 5 스펙 구현체는 JSTL 구현체를 포함해야 한다. 아파치 제로니모 개발자들은 자바 EE 5 구현체인 제로니모 2.0을 만들 때, JSTL 구현체를 포함해야만 했다.
그러나 기존 구현체 중에서 선택할 수 없었다. 통합 표현식 언어는 JSTL 구현체의 주요 요구사항이었지만, 많은 JSTL 구현체는 JSF와 함께 동작할 수 있도록 설계되어 있지 않았다. 다행히도 제로니모 팀은 직접 JSTL과 통합 표현식 언어 구현체를 만드는 대신 썬의 글래스피시를 활용할 수 있었다.
글래스피시가 친숙하지 않은 독자들을 위해 간단히 설명하면, 글래스피시는 자바 EE 5 스펙을 위한 썬의 참조 구현체다. 이 오픈 소스 응용 프로그램은 썬의 CDDL(Common Development and Distribution License)과 GNU의 GPL(GNU General Public License), 두 가지 라이선스를 따른다. 썬은 항상 JSF 참조 구현을 제공해 왔으며, 통합 표현식 언어를 포함한 JSTL의 참조 구현도 신속하게 제공했다. 글래스피시는 오픈 소스이기 때문에 제로니모 팀은 이들의 노력을 활용해 글래스피시 JSTL 구현을 제로니모 2.0에 포함시킬 수 있었고, 첫 번째 마일 스톤인 제로니모 2.0-M1에 포함되었다.
라이선스에 대한 고려 사항
글래스피시는 CDDL과 GPL 두 가지 라이선스를 따르기 때문에 제로니모에 포함될 수 있었다. 그러나 제로니모의 나머지 부분들은 아파치 스타일의 라이선스를 따르기 때문에 제로니모 팀은 몇 가지 제약을 받았다.
기본적으로 제로니모는 글래스피시 JSTL을 포함하지만 그 소스 코드를 포함하지 않는다. 더욱이 제로니모 팀은 글래스피시 JSTL에 기여할 수도 있고, 새 바이너리를 제로니모와 함께 패키징할 수 있지만, 그 소스 코드를 변경할 수는 없다.
제로니모의 멋진 점 중 하나는, 코드를 변경하고 그렇게 커스터마이즈된 버전의 제로니모를 배포할 수 있다는 것이다. 그러나 JSTL 구현체는 여기에서 명백히 예외다. 그 소스 코드는 제로니모에 포함되어 있지 않으며, 그 라이선스는 커스터마이즈된 버전 배포를 제한한다. 예를 들어 글래스피시의 소스 코드를 변경했다면, 변경된 결과도 글래스피시와 동일한 라이선스를 따라야 한다.
요약
자바 기술에서 웹 기술 진화는 수 년 동안 개발자들에게 엄청난 이득을 가져다 주었다. 최근의 진화인 통합 표현식 언어는 JSTL과 JSF 기술을 혼합해 사용할 수 있도록 함으로써 더 큰 이득을 보장한다. 자바 EE 5 스펙의 중요한 부분인 통합 표현식 언어는 제로니모 2.0에서도 중요한 부분이다. 제로니모는 스펙을 구현하는 대신 스펙을 위한 참조 구현을 사용함으로써 다시 한번 개발자들에게 도움이 되었다. 글래스피시 JSTL과 제로니모 2.0을 함께 사용함으로써, 개발자들은 자바로 웹 응용 프로그램을 구축하기 위한 또 하나의 선택을 갖게 되었다.
참고자료 교육
제품 및 기술 얻기
토론
필자소개  | 
|  | Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발하고 있으며 이베이에서 근무중이다. 캘리포니아 공대에서 수학을 전공하였다. |
기사에 대한 평가
|