=> 원문 : http://www.fawcette.com/javapro/2004_01/magazine/features/cschalk/

자바 웹 개발자는 페이지탐색과 웹을 위한 재사용 가능한 UI컴포넌트를 포함한 그들 자신의 프레임워크를 가져야 한다. 이것을 구현하기 위한 표준화된 방법이 없고 제공되는 생산품과 자바 기반의 웹 애플리케이션을 위한 비주얼한 방식을 지원하기 위한 향상된 개발툴또한 없다.

JavaServer Faces(JSF)는 이런 요구를 만족시킨다. Java Community Process(JCP)는 새로운 자바 스펙--127인 JavaServer Faces를 소개했다. JSF의 core개념과 간단한 JSF웹 애플리케이션을 개발하는 방법을 알아보자.

JSF Application Components#

J2EE웹 개발자는 JSF애플리케이션이 간단하나 이해하기 쉽다는것을 알게될것이다. JSF애플리케이션은 기본적으로 요구되는 컨포넌트의 집합인 기본적인 J2EE애플리케이션이다.

  • web.xml에 설정된 Faces servlet - faces servlet는 Model-View_Controller컨트롤러나 웹 애플리케이션 요소의 모든 요청을 위한 교통경찰처럼 작동을 한다. Struts개발자들은 Struts컨트롤러 서블릿과 매우 동일하다는것을 발견하게 될것이다.
  • faces설정 파일 - faces-config.xml라는 JSF설정 파일은 애플리케이션 컨포넌트와 탐색(navigation)모델을 위한 주된 설정파일처럼 Struts설정파일과 매우 유사하다.
  • 요청되는 JAR파일 - 모든 JSF애플리케이션은 애플리케이션의 WEB-INF/lib디렉토리에 jsf-api.jar, jsf-ri.jar, jstl.jar, standard.jar, commons-beanutils.jar, commons-digester.jar, commons-collections.jar, and commons-logging.jar과 같은 JAR파일이 필요하다.

상징적으로 JSF애플리케이션은 필요한 컨포넌트들(managed 모델 자바빈즈, UI를 표현하기 위한 JSP와 HTML파일)로 구성이 된다. 부수적으로 언급하면 JSF애플리케이션 UI들은 JSP페이지 보다 잘정의된 xml문서로 작성될수 있다. 배치를 위해 JSF애플리케이션은 특별히 설정된 run-time환경을 요구하지는 않는다. 하지만 톰캣 같은 표준적인 J2EE컨테이너에 혼자서 배치될수 있다. JSF API는 간단한 text labeling이나 input tags부터 좀더 복잡한 컴포넌트까지 애플리케이션을 위한 풍부한 UI컴포넌트의 라이브러리, 이벤트를 제어하고 validations과 UI과 관련이 없는 actions을 수행하는 JSF core태크 라이브러리, JSP안의 UI컴포넌트를 랜더링하기 위한 기본적인 HTML랜더러 kit tag라이브러리등의 툴들의 집합을 개발자에게 제공한다.

일반적인 JSF애플리케이션 개발 시나리오는 JSF컴포넌트와 함께 설정된 J2EE웹 애플리케이션을 포함한다. 웹 페이지 디자이너는 초기 정적으로 디자인된 JSP/HTML페이지의 시리즈속에서 애플리케이션을 디자인한다. 컨포넌트 개발자는 따로 개발된(custom) UI컴포넌트를 빌드하기 위한 옵션이나 존재하는 JSF UI컨포넌트의 새로운 랜더러를 가진다. 예를 들면 따로 개발된(custom)랜더러는 무선 마크업 랭귀지나 다른 클라이언트 기술처럼 HTML에 기반을 두지 않는 클라이언트를 위한 빌드 될수 있다. 마지막으로 웹 애플리케이션 개발자는 커스텀 컨포넌트와 데이터 관리또는 모델빈즈와 더불어 제공되는 JSF태그 라이브러리로 정적인 HTML디자인을 사용해서 완성된 JSF애플리케이션을 만든다.

지금 런타임--JSF생명주기프로세스(JSF life cycle process)시 JSF애플리케이션이 수행되는 방법을 보자. 이 프로세스는 웹클라이언트가 HTTP요청을 만들고 애플리케이션이 요청을 받아서 처리하고 다시 HTML로 응답을 하는 대부분의 웹 애플리케이션의 생명주기와 유사하다. JSF생명주기는 UI의 런타임 표현, 서버측에서 관리되는 UI컴포넌트 트리또는 런타임시 view를 관리하기 때문에 좀더 많은것을 포함한다. 생명주기는 JSF애플리케이션이 상태변화를 관리하고 이벤트를 제어하는데 좀더 확장성을 가지도록 허락한다. 추가적으로 JSF애플리케이션의 수행시간은 JSF애플리케이션의 안이나 밖에서 오는 요청들에 따라 다른 방식으로 HTTP요청을 관리해야만 한다.

Requests and Responses#

일반적으로 JSF애플리케이션은 두가지의 요청과 응답을 제공한다. JSF UI컴포넌트를 사용한 페이지로부터 서브밑된 폼같은 JSF페이지로 부터 보내진 faces요청, 요청-처리 생명주기의 형태의 JSF "render response"같은 JSF애플리케이션으로 부터 기본적으로 생성된 faces응답. 서블릿또는 non-JSF JSP같은 JSF가 아닌 애플리케이션 컴포넌트보 보내어진 non-faces요청. JSF컴포넌트를 사용하지 않은 JSP로부터의 응답같은 non-faces응답.

시나리오에 의해 JSF가 수행되는 방식. 예를 들면 non-faces요청이 faces응답을 발생하도록 할때 처음에 새로운 faces view를 생성하고 이것을 faces context내에 이것을 저장하고, faces응답을 수행하는 등의 어떤 작업들이 발생한다. faces view는 런타임 UI콤포넌트트리를 포함하고 이후 요청을 참조할것이다.

이후 faces요청을 받았을때 faces context내에 존재하는 view나 콤포넌트트리는 재구성되고 새로운 요청은 적용된다. 차후의 이벤트또는 유효성체크는 처리되고, 일치하는 faces응답은 수행된다.
figure1.gif

JSF애플리케이션의 생명주기 처리에 대한 가장 중요한 점은 전형적인 비상태(stateless)웹 애플리케이션에 대조적인 UI를 일관적으로 표현하는 관리된 컴포넌트 트리이다. 이 특징은 무거운 클라이언트(thick-client) 자바 애클리케이션이 되도록 JSF애플리케이션에 이벤트를 조절하고 전통적으로 같은 방식으로 상태를 변화시키는 능력을 부여한다.

자바 애플리케이션과 유사하게, JSF는 충분히 설정될수 있고 많은 방식으로 사용될수 있는 UI컴포넌트의 풍부한 라이브러리를 제공한다. JSF UI컴포넌트는 기본적으로 다른 웹 애플리케이션 관련 함수를 수행하는 자바 클래스이다. UI컴포넌트는 다른 방식으로 랜더러될수 있다. 예를 들면 UICommand컴포넌트는 URL 링크나 form버튼을 표현할수 있다. JSF UI컴포넌트는 전제된 표현자(renderer)의 집합이다. 그러나 당신 자신의 표현자(renderer)를 빌드하는것은 당신 자신의 컴포넌트와 마찬가지로 가능하다.

그래서 특정 표현자(renderer)와 특정 UI컴포넌트를 어떻게 사용하는가? JSF는 UI컴포넌트의 기본 사용법을 조절하기 위한 JSP태그 라이브러리를 제공한다. 제공된 JSF JSP태그들을 사용할때 UI컴포넌트 클래스는 특정 표현자(renderer)과 묶여지고 클라이언트에 컴포넌트를 표현한다. JSF의 JSP컴포넌트태그 라이브러리는 UI컴포넌트 클래스와 컴포넌트 표현자(renderer)의 유용한 조합을 제공합니다. 예를 들면, UI컴포넌트인 UICommand를 사용하기 위해 웹 애플리케이션 개발자들은 commmand_button태크또는 command_link태그를 사용할수 있다.

JSF개발자들은 JSF API의 core컴포넌트로 제한되지 않는다. 필요할때 개발자들은 특별한 필요에 좀더 적합한 UI컴포넌트로 확장하거나 새로 생성하기 위해 JSF API를 사용할수 있다. 이 확장성때문에, JSF는 많은 비의존적인 개발자가 JSP태크 라이브러리와 다른 컴포넌트가 공유하도록 커스텀JSF컴포넌트를 제공할수 있도록 계획하에 구조화 되었다.

간단한 로그인 애플리케이션 만들기#

JSF애플리케이션를 생성하기 위한 첫번째는 표준적인 디렉토리 구조와 web.xml설정 파일을 가지는 빈 J2EE웹 애플리케이션을 새로 생성하는것이다. WEB-INF/lib디렉토리에는 필요한 JAR파일이 들어가있다. 우리는 web.xml파일에 /faces/* URL패턴을 위한 faces서블릿과 서블릿맵핑을 지정한다.

<!-- Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet
</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup> 
</load-on-startup>
</servlet>

<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet
</servlet-name>
<url-pattern>/faces/*
</url-pattern>
</servlet-mapping>
컨텍스트 파라미터는 faces-config.xml파일에 정의한다.

<context-param>
<param-name>javax.faces.application.CONFIG_FILES
</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>

우리는 이것을 web.xml파일에서 참조하기 때문에 우리는 faces-config.xml파일을 /WEB-INF/디렉토리밑에 추가한다. 여기서는 빈 샘플 faces-config파일을 사용한다.

<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//  DTD JavaServer Faces Config \\
1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
<faces-config>
content goes here...
</faces-config>

우리는 지금 빈 JSF가능한 J2EE애플리케이션을 설정한다. 이 시점에 우리는 우리의 자바 클래스의 소스코드를 포함하는 J2EE웹 디렉토리에서 src디렉토리를 생성할 것이다. 극단적으로 유용한 단계는 자바 클래스를 컴파일하고 /WEB-INF/classes디렉토리로 그것들을 배치하기 위한 Ant빌드 스크립트를 생성하는 것이다. 당신의 Ant스크립트는 당신의 애플리케이션 서버에 당신의 애플리케이션 WAR파일을 자동적으로 생성하고 배치할수 있도록 설정할수 있다.

우리는 유저아이디와 패스워드필드를 로그인 화면에 표현하고 입력된 필드에 의존적으로 유효성을 체크하고 서브밋 처리를 하는 그리고 로그인 성공 페이지나 로그인 실패 페이지를 탐색하는 간단한 로그인 애플리케이션을 빌드한다. 이 애플리케이션이 간단함에도 불구하고, 이것은 JSF개념즉 JSF애플리케이션 설정, 기본적인 JSF태크 사용법, 컴포넌트 표현하기, 모델 데이터 객체를 관리하거나 표현하기, 유효성체크하고 에러 관리, JSF탐색적 모델, 리소스 번들및 메세지 국제화(지역화)를 소개한다. 이 프로세스는 다음과 같은 순서로 진행된다. 1) 데이터를 가지는 모델 객체를 생성 2) faces설정 파일에 관리빈(managed bean)선언 3) JSF태크를 사용한 페이지 생성 4) faces-config파일에 페이지 탐색(navigation)정의

우리의 로그인 애플리케이션은 유저아이디와 패스워드의 구개의 필드를 가진다. 우리는 데이터를 관리하기 위해서 간단한 자바 클래스인 loginServer를 만든다.

package jsflogin;

public class loginServer extends Object
{
String userid;
String password;

public String getUserid()
{
return userid;
}
public void setUserid(String newUserid)
{
userid = newUserid;
}
public String getPassword()
{
return password;
}
public void setPassword(String newPassword)
{
password = newPassword;
}
}

기본적인 데이터관리에 추가적으로 JSF는 특정 유저아이디와 패스워드값으로 로그인을 요청할때 유저요청액션을 수행할 로직을 추가하도록 허락한다. 이런 목적으로 우리를 로그인 요청을 처리하는 메소드를 추가한다. 로그인 폼이 서브밋 되었을때 logicAction()메소드는 유저아이디와 패스워드를 검토하고 문자열을 반환한다. 예를 들면 로그인 값이 scott와 tiger와 같다면 우리는 간단한 체크를 한다.

public String loginAction() 
{

if userid.equals("scott")  && password.equals("tiger") ) 
  return "success";
else 
  return "failure"

}

반환하는 것은 succes또는 failure의 문자열값이다. faces-config.xml파일에 정의된 탐색룰(navigation rules)이다. 나는 이것이 작동하는방식을 간단히 설명할것이다.

빈 추가하기#

우리의 모델빈인 logicServer를 참조하기위해 애플리케이션을 통해 로그인 필드의 최근값을 유지한다. 우리는 faces-config.xml파일에 관리빈 엔터티(managed-bean entity)를 추가한다.

<managed-bean>
<managed-bean-name>LoginServer
</managed-bean-name>
<managed-bean-class>
  jsflogin.loginServer
</managed-bean-class>
<managed-bean-scope>session
</managed-bean-scope>
</managed-bean>

지금 우리는 JSP페이지로부터 빈과 LogicServer.*같은 그것의 속성값(properties)를 참조한다. 우리는 빈의 범위를 정의할수 있다. 이 애플리케이션을 위해 우리는 세션을 사용한다.

우리의 모델객체는 끝났다. 애플리케이션의 view부분을 생성할 시간이다. 이것은 기본적으로 조금의 JSP페이지와 HTML첫 페이지로 구성된다. 몇개의 빈 JSP페이지(login.jsp, success.jsp, failure.jsp)생성한 후에 간단한 HTML페이지(index.html)를 생성한다. 이 인덱스 페이지는 faces서블릿맵핑을 가진 login.jsp로의 링크를 포함한다.

<a href="/jsflogin/faces/login.jsp">
  Click here to proceed...</a>

지금 로그인 페이지를 만든다. 이것은 로그인 서브및 버튼과 함께 유저아이디와 패스워드를 받아들이는 간단한 폼을 표현한다. figure2.gif
전통적인 웹 애플리케이션에서는 이 폼을 HTML으로 구성한다. 그러나 이 폼을 서로 다른 언어로 유동적으로 표현하고 어떤 JSF요청을 받을수 있도록 하기 위해서 우리는 JSF JSP태그 라이브러리를 사용해서 이 폼을 만든다. 커스텀태그 라이브러리를 사용하기 위해 우리는 JSF html과 JSF core태그 라이브러리를 소개하는 지시자(directives)를 추가한다.

<%taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 

리소스 번들에 접급하기 위해서 우리는 다음 코드를 추가한다.

<f:loadBundle basename="jsflogin.Resources" var="jsfloginBundle"/>

basename는 서로 다른 언어로 표현한 버전을 가지기 위해서 우리의 리소스 속성파일의 위치를 지정한다. 예를 들어 우리는 영어, 프랑스어, 그리고 스페인어 버전의 리소스 파일(resource.properties, resource_fr.properties, and resource_es.properties)을 가질것이다. 각각의 파일은 각각의 언어로 표현되는 애플리케이션에서 사용되는 메시지 문자열을 모두 포함한다. 우리의 로그인 애플리케이션은 완벽한 다중언어를 사용하기 위한 리소르 파일을 사용할것이다.

taglib와 리소스 번들을 정의한 후에 우리는 기본적인 HTML header와 body를 넣는다. body태그안에 우리는 첫번째 JSF태그인 <f:View>를 소개한다.

<body >
<f:view>
....
</f:view>

JSF view태그는 가장 상위 부모 컴포넌트 태그처럼 제공하고 모든 하위 JSF태그는 일련의 작업을 위해서 이 태그안에 포함되어야 한다. JSF view태그는 자식 JSF태그또는 UI컴포넌트 트리의 상태를 저장한다.

다음에 우리는 JSF output_text태그를 사용해서 페이지의 상단에 지역화된메시지를 포함하는 배너를 추가한다. output_text는 리소스번들 jsflogicBundle에서 JSF Login Sample App라고 정의된 지역화된 bannerLabel을 사용한다.

<h:output_text value=
  "#(jsfloginBundle.bannerLabel}"
/>. 

  1. 폼 그리고 유효성 체크

로그인 페이지에 폼을 생성하기 위해서 우리는 JSF <h:form ..>태그를 사용한다. 실행되었을때 JSF form태그는 유저에게 표현하기 위한 input form을 표현하고 자식 컴포넌트는 폼이 서브및 될때 포함되는 input 필드이다.

<h:form id="loginForm">...</h:form>

폼 태그의 body에서 우리는 메시지 labels과 input필드를 포함하는 HTML테이블을 추가할것이다. 필드는 우리가 지역화된 유저아이디와 패스워드를 표현하기 위해 JSF output_text태그를 사용하도록 한다. input_text태그는 input필드이다. 빈 텍스트 input필드를 표현하는 것에 더해서 input_text태그는 value속성을 셋팅함으로써 모델 빈의 필드의 최근값을 표현할수 있다.

<h:input_text id="userid" size="15" required="true" value="#{LoginServer.userid}" />

LoginServer.userid 모델빈 필드가 값을 가진다면 이것은 text필드에 표시된다. 필드값의 어떤 변경은 모델에 적용이 된다. 요구되는 속성이 만약에 어떤 값도 입력되지 않는다면 text필드가 에러를 발생시킬수 있도록 true로 셋팅한다. message태그는 에러를 보여주기 위해 필요하다.

<h:input_text id="userid" size="15" required="true" value="#{LoginServer.userid}" />
<h:messages id="errors1" for="userid"/>

figure3.gif
은 서브및 된 후에 유저아이디가 빈공간으로 남는다면 유효성 체크 에러가 출력되는것을 보여준다. 더욱더 유효성을 지원하기 위해서는 JSF는 <f:validate_length >, <f:validate_doublerange > 그리고 기타 등등처럼 공통된 유효성 작업을 제공하는 validate태그의 collection을 제공한다. 예를 들어 어떤 minimum, maximum길이로 유저아이디의 유효성을 체크하기 위해서는 다음을 사용한다.

<h:input_text id="userid" size="15" required="true" value="#{LoginServer.userid}" >
<f:validate_length minimum="4" maximum="15" /></h:input_text>
  <h:messages id="errors1" for="userid"/>

figure4.gif
는 결과를 보여준다. 만약 당신이 당신의 커스텀 유효성체크를 제공하기를 원한다면 JSF는 당신의 애플리케이션에 당신의 유효성 체크 로직을 쓰거나 통합하도록 허가한다. 예를 들면 다음과 같은 방법으로 클래스를 생성할것이다.

import javax.faces.validator.Validator;
import javax.faces.component.StateHolder;
....
public class MyCustomValidator implements Validator, StateHolder .... }

클래스를 생성하는데 더해서 당신은 JSP페이지로부터 당신의 validator에 접근할수 있도록 커스텀 태그를 만들것이다.

import javax.faces.webapp.ValidatorTag;
import javax.faces.validator.Validator;
...
public class MyCustomValidatorTag extends ValidatorTag .... }

JSP내에 태그를 사용하기 위해 당신은 당신의 태그 조절(handler) 클래스에 속하는 태그 라이브러리 서술 파일을 정의할것이다.

로그인 폼을 끝마치기 위해서는 우리는 하이퍼링크와 메뉴 아이템을 표현할수 있는 UICommand컴포넌트를 사용해서 Submit버튼을 추가한다. 우리의 로그인 버튼을 표현하기 위해 <h:command_button>태그를 사용할것이다.

<h:command_button id="submit" actionRef="LoginServer.loginAction" value="#{jsfloginBundle.loginSubmitLabel}">
</h:command_button>

우리가 command_button을 사용하기 위한 가장 중요한 속성 actionRef이다. 우리의 관리빈(managed bean)인 LoginServer으로 정의된 logicAction코드를 참조하기 위한 속성이다. Struts유저는 Struts Action개념과 비슷하다는것을 알아차리게 될것이다. 다시 생각해보면 loginAction메소드는 실행되고 success나 failure문자열을 반환한다.

public String loginAction() {
 if userid.equals("scott")  && password.equals("tiger") )  
  return "success";
 else 
  return "failure"
}

success와 failure는 무엇을 표현하는것일까.? 그것은 faces-config.xml내에 정의된 탐색경우(navigation cases)이다.

JSF탐색 모델(Navigation Model)#

JSF는 표준적인 전체 애플리케이션 탐색(navigation)을 조정하는 역활을 담당하는 NavigationHandler을 제공한다. 탐색하기 위한 곳을 결정하기 위한 유저아이디와 패스워드를 체크하는 LoginAction내 우리의 Login application의 구조처럼 NavigationHandler는 로직없이 페이지에서 페이지로(pages-to-pages)가는 문자적 탐색과 페이지에서 로직적인 액션 그리고 결과로 이어지는 탐색(page-to-logical-action-to-outcome navigations) 사이에서 작동한다. 우리의 경우에 만약 LoginAction의 결과가 success라면 탐색 조절자(navigation hanler)는 /success.jsp처럼 목표를 결정하기 위한 정의된 <navigation rule>을 사용한다. 이것은 성공한 탐색케이스를 다루기 위한 faces-config.xml에 정의된 탐색룰(navigation rule)이다.

<navigation-rule>
<description>
Login Page Navigation
</description>
<from-view-id>/login.jsp
</from-view-id> 
<navigation-case>
<description>Handle case for successful login</description> 
<from-action-ref>
  LoginServer.loginAction
</from-action-ref> 
<from-outcome>success
</from-outcome> 
<to-view-id>/success.jsp
</to-view-id> 
</navigation-case>
... 
</navigation-rule>

success결과는 한가지의 탐색케이스내에서 다루어진다. 로그인 실패라면 우리는 같은 탐색룰내에 실패한 탐색케이스를 포함한다.

<navigation-case>
<description>Handle case for login failure</description> 
<from-action-ref>
  LoginServer.loginAction
</from-action-ref> 
<from-outcome>failure
</from-outcome> 
<to-view-id>/failure.jsp
</to-view-id> 
</navigation-case>

우리의 애플이케이션 종료를 위해서 우리는 command_button태그를 사용함으로써 로그인 페이지로 돌아가기 위해 success.jsp와 failure.jsp를 위해서 command_button을 추가한다.

<h:command_button id="buttonback" action="return" value="Back to Login Page" />

리소스 번들로부터 label을 사용하기 위해

<h:command_button id="buttonlabelback" action="return" value="#{jsfloginBundle.backLabel}"   />

success.jsp로 부터 관련된 "return" 이라는 from-outcome값이 faces-config.xml내에 필요하다.

<navigation-rule>
<from-view-id>/success.jsp
</from-view-id>
<navigation-case>
<from-outcome>return
</from-outcome>
<to-view-id>/login.jsp
</to-view-id>
</navigation-case>
</navigation-rule>

failure.jsp에서 "return"이라는 from-outcome값 또한 필요하다. figure5.gif
는 정확한 유저아이디와 패스워드를 입력했을때 볼수 있는 success.jsp페이지를 보여준다.

지금 우리는 프라우저 언어를 프랑스어로 바꾸고 틀린 유저아이디와 패스워드를 입력했다면 (see Figure 6- figure6.gif
. 우리는 로그인 실패 페이지로 이동할것이다. 프랑스어로 화면이 표현된다. 물론 문자열은 영어로 그대로 남는다. (역자주: 여기서 해당 이미지를 보면 버튼에 해당되는 부분은 프랑스어, label쪽은 영어로 표기됩니다.)

Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
gif
figure1.gif 16.3 kB 1 06-Apr-2006 09:45 218.239.69.158
gif
figure2.gif 35.4 kB 1 06-Apr-2006 09:45 218.239.69.158
gif
figure3.gif 35.8 kB 1 06-Apr-2006 09:45 218.239.69.158
gif
figure4.gif 38.9 kB 1 06-Apr-2006 09:45 218.239.69.158
gif
figure5.gif 38.9 kB 1 06-Apr-2006 09:45 218.239.69.158
gif
figure6.gif 39.4 kB 1 06-Apr-2006 09:45 218.239.69.158
« This page (revision-2) was last changed on 06-Apr-2006 09:45 by 218.232.42.220