 |  |
|
난이도 : 중급 Dan Allen, Senior Java engineer, CodeRyte, Inc.
2007 년 7 월 10 일 JavaServer Faces (JSF)는 자바™ 웹 애플리케이션을 위한 최초의 표준 사용자 인터페이스 프레임웍입니다. Seam은 JSF를 확장한 강력한 애플리케이션 프레임웍입니다. Seamless JSF 시리즈 첫 번째 기술자료에서는 이 두 개의 프레임웍이 갖고 있는 강력한 힘을 발견하고자 합니다. Dan Allen은 JSF 라이프 사이클에 Seam이 어떤 기여를 했는지를 설명하고, 정황상의 상태 관리, RESTful URL, Ajax 리모팅, 적절한 예외 핸들링, "Convention over Configuration" 에 대해 설명합니다.
JSF는 자바 웹 표준이라는 위상 때문에 자바 웹 애플리케이션 시장을 선도하기 시작했다. JSF를 기반으로 애플리케이션을 구축하는 것이 의무화 되었기 때문에, 점점 더 많은 개발자들은 핵심 스팩에 관심을 갖기 시작했다. JSF는 웹 애플리케이션 프레임웍으로 기획된 것은 아니다. 오히려, 튼튼한 이벤트 중심의 API와 UI 컴포넌트 라이브러리를 제공하여, 이를 기반으로 보다 완벽한 애플리케이션 프레임웍을 구현하도록 한다.
JSF의 컴포넌트 중심 아키텍처를 보완할 수 있는 알맞은 확장 프레임웍을 찾던 중, Shale과 Struts 2를 생각해 냈다. Struts 2는 JSF를 의붓자식으로 밖에 취급하지 않기 때문에 Struts 2는 배제하기로 한다. Shale은 근본적으로 JSF 기반이지만, 여기에도 한계가 있다. 반면, JBoss Seam은 포괄적인 애플리케이션 프레임웍으로서 궁극의 목표를 타협하지 않으면서도 JSF를 토대로 하고 있다.
 |
시리즈 소개
Seamless JSF는 JSF에 정말로 잘 맞는 최초의 애플리케이션 프레임웍인 Seam에 대해 설명합니다. 이 글을 읽고 Seam이 과연 JSF를 보완할 수 있을지를 여러분 스스로 평가해 보기 바랍니다. |
|
세 편의 시리즈에서는 Seam 애플리케이션 프레임웍과 이것이 지닌 강점을 소개하고, 자바 엔터프라이즈 애플리케이션을 개발할 때 이것이 어떻게 JSF의 대안이 될 수 있는지를 설명합니다. 본 시리즈를 읽기 전에 참고자료 섹션에서 Seam을 다운로드 하기 바란다.
Seam을 찾아서
 |
Shale에 대하여
Shale은 평탄치 않은 역사를 지녔다. Struts-JSF 통합 패키지에서 탄생했지만, 나중에는 Struts 개발자로부터 외면당했다. 오늘날에는 JSF에 전념하고 있는 Apache 프로젝트의 일부로 존재하고 있다.
필자가 Shale에 대해 이야기 하고 싶은 것은 약결합 서비스로서의 그 위상이다. 여기에서 통합이라는 부담은 개발자가 안고 가야 한다. Shale의 자바 5 언어 향상은 뛰어나지만, 모두 확장 패키지에 있다. 네이밍 규약을 통한 단일 템플릿으로의 뷰 컨트롤러의 커플링 역시 매우 제한적이다. Shale 애플리케이션 컨트롤러와 다이얼로그 매니저 모두 표준 JSF 라이프 사이클의 효과를 줄인 애드온일 뿐이다.
Seam은 Shale에서 기대했던 모든 기능들을 제공하면서도, 잘 통합된 딱 맞는 패키지의 형태로 탄생했다.
|
|
JBoss Seam에 대한 아티클의 단 한 페이지만 읽은 후에(참고자료), 필자는 Seam이야 말로 그 동안 찾고 있었던 프로젝트였다는 것을 알게 되었다. Seam 개발자인 Gavin King은 웹 애플리케이션 프레임웍은 초반부터 매우 힘든 문제들을 공략해야 한다는 것을 깨닫는 데까지 힘든 시간을 보냈다고 기록했다. 여기에는 정황적 상태 관리, 사용자 친화적인 RESTful URL, Ajax 리모팅(remoting), 적절한 예외 핸들링, Convention Over Configuration 등이 포함된다. 다행스럽게도, 자바 개발자들에게 Seam은 이러한 모든 요구 사항들 그 이상을 제공한다. 여러분이 JSF를 사용하고 있지만 Seam에 대해 들어본 적이 없다면, Seam 문서를 참고하기 바란다. (참고자료) Seam의 매뉴얼도 좋은 참고자료이다.
JSF를 보완할 수 있다는 확실성에도 불구하고, Seam은 쟁쟁한 경쟁자들과 많은 차이를 경험해야 했다. Shale과 Struts 2를 포함하여 웹 애플리케이션 프레임웍이 포화 상태에 이른 시장에서, 신참자들은 애송이 취급을 받기 마련이고, Seam은 확고한 기반을 아직 다지지 못했다. Seam의 채택이 느려진 또 다른 이유는 프레임웍에 대한 미신 때문이었다.
필자가 근절하고 싶은 미신 중에는, Seam이 EJB3와 함께 사용될 때에만 유용하다거나 Seam으로 애플리케이션을 개발하기 위해서는 EJB3 컨테이너가 필요하다는 것이다. 사실, Seam 문서에는 이러한 오해를 명백히 해명하고 있다. "Seam에서는 EJB 컴포넌트를 요구하지 않으며, EJB 3.0 호환 컨테이너 없이도 사용할 수 있다." EJB3도 사용하고 있다면 Seam만 사용할 수 있다고 주장하는 것은 Hibernate도 사용할 수 있다면 Spring만 사용할 수 있다는 의미와 같은 뜻이 된다.
EJB3
Seam은 중요한 후크와 컴포넌트 관리 프로세스를 기본 JSF 라이프 사이클에 추가했다. 이것을 EJB3와 완벽히 분리하여 사용할 수 있다. 하지만, EJB3와 마찬가지로, Seam은 컴포넌트 선언에 JDK 5 주석 메타데이터에 의존하기 때문에, 이것을 사용하기 위해서는 Java 5 호환 JVM도 사용해야 한다. 그림 1은 Seam POJO 구현용 애플리케이션 스택이다.
 |
JBoss Seam과 JSR 299
JBoss Seam은 오픈 소스 애플리케이션 프레임웍으로서 JSF와 EJB3와 Spring 빈 같은 비즈니스 컴포넌트들간 통합을 향상시킨다. Seam은 웹 환경의 다양한 정황 속에서 컴포넌트를 관리하고, JSF 애플리케이션 개발에 필요한 XML 설정을 줄인다. 이 프로젝트는 Hibernate의 창시자인 Gavin King이 만들었으며 현재 JBoss 연구실에서 호스팅 되고 있다.
최근에 JBoss는 JCP에 Seam의 개념을 표준화 할 것을 제안했다. 이 제안서는 JSR 299, Web Beans로서 수락되었다. 이 스팩의 목적은 JSF에서 관리되는 빈 컴포넌트 모델을 EJB3 컴포넌트 모델과 통합하여 웹 기반 애플리케이션의 프로그래밍 모델을 단순화 하는 것이다.
|
|
그림 1. Seam POJO 애플리케이션 스택
EJB3 jar 또는 디스크립터 파일에 대한 참조 없이도 Seam을 충분히 활용할 수 있다. Seam과 POJO를 사용할 때, 이 프레임웍은 컴포넌트 인스턴스화에 대한 완벽한 제어권을 가지며, 특별한 설정이 필요 없다. Seam은 EJB3의 메커니즘에 의존하는 대신에 대부분의 Java 5 주석 프로세싱을 핸들한다. EJB3 컨테이너에 의존하는 주석들은 이 환경에 맞게 도정된다. 어떤 경우, EJB3 커플링 없이 Seam을 현재 IT에 통합하면 비용적인 측면에서 혜택을 누릴 수 있다. 여러분이 Seam을 사용하는 방법은 단순한 선호도의 문제이다.
설정과 실행
거대한 자바 프레임웍이 있고 시간이 한정된 상황에서, 이것이 통합하기 어려운 것이라면 Seam은 성공할 가망이 없다. 다행히도, Seam을 프로젝트에 추가하는 것은 간단하다. JSF 라이프 사이클은 Seam 애플리케이션의 중심 부분이기 때문에 교육 기간을 견딜 필요가 없다. 네 개의 jar 파일들을 추가하고, 서블릿 리스너와 JSF 단계 리스너를 등록하고, 빈 자바 프로퍼티 파일에 전달한다. 설정이 완료되면 네이티브 JSF 애플리케이션을 한 번에 하나의 관리 빈을 Seam으로 이동할 수 있다.
Seam을 사용하기 위해 가장 먼저 해야 할 일은 필요한 jar 파일을 프로젝트에 추가하는 것이다. Hibernate를 사용하지 않거나 최신 버전으로 업그레이드 하지 않았다면, 이것 역시 설정 단계에 포함시킨다. Hibernate 3.2 distribution에서 jar를 포함시켜야 하고 이것의 수 많은 종속물들도 포함시켜야 한다. Seam은 데이터 밸리데이션에 Hibernate 주석도 사용하기 때문에 메인 Hibernate jar 외에도 확장 jar도 포함시켜야 한다. Seam 배포판에 필요한 라이브러리는 jboss-seam.jar와 jboss-seam-ui.jar는 물론, 두 개의 지원 라이브러리인 Javassist (자바용 로드 반영 시스템)와 Java Persistence API이다. 그림 2의 프로젝트 트리는 Seam 프로젝트에서의 jar를 나타내고 있다. 이 그림에 나타난 추가 라이브러리 대부분이 JSF의 MyFaces를 지원한다.
그림 2. Seam 프로젝트의 라이브러리
Seam 설정하기
다음 단계는 web.xml 파일에 서블릿 리스너 클래스를 설치하는 단계이다. (Listing 1) 이 리스너는 애플리케이션이 전개될 때 Seam을 시작한다.
Listing 1. Seam 서블릿 리스너 설정
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
|
다음에는, JSF 단계 리스너를 faces-config.xml 파일에 추가한다. (Listing 2) 이 리스너는 Seam과 표준 JSF 라이프 사이클을 통합한다. (그림 3에는 Seam이 이 라이프 사이클을 어떻게 향상시켰는지를 보여주고 있다.)
Listing 2. Seam 단계 리스너 설정
<lifecycle>
<phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
</lifecycle>
|
마지막으로, 비어있는 seam.properties 파일을 클래스 경로의 루트(root)에 두어, Seam에게 로딩을 명령한다. (Listing 3) 빈 파일은 JVM 클래스로더 최적화로서 사용되어 Seam에게 좁은 클래스 경로 영역을 제공하는데, 여기에서 컴포넌트를 검색하여 로딩 시간을 현격히 줄인다.
Listing 3. Seam 프로퍼티 파일
# The mere presence of this file triggers Seam to load
# It can also be used to tune parameters on configurable Seam components
|
물론, 이러한 미니 설정 상황에서는 Seam의 많은 기능들을 사용할 수 없다. 여기에서는 Seam이 엔트리 레벨 사용에 매우 작은 풋프린트를 갖고 있다는 것을 보여줄 뿐이다. 예를 들어, Seam에는 JSF 라이프 사이클 이상으로 기능 범위를 확장하는 서블릿 필터가 포함되어 있다. 이 서블릿 필터에는 비 JSF 요청들과의 통합, 리다이렉션 이상의 파급 규약, 파일 업로드 관리 기능 등이 포함된다. 참고자료 섹션에서, EJB3 통합과 관련한 추가 기능을 제어하는 설정 파일에 대해 다루고 있는 Seam 참조 문서를 참고하기 바란다.
Seam으로 보완하기
Seam에서 관리 빈을 개발하는 것은 전형적인 JSF 설정 프로세스와 비교해 볼 때 놀라울 정도로 쉽다. 빈을 JSF 라이프 사이클에 노출하려면 클래스 정의 위에 하나의 주석, @Name을 추가하면 된다. Seam은 여기서부터 컴포넌트의 가시성과 라이프 사이클을 관리한다. 무엇보다도, 여러분이 faces-config.xml 파일에서 빈을 정의할 필요가 없다.
@Name 주석과 @DataModel, @DataModelSelection, @In, @Out, @Factory는 Listing 4에 묘사되어 있다. 이러한 주석들은 뷰 템플릿과 Seam 컴포넌트 간 변수의 양방향 흐름을 가능케 한다.
Seam 용어로, 이러한 액션을 bijection이라고 하는데 이는 양방향 투입(bidirectional injection)을 줄인 말이다. 프로퍼티 데이터가 밖으로 나가게 되면(outjected), 이것은 이름에 의해 뷰에 직접 보이게 된다. 데이터는 포스트백(postback) 또는 컴포넌트가 시작될 때 컴포넌트로 투입(injected)된다. 후자는 잘 알려진 inversion of control (IOC) 패턴이고 위임 객체들을 묶을 때 사용될 수 있다. 전통적인 IOC와 Seam의 bijection간 큰 차이점은 bijection은 장기적인 범위에 있는 컴포넌트들이 단기적인 범위에 있는 컴포넌트에 대한 레퍼런스를 가질 수 있다는 점이다. Seam은 컴포넌트가 호출될 때마다 의존성을 정하기 때문에 연결이 가능하다. bijection은 Stateful 컴포넌트 개발의 머릿돌과 같은 존재이다.
확실히, Listing 4의 POJO 빈은 Seam의 표면만을 건드릴 뿐이다. 향후 시리즈를 통해서 Seam 구현 방식을 설명하도록 하겠다.
Listing 4. 일반적인 Seam POJO 빈
@Name("addressManager")
public class AddressManagerBean {
@DataModel
private List<Address> addresses;
@DataModelSelection
@Out( required = false )
private Address selectedAddress;
@Factory( value = "addresses" )
public void loadAddress() {
// logic to load addresses into this.addresses
}
public String showDetail() {
// no work needs to be done to prepare the selected address
return "/address.jspx";
}
public String list() {
return "/addresses.jspx";
}
} |
Spring의 투입
기존 Spring 컨테이너에 의해 관리되는 서비스 레이어 객체들을 활용하려면 관련 비즈니스 로직을 처리하는 Spring 빈들을 Seam 컴포넌트에 투입해야 한다. 먼저, Spring-JSF 통합이 설정되었는지를 확인한다. 이것은 Spring 프레임웍에 포함된 커스텀 변수 리졸버에 의해 다루어진다. (참고자료) 이렇게 상관 관계가 설정되었다면, Spring과 Seam의 통합은 @In Java 5 주석과 변수 바인딩 식을 함께 사용하여 Seam 컴포넌트의 어떤 프로퍼티가 Spring 빈의 투입을 받아야 하는지를 정하면 된다. (Listing 5) (Seam의 향후 버전에는 값 바인딩 식과 관련한 Spring의 커스텀 네임스페이스가 포함될 것이다.)
Listing 5. Spring 빈 투입하기
@Name("addressManager")
public class AddressManagerBean {
@In("#{addressService}")
private AddressService addressService;
}
|
이 설정은 Spring의 경우, 경량의 컨테이너를 사용하여 설정되는 Stateless 서비스 및 데이터 액세스(DAO) 레이어의 사용을 지원한다. EJB3가 필요하지 않으므로, 전개 대상은 기본 서블릿 컨테이너가 될 수 있다.
Seam- JSF 구현을 살펴보았으니, 이제는 JSF를 사용할 때의 문제점과 Seam에서 이러한 문제를 해결하는 방법을 자세히 설명하도록 하겠다.
다시 JSF로..
Seam이 JSF에 어떤 혜택을 가져왔는지를 확실히 이해하려면, 웹 기반 프로그래밍에 대해 JSF가 다른 방식들과 어떻게 다른지를 이해해야 한다. JSF는 기존 Model-View-Controller (MVC) 아키텍처를 구현하는 웹 프레임웍이다. 차이점이라고 하면 이 패턴의 풍부한 구현을 들 수 있다. JSF의 MVC는 Struts, WebWork, Spring MVC 같은 프레임웍에서 사용되었던 Model 2 또는 "push-MVC" 접근 방식보다는 전통적인 GUI 애플리케이션 스타일에 가깝다. Struts, WebWork, Spring MVC 프레임웍들은 액션 기반(action-based)으로 분류되는 반면, JSF는 컴포넌트 모델(component
model) 기반의 새로운 프레임웍으로 간주된다.
액션 기반 프레임웍을 "push" 모델을 사용하는 것으로 생각하고, 컴포넌트 프레임웍은 "pull" 모델을 사용하는 것으로 생각한다면 이해하기 쉽다. 전면에서 페이지 요청에 주도적인 역할을 취하는 대신(액션 기반 프레임웍), 컴포넌트 프레임웍의 컨트롤러는 요청 라이프 사이클의 뒷 부분에서 뷰 안에서 데이터 제공 메소드를 호출한다. 게다가, 컴포넌트로 알려진 페이지 상의 엘리먼트는 서버 측 객체에 메소드 호출을 실행할 수 있는 이벤트에 연결되어 같은 뷰를 다시 디스플레이 하거나 또 다른 페이지로 이동한다. 따라서, 컴포넌트 프레임웍은 이벤트 중심(event-driven)으로 분류된다. 컴포넌트 프레임웍은 이벤트들간 통신하는데 사용되는 요청-응답 프로토콜을 요약한다.
이벤트 중심 방식은 하나의 메소드로 전면에서 수행되어야 하는 작업량을 줄여서 뷰의 렌더링을 준비하기 때문에 유용하다. 컴포넌트 프레임웍에서, 호출은 UI 이벤트의 직접적인 결과이거나 값-바인딩 식이 된다.
애플리케이션이 중간 단계의 성숙도에 이르면, 페이지 상에 관련이 없는 많은 액티비티를 필요로 한다. 이 모든 정보들을 단일 액션 또는 연쇄적인 액션으로 몰아간다면 관리는 어려워진다. 결국, 개발자들은 객체 지향 모델에서 절차 프로그래밍(procedural programming) 스타일의 코딩 방식을 모색하게 된다.
Seam과 JSF
JSF와 컴포넌트 프레임웍의 기초에 대해 충분히 이해했는가? 많은 개발자들이 최근에 깨달은 문제는 JSF로 전향하는 것이 순탄하지만은 않다는 점이다. 컴포넌트 모델을 채택한다면 완전히 새로운 문제들이 생길 것이다. JSF가 액션 기반 프레임웍처럼 작동해야 할 때가 있지만, 네이티브 JSF에서는 이는 가능하지 않다. 적어도 모든 요청을 실행하는 단계 리스너를 사용하지 않고서는 불가능하다.
JSF의 또 다른 단점 중에는 HTTP 세션에 대한 과도한 의존(페이지 시퀀스마다 데이터를 내보낼 때), 피상적인 예외 핸들링, 북마킹 지원의 부족, 엉성한 XML 설정 등이 있다. Seam은 JSF와 자연스럽게 통합하고, 이와 동시에 JSF 스팩 위원회가 거부하거나 간과했던 새로운 기능들을 추가함으로써 이러한 문제들을 해결하고 있다. Seam의 프레임웍은 매우 간결하고, 가독성 있으며, 재사용 가능한 코드를 사용하도록 하여 앞서 설명한 문제들을 관리한다. 그림 3은 애플리케이션의 코드를 단순화 하는데 도움이 되는 JSF 라이프 사이클에서의 Seam의 확장 포인트를 묘사하고 있다.
그림 3. Seam 라이프 사이클 향상
JSF 개발의 일반적인 문제들을 짚어가면서 어떤 부분이 향상되었는지를 살펴보자.
조용한 설정
Seam은 Java 5 주석을 매우 실용적으로 사용하고 있다. Seam의 전개 스캐너는 seam.properties 파일을 포함하고 있는 모든 압축 파일들을 검사하고 @Name 주석으로 표기된 클래스에 대한 Seam 컴포넌트를 생성한다. 자바 언어는 코드 레벨에 메타데이터를 추가하는 신택스가 부족하기 때문에 많은 XML 설정들이 고안되었다. 주석이 Java 5 스팩에 추가되었을 때, 더 나은 솔루션이 생겼다. 특정 애플리케이션에서 사용할 기본 빈들이 개발되기 때문에 이러한 빈들의 설정을 클래스가 아닌 파일로 "추출"할 이유가 없다. 게다가, 여러분이 다루어야 할 파일은 한 개 미만이다. Seam은 빈들을 JSF 라이프 사이클로 통합하는데 도움이 되는 전체 주석 카탈로그를 제공한다. (Listing 4)
페이지 액션과 RESTful URL
한 가지 아쉬운 점은 액션 기반 프레임웍에서 수행했던 것처럼 모든 요청을 전면에서 처리해야 한다. RESTful URL, 북마킹 지원, URL 패턴 보안, 페이지 흐름 밸리데이션 등이 대표적인 유스 케이스이다. 이러한 문제는 JSF를 사용하는 개발자들이 겪게 되는 주요한 문제들이다. 일부 JSF 벤더들은 onPageLoad 기능(참고자료)과 개발자 툴을 제공하여 이러한 문제를 해결하고 있지만, 이는 핵심 스팩이 아니다.
사용자가 아이템 상세를 스크린에 직접 요청할 때 일반적으로 어떤 일이 발생하는지를 생각해 보자. JSF 컨트롤러는 수동적인 방식으로 실행되기 때문에, 페이지가 렌더링을 시작하면 여러분은 사용자를 논리적 흐름의 시작 부분으로 다시 보낼 수 없다. 심지어 대상 데이터가 없다는 것이 명백할 때에도 마찬가지이다. 대신, 여러분은 blank 값으로 페이지를 디스플레이 할 수 밖에 없다.
처음에, 여러분은 직관적으로 페이지의 기본 Backing Bean에 "prerender" 메소드를 구현할 것을 생각했을 것이다. 하지만, 컴포넌트 프레임웍에서 Backing Bean과 페이지간 제휴는 반드시 일대일이 될 필요가 없다. 각 페이지는 혼합된 Backing Bean들에 의존할 수 있고, 이러한 빈들은 여러 다른 페이지들에서 사용될 수도 있다. 대신, 하나의 뷰 ID(/user/detail.jspx)를 상응하는 뷰 템플릿이 렌더링을 위해 선택될 때 호출하고자 하는 한 개 이상의 메소드로 제휴시키는 방법이 있다. 단계 리스너 방식을 사용할 수 있지만, 여기에는 그 기능이 현재 뷰와 단계에서 실행되어야 하는지 여부를 결정하는 커스텀 로직이 필요하다. 이러한 솔루션은 수 많은 과잉 로직을 양산할 뿐만 아니라 뷰 ID를 컴파일 된 자바 코드로 하드 코딩 해야 한다.
구원자, 페이지 액션
Seam의 페이지 액션은 렌더링 문제가 발생하기 전에 이 문제를 해결한다. 페이지 액션들은 Render Response 단계 직전에 페이지를 입력할 때 실행되는 메소드 바인딩을 사용하여 지정된다. /WEB-INF/pages.xml 설정 파일에 뷰 ID용 메소드 바인딩을 설정할 수 있다. (뷰 템플릿에 인접한 파일에 이들을 배치하고, 이름을 복제하고, 파일 확장을 *.page.xml로 대체한다.) 페이지 액션의 경우, 뷰 ID가 변할 수 있기 때문에 XML이 적합하다. JSF가 Apply Request Values 단계 동안 값 바인딩을 통해 포스트 데이터를 모델 객체로 매핑 하듯이, Seam은 임의의 요청 매개변수들을 페이지 액션 실행 전에 값 바인딩을 통해서 모델 객체로 매핑한다. 이러한 요청 매개변수 투입 설정은 페이지 액션 XML 선언 안으로 중첩된다. 페이지 액션 메소드 호출이 무효가 아닌 스트링 값을 리턴하면, Seam은 이를 검색 이벤트로 취급한다. 따라서, 액션 기반 프레임웍으로 마이그레이션 하지 않고도, 가장 중요한 기능을 모방할 수 있다. Seam에는 애플리케이션에 공통으로 사용되는 빌트인 페이지 액션 세트가 포함되어 있다. 이 세트에는 대화가 구축되었는지를 확인하는 액션, 대화를 시작, 중첩, 종료할 수 있는 액션, 예외를 핸들하는 액션, 신임을 증명하는 액션 등이 포함된다.
페이지 액션들은 JSF에 북마킹 지원을 실행하는 열쇠이다. Seam의 생성자들은 매직 요청 매개변수 actionMethod가 페이지를 입력할 때 메소드 호출을 실행하도록 함으로써 이러한 기능을 활용했다. 게다가 여러분은 북마킹 용 링크를 만들기 위해 추가 작업을 할 필요가 없다. Seam은 두 개의 컴포넌트 태그, s:link와 s:button을 사용하여 상세를 관리한다. 이러한 두 개의 태그들은 네이티브 JSF 카운터파트인 h:commandLink와 h:commandButton에 상응한다. 차이점은 Seam 컴포넌트 태그들로 조합된 링크들이 HTTP POST 폼 제출 모델이 아닌 HTTP GET 연산을 사용하여 요청을 실행한다는 점이다. 따라서, Seam에 의해 만들어진 링크는 북마킹에 더욱 친숙하고 개발자들에게는 훨씬 더 편리하다.
페이지 액션을 사용할 때 위치 바에 있는 URL이 언제나 한 페이지 뒤에 있기 보다는 보여지는 페이지에 상응한다는 점이다. (전자의 상황은 이들을 생성했던 같은 URL에 게시하도록 폼을 설정했기 때문이다. JSF는 서버 측 리다이렉션을 통해서 앞으로 나아가기 때문에 하나의 액션 후에 새로운 뷰를 반영하기 위해 업데이트 되지 않는다.) 페이지 액션의 유연성을 나타내고 싶다면, 이들을 사용하여 RESTful URL (/faces/product/show/10)을 만들 수 있다. 이렇게 하려면 페이지 액션 메소드를 뷰 ID "/product/show/*"로 매핑해야 하고, 여기에서 /faces 접두사는 JSF 서블릿-매핑 부분이다. 페이지 액션 메소드는 요청 URL을 찾아서 데이터 유형과 데이터 식별자를 파악하고, 그 데이터를 로딩한 다음, 적절한 템플릿으로 진행시킨다. 이 예제는 JSF 요청 URL과 뷰 템플릿 간 일대일 매핑이 반드시 필요한 것은 아니라는 것을 명확히 보여준다.
팩토리 컴포넌트
JSF에서 가장 크게 좌절감을 느끼는 것 중 하나는 사용자 실행 액션 또는 액션-리스너 메소드 내에서와는 달리, 뷰에 데이터를 준비할 시간이 없다는 점이다. 액션 메소드 안에 로직을 둔다고 해서 렌더링 전에 실행되리라는 보장도 없다. 페이지 뷰는 사용자 실행 이벤트에 의해서 언제나 선행되는 것은 아니기 때문이다.
예를 들어, JSF 애플리케이션에 대한 URL이 최초로 요청되었을 때 어떤 일이 발생하는지를 생각해 보자. 이 페이지에 서비스 레이어에서 가져온 데이터 컬렉션을 디스플레이 해야 한다면 JSF 라이프 사이클에서는 데이터를 내보낼 적합한 기회가 없다. 뷰에서 값 바인딩 식으로 매핑하는 Backing Bean의 getter 메소드 내에 로직에 의존할 수 있다고 생각할 수도 있다. 하지만, JSF가 이 식을 풀 때마다, 추가 호출이 발생하고, 이는 서버 레이어를 히트하게 된다. 페이지에 있는 몇 개의 컴포넌트만으로도 다섯 번 정도 getter 메소드가 호출될 수 있다. 확실히 이것은 성능의 측면에서 볼 때 적합하지 않다. 관리 빈에 개인 프로퍼티를 사용하여 상태를 관리하더라도 이러한 유스 케이스에 직면할 때마다 추가 작업을 해야 한다. 한 가지 솔루션은 Seam의 페이지 액션을 사용하는 것이다. Seam은 훨씬 더 쉬운 솔루션을 제공하고 있다.
Seam은 팩토리 데이터 프로바이더(factory data
provider)라고 하는 개념을 도입했는데, 이는 @Factory Java 5주석이라고 한다. 이 팩토리를 설정하는 두 가지 방법이 있지만, 최종 결과는 처음 요청될 때 정확히 한번만 데이터가 준비된다는 것이다. Seam은 같은 데이터에 대한 후속 요청들이 있을 경우, 룩업 메소드를 추가로 호출하는 대신 이전에 생성된 결과 세트를 리턴한다. 이 팩토리 데이터 프로바이더는 규약과 결합하여 매우 까다로운 검색을 수반하는 데이터의 단기 캐시를 제공한다.
Stateful 컨버세이션(Stateful conversation)
JSF에 관해 혼동하는 것들 중 하나는 상태 관리 장치이다. JSF 스팩에서는 이벤트가 대기하고 선택이 등록되는 시간 동안 액션을 받은 후에 페이지들이 "복원"되는 방법을 설명하고 있다. 이 스팩에 사용된 단어들을 조사해 보면 컴포넌트 트리가 포스트백에서 복원되는 동안, 그러한 컴포넌트들에 의해 사용되는 Backing Bean 데이터는 복원되지 않는다. 이 컴포넌트는 값 바인딩(#{value} 신택스를 사용하는 EL 식)을 스트링 리터럴로서 저장하는데, 이는 런타임에서 기반 데이터로 변환된다. 다시 말해서, 값이 페이지 또는 요청 범위 같은 단기 범위에 저장된다면 JSF 라이프 사이클이 Restore View 단계에 다다르는 시간까지 존재하지 않는다.
컴포넌트 트리에 값 바인딩 데이터를 저장하지 않을 경우 가장 큰 단점은 고스트 이벤트(ghost event) 효과이다. (참고자료) 이는 UIData 군에 있는 일시적인 부모 컴포넌트들에 의해 발생한다. 값 바인딩 식에 의해 참조되는 모델 데이터를 더 이상 사용할 수 없거나 복원되는 컴포넌트 트리에 앞서 변경되었다면 트리의 부분들이 누락될 수 있다. 이벤트가 이렇게 누락된 브랜치 안에 있는 컴포넌트에서 실행되었다면, 발견되지 않을 것이고 누락은 눈에 띄지 않을 것이다. (다만 개발자의 외치는 소리만 들릴 뿐이다. "왜 내 액션이 호출되지 않은 거야?")
소실된 이벤트들은 예외 조건처럼 보이지만, JSF 라이프 사이클에서는 어떤 빨간색 플래그도 없다. 이러한 컴포넌트들은 올바르게 복원될 안정적인 기반 데이터에 의존하기 때문에 JSF가 무엇이 잘못되었는지를 알기는 어렵다.
불행히도, JSF 스팩은 세션 범위(여러분은 심지어 이를 "편리한" 범위라고 부른다.)에 대부분의 Backing Bean을 배치하는 길로 개발자들을 인도하고 있다. 서버 관리자들은 "out of memory" 에러, 클러스터링 환경에서의 서버 유사성, 서버 재시작 시 직렬화 예외 등을 처리해야 한다. MyFaces Tomahawk 프로젝트는 t:saveState 태그로 고스트 이벤트에 대한 솔루션을 제공한다. 이 MyFaces 태그는 데이터(전체 Backing Bean 포함) 값 바인딩이 아닌 컴포넌트 트리의 일부로서 저장할 수 있도록 해준다. 이러한 솔루션은 다소 미숙하지만, 요청들 사이에 값을 전달하는 숨은 폼 필드를 사용하는 것을 모방했다. 이것은 또한 뷰와 컨트롤러간 강결합을 만든다. Seam 생성자들은 자바 서블릿 스팩의 세 가지 빌트인 콘텍스트(request, session, application)가 데이터 중심 웹 애플리케이션에 대해 충분한 범위를 구성하지 않았다는 것을 깨달았다. Seam에서는 컨버세이션 범위(conversation scope)라는 개념이 도입되었는데, 이는 페이지 흐름에서 시작과 엔드 포인트로 묶인 Stateful 범위이다.
Seam의 컨버세이션 범위(conversation scope)
"Seam은 Stateful 컴포넌트들의 사용을 강조한다." Seam 참조 문서에서 발췌한 이 문장은 Seam의 핵심 개념을 내포하고 있다. 웹 애플리케이션은 오랫 동안 Stateless였다. 이러한 개념은 HTTP 프로토콜의 Stateless 특성에 일부 기여했다. 대부분의 프레임웍들은 페이지의 렌더링으로 끝나기 전에 원샷 프로세싱(one-shot-processing)을 제공한다. 이러한 방식은 중요한 애플리케이션이 장기 실행 컨버세이션이 특정 유스 케이스를 만족시키도록 요구하기 때문에 상당한 저항을 불러 일으킨다. Stateful 콘텍스트를 필요로 하는 애플리케이션 예제에는 스토어 체크아웃 프로세스, 제품 커스터마이징, 멀티페이지 폼 마법사, 기타 리니어 인터랙션에 기반한 애플리케이션들이 있다. 이러한 케이스들은 URL 매개변수(RESTful URL)와 숨겨진 필드를 사용하여 확실히 제거하여 데이터를 페이지에서 페이지로 이동할 수 있지만, 이렇게 하는 것은 개발자들에게는 성가신 일이다. 또한 이러한 방식은 구식이다. 대부분의 웹 프레임웍은 Stateless 모델에서 작동하기 때문에 여러분은 아예 커스텀 솔루션은 생각하지도 않는다.
JSF는 HTTP 세션에 많이 의존함으로써 Stateful 콘텍스트를 도입하고 있다. 사실, 세션 범위의 Backing Bean으로 작동할 때 JSF 컴포넌트는 훨씬 잘 작동한다. 어떤 사람은 기대한 만큼 작동한다고 말할 수도 있겠다. 신중하게 디자인 하지 않으면, HTTP 세션을 과용하여 심각한 메모리 누수, 성능 병목 현상, 보안 문제를 만들어 낼 수 있다. 게다가, HTTP 세션을 사용하면 멀티탭 브라우저 환경에서 매우 이상한 작동을 만들어 내며, 신성한 Back 버튼을 망가뜨린다. 중요한 것은 JSF는 반만 충족시킨다는 점이다. 이것은 Stateful UI이고 컴포넌트 트리의 저장과 복원에 대한 모든 상세들을 다루지만, 데이터를 저장 및 복원할 때 어떤 지원도 제공하지 않는다. JSF는 Stateful UI를 가져왔고, 여러분은 Stateful 데이터를 가져왔다. 불행히도, 이들을 일치시키는 일은 순전히 여러분의 몫이다.
Seam 이전에는, Stateful 데이터를 갖는 유일한 방법은 HTTP 세션에 의존하는 것이었다. Seam은 이 문제를 수정하고, 완전히 새로운 컨버세이션 범위를 확립하여 JSF의 상태 관리 구도를 완성했다. JSF 라이프 사이클에 Seam을 추가함으로써, 컨버세이션 콘텍스트는 단일 브라우저 윈도우(또는 탭)으로 묶이고, 요청과 함께 제출된 토큰에 의해 구별된다. 컨버세이션 범위는 HTTP 세션의 독립 세그먼트를 사용하여 페이지들간 데이터를 이동시킨다. 컨버세이션 영속성을 위해 Seam은 HTTP 세션을 사용하는데, 이는 완전히 투명한 과정이다. Seam은 컴포넌트들을 HTTP 세션에 무리하게 내던지지 않는다. 대신, 세션 데이터의 세그먼트의 라이프 사이클은 Seam에 의해 신중하게 관리되고, 컨버세이션이 종료될 때 자동으로 비워진다. Seam은 bijection을 현명하게 사용하여 데이터들이 새롭고, 선언적인 방식으로 "웹 컨버세이션"의 각 페이지들 안팎으로 흐르도록 한다. Seam의 컨버세이션 범위는 HTTP 세션의 한계를 극복했다.
예외 핸들링
Seam의 생성자는 "JSF는 예외 핸들링에 있어서 매우 제한적이다."라고 했으며, 이는 맞는 말이다. JSF 스팩은 예외 관리는 취급하지 않으며, 이 책임을 서블릿 컨테이너에 전적으로 위임한다. 서블릿 컨테이너가 예외를 처리하게 되면 결과 에러 페이지에 디스플레이 될 수 있는 것이 제한되고 트랜잭션 롤백이 거의 불가능해진다. 에러 페이지는 요청-디스패처 전달 후에 디스플레이 되기 때문에 FacesContext는 더 이상 범위에 없고, 비즈니스 로직을 수행하기에는 너무 늦다. 최후의 보루는 Servlet API이고 javax.servlet.error.* 요청 애트리뷰트를 획득하여 무엇이 잘못되었는지에 대한 정보를 검색하는 것이다.
Seam은 고급의 예외 핸들링을 제공한다. 예외 관리는 주석 또는 설정 파일을 통해 이루어진다. @HttpError, @Redirect, @ApplicationException 주석은 예외 클래스의 맨 위에 놓여서 예외가 발생했을 때 어떤 액션이 취해져야 하는지를 나타낸다. 액세스가 가능하지 않은 예외 클래스들의 경우, XML 설정 옵션이 사용된다. Seam의 예외 매니저는 HTTP 상태 코드를 보내고, 리다이렉션을 수행하고, 페이지 렌더링을 수행하고, 컨버세이션을 종료하고, 예외가 발생할 때 사용자 메시지를 커스터마이징 하는 역할을 한다. JSF는 응답의 렌더링을 시작한 후에 액션 과정을 변경할 수 없기 때문에 이러한 예외들이 처리될 때 한계가 있다. 페이지 액션 같은 기타 Seam 기능들을 적절히 사용하면 전부는 아니지만 거의 모든 예외 상황이 응답 렌더링 전에 처리될 수 있다.
Ajax 리모팅
마지막 JSF 스팩의 릴리스가 Ajax의 등장과 거의 일치했기 때문에, JSF 프레임웍은 비동기식 JavaScript와 부분적인 페이지 렌더링에 아주 적은 지원만 제공한다. 얼마 동안, 두 개의 프로그래밍 스타일이 다른 것처럼 보였다. 결국, 부분적인 페이지 업데이트에 JSF PhaseListener 또는 컴포넌트 Renderer를 사용하는 솔루션이 제안되었다. 그때 당시에도 JSF가 Ajax를 채택하기는 더 어려울 것이라는 것이 명백했다. ICEfaces 같은 프로젝트들은 JSF 라이프 사이클을 페이지-서버 통신으로 대체했다. (direct-to-DOM 렌더링)
Seam은 JavaScript 리모팅(Ajax에 속하는 기술)에 고유의 접근 방식을 제공한다. Direct Web Remoting (DWR) 라이브러리의 그것과 약하게 병렬화 하는 것이다. Seam은 JavaScript가 서버 컴포넌트에 메소드를 직접 호출할 수 있도록 함으로써 클라이언트와 서버를 혼합한다. Seam 리모팅은 DWR 보다 훨씬 강력하다. 단산한 독립 엔드포인트와는 반대로 풍부한 정황상의 컴포넌트 모델로 액세스 할 수 있기 때문이다. 인터랙션은 JSF의 이벤트 중심 디자인을 기반으로 이루어지며, Swing 패러다임을 따른다. 최고의 부분은 이 기능에는 추가 개발 노력이 필요 없다. 컴포넌트의 메소드에 대한 간단한 주석 @WebRemote로 JavaScript에 액세스 할 수 있다. 서버 컴포넌트의 상태가 수정되면, 부분 렌더링이 Ajax4JSF 컴포넌트 라이브러리에 의해 핸들될 수 있다. 간단히 말해서, Seam 리모팅 라이브러리를 통해서 JSF은 인터랙티브 디자인을 이룩할 수 있다.
결론
Seamless JSF 시리즈에서 배웠던 것을 돌이켜 보면, Seam을 사용하지 않고 JSF에서 개발하는 것은 어리석은 일이다. JSR 299, Web Beans(참고자료)에 대한 투표 결과를 더 볼 필요도 없다. 조만간, Seam은 공식 스팩이 될 것이고, Java EE 스택은 "상당히 단순화 된 웹 기반 애플리케이션용 프로그래밍 모델"을 제공하게 될 것이다. 이는 JSF 개발자와 Seam 개발자들에게 굿뉴스가 아닐 수 없다. 자바 표준이라는 것을 들이대지 않고도, Seam은 확실히 JSF를 보완한다.
JSF에서 차이를 메우려면 Seam은 약간의 설정이 필요하다. 이러한 작은 노력으로 JSF 개발의 가장 복잡한 문제를 해결할 수 있다. 지금까지 이 글에서 설명한 것은 Seam의 서곡에 불과하다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| Barebones Seam project1 | j-seam1.zip | 13KB | HTTP |
|---|
Note - 본 프로젝트는 Maven 2 빌드 인프라스트럭처를 사용하고 있습니다. 빌드가 실행될 때 모든 종속물들은 온 디맨드 방식으로 제공됩니다.
참고자료 교육
- "Web 2.0 application made easy with Rational Application Developer V7" (Yury Kats, developerWorks, 2006년 12월)
- "불신자를 위한 JSF: JSF 소문의 실체 (한글)" (Richard Hightower,
developerWorks, 2005년 2월)
- "Facelets fits JSF like a glove" (Richard Hightower, developerWorks, 2006년 2월)
-
The Seam Reference Documentation
- "The Next Generation Web Framework" (Michael Juntao Yuan, Red Hat Application Stack, 2006년 9월)
- "Seam: The Next Step in the Evolution of Web Applications" (Norman Richards, Java Developers Journal, 2006년 2월)
-
The MyFaces Wiki FAQ
- "Creating onPageLoad functionality for JavaServer Faces using Facelets" (Alyssar, JSFCentral, 2007년 3월)
-
한국 developerWorks 자바 존: 자바 프로그래밍 관련 다양한 기술자료.
제품 및 기술 얻기
토론
필자소개  | 
|  | Dan Allen은 CodeRyte, Inc.의 자바 엔지니어이다. 열정적인 오픈 소스 옹호가이다. Cornell University를 졸업한 후에, 리눅스와 오픈 소스 소프트웨어의 세계에 매료되었다. 그 이후, 웹 애플리케이션에 열중하고 있으며, 지난 몇 년 동안, Spring, Hibernate, Maven 2, JSF 같은 자바 기술에 주력했다. (http://www.mojavelinux.com) |
기사에 대한 평가
|  |