JSF의 universal EL

참조 : http://www.ibm.com/developerworks/edu/j-dw-java-jsf1-i.html

JSP의 EL은 JavaBean 스펙에 따른 게터, 세터만 사용하는데 비해, JSF의 universal EL은 다른 메소드 호출도 됩니다.

<table>
<tr>
<td><h:outputLabel value=”First Number” for=”firstNumber” /></td>
<td><h:inputText id=”firstNumber”
value=“#{calculator.firstNumber}” required=”true” /></td>
<td><h:message for=”firstNumber” /></td>
</tr>
<tr>
<td><h:outputLabel value=”Second Number” for=”secondNumber” />
</td>
<td><h:inputText id=”secondNumber”
value=“#{calculator.secondNumber}” required=”true” /></td>
<td><h:message for=”secondNumber” /></td>
</tr>
</table>

굵게 표시한 부분이 universal EL인데, 뒷단 빈의 속성 값과 연결해 줍니다.

<div>
<h:commandButton action=”#{calculator.add}” value=”Add” />
<h:commandButton action=”#{calculator.multiply}” value=”Multiply” />
<h:commandButton action=”#{calculator.clear}” value=”Clear” immediate=”true”/>
</div>

그리고 위 코드에 있는 EL은 add(), multyply() 등의 메소드를 호출해줍니다. 모든 액션 메소드를 호출하기 전에 폼 검증 단계를 거치는데, immediate=”true”를 사용하면 그 과정을 생략하고 바로 메소드를 호출합니다.

꺄오.. JSF 괜찮은거 같은데~

JSF 기초 2

참조 : JSF for nonbelievers: The JSF application lifecycle

JSF 라이프사이클

사용자 삽입 이미지1. Restore view

요청이 FacesSevlet으로 전달되면, 컨트롤러는 요청을 분석해서 뷰 ID를 뽑아낸다. (JSP 페이지 이름을 바탕으로)

JSF 프레임워크 컨트롤러는 뷰 ID를 사용해서 현재 뷰에서 사용할 컴포넌트들을 가져온다. 뷰가 없으면, 새로 만들어서 사용한다. 만약에 뷰가 이미 있으면, 그것을 사용한다. 뷰에는 모든 GUI 컴포넌트들이 있다.

이 단계에서는 세 가지 경우가 있는데 각각 new view, initial view, postback 이다.

new view: 이 경우에 JSF는 Faces 페이지 뷰를 만들고 이벤트 핸들러와 벨리데이터를 컴포넌트에 연결시킨다. 그렇게 만들어진 뷰는 FacesServlet 객체에 저장된다.

initial view: 이 경우에는 비어있는 뷰를 만든다. 비어있는 뷰는 사용자가 이벤트를 발생시켰을 때 만들어지고, 이 다음 단계는 render response로 넘어간다.

postback: 이미 만들어둔 페이지를 다시 보여줄 때가 postback이다. 이 경우에 JSF는 기존에 존재하던 뷰의 상태 정보를 유지한채 보여준다. 이 다음 단계는 apply request values가 된다.(원래 그 단계인데, 뭘 새삼 언급해주는거지;;)

2. Apply request values

이 단계의 목적은 각각이 컴포넌트들이 현재 상태를 가져오는 것이다. 컴포넌트들은 반드시 그 값들을 사용해서 FacesContext 객체에서 가져오거나 새로 만들어진다. 컴포넌트 값들은 보통 요청 파라메터에서 가져오는데, 쿠키나 헤더에서 가져올 수도 있다.

컴포넌트의 immediate event handling 속성이 true 가 아닐 경우: 값의 타입을 객체의 속성 타입으로 변환하는 작업을 한다. 이 때 만약 캐스팅 시에 에러가 나면, response render 단계에서 validation error랑 같이 보여준다.

컴포넌트의 immediate event handling 속성이 true 일 경우: 타입 변환 + 검증작업까지 한다. 그리고 변환된 값을 컴포넌트에 저장한다. 만약 값 변환이나 값 검증이 실패하면, 에러 메시지를 생성해서 FacesContext에 있는 큐에 넣는다. 그럼 다른 검증 에러들과 함께 response render 단계에서 보여준다.

3. Process Validation

사용자가 입력한 값이 검증 룰에 비교해봤을 때 적당한지를 살펴본다. 적절치 않을 때는 FacesContext에 에러 메시지를 추가하고 컴포넌트를 invalid 상태로 표시한다.

만약 컴포넌트가 invalid로 표시되어 있다면: JSF는 바로 render response 단계로 넘어간다.

만약 invalid 컴포넌트가 아니면: update model values 단계로 간다.

4. Update model values

서버쪽에 있는 모델이 가진 속성 값들을 수정한다. 이 단계는 항상 validation 단계 다음에 오기 때문에 빈 속성값의 유효함을 보장받는다.(물론 폼 필드 수준일 뿐, 비즈니스 룰 수준에서는 유효하지 않을 수도 있다.)

5. Invoke application

JSF 컨트롤러는 폼 처리를 할 애플리케이션을 호출한다. 컴포넌트 값이 그에 따라 바뀔 것이고, 검증될 것이고(여기선 비즈니스 룰 수준의 검증을 말하는 거겠죠), 모델 객체에 반영이 될 것이다. 즉 비즈니스 로직을 수행한다.

이 단계에서, 다음에 보여줄 논리적인 뷰를 명시할 수 있다. 간단하게 폼 처리의 성공 여부를 반환하면 된다. 네비게이션 룰은 faces-config.xml에 정의한다. 네비게이션이 이루어지면, 최종 단계로 이동한다.

6.  Render response

모든 컴포넌트의 현재 상태를 화면에 보여준다. 그림2는 객체 상태 다이어그램으로 JSF 라이프사이클 6 단계를 보여주고 있다.

사용자 삽입 이미지

JSF 기초 1

참조 : JSF for nonbelievers: Clearing the FUD about JSF

기본 아키텍처

사용자 삽입 이미지
JSF 예제

1. web.xml 과 faces-config.xml 설정하기

1-1. web.xml

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

<!– Faces Servlet Mapping –>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/calc/*</url-pattern>
</servlet-mapping>

이건 뭐 별 설명이 필요 없을 것 같네요. 스프링의 DispatcherServlet하고 비슷한 일을 할 듯 합니다. 중요 클래스니까 언제 한 번 열어봐야겠네요.

1-2. faces-config.xml

<faces-config>
    …
  <managed-bean>
    <description>
      The “backing file” bean that backs up the calculator webapp
    </description>
    <managed-bean-name>CalcBean</managed-bean-name>
    <managed-bean-class>com.arcmind.jsfquickstart.controller.CalculatorController</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

<navigation-rule>
  <from-view-id>/calculator.jsp</from-view-id>
  <navigation-case>
    <from-outcome>success</from-outcome>
    <to-view-id>/results.jsp</to-view-id>
  </navigation-case>
</navigation-rule>

</faces-config>

흠.. JSF에서 사용할 컨트롤러 빈의 이름과 스코프를 등록하고, 네비게이션을 등록해두네요. 네비게이션을 등록해둔 걸 보면, 서브밋 할 때 action=”result.jsp” 같은 코드를 사용하지 않아도 알아서 가주겠죠?

2. 모델 객체 만들기

package com.arcmind.jsfquickstart.model;

/**
 * Calculator
 *
 * @author Rick Hightower
 * @version 0.1
 */
public class Calculator {
    //~ Methods —————————————————————-

    /**
     * add numbers.
     *
     * @param a first number
     * @param b second number
     *
     * @return result
     */
    public int add(int a, int b) {
        return a + b;
    }

    /**
     * multiply numbers.
     *
     * @param a first number
     * @param b second number
     *
     * @return result
     */
    public int multiply(int a, int b) {
        return a * b;
    }
   
}

3. 컨트롤러 만들기(완전 POJO)

package com.arcmind.jsfquickstart.controller;

import com.arcmind.jsfquickstart.model.Calculator;

/**
 * Calculator Controller
 *
 * @author $author$
 * @version $Revision$
 */
public class CalculatorController {
    //~ Instance fields ——————————————————–

    /**
     * Represent the model object.
     */
    private Calculator calculator = new Calculator();

    /** First number used in operation. */
    private int firstNumber = 0;

    /** Result of operation on first number and second number. */
    private int result = 0;

    /** Second number used in operation. */
    private int secondNumber = 0;

    //~ Constructors ———————————————————–

    /**
     * Creates a new CalculatorController object.
     */
    public CalculatorController() {
        super();
    }

    //~ Methods —————————————————————-

    /**
     * Calculator, this class represent the model.
     *
     * @param aCalculator The calculator to set.
     */
    public void setCalculator(Calculator aCalculator) {
        this.calculator = aCalculator;
    }

    /**
     * First Number property
     *
     * @param aFirstNumber first number
     */
    public void setFirstNumber(int aFirstNumber) {
        this.firstNumber = aFirstNumber;
    }

    /**
     * First number property
     *
     * @return First number.
     */
    public int getFirstNumber() {
        return firstNumber;
    }

    /**
     * Result of the operation on the first two numbers.
     *
     * @return Second Number.
     */
    public int getResult() {
        return result;
    }

    /**
     * Second number property
     *
     * @param aSecondNumber Second number.
     */
    public void setSecondNumber(int aSecondNumber) {
        this.secondNumber = aSecondNumber;
    }

    /**
     * Get second number.
     *
     * @return Second number.
     */
    public int getSecondNumber() {
        return secondNumber;
    }

    /**
     * Adds the first number and second number together.
     *
     * @return next logical outcome.
     */
    public String add() {
       
        result = calculator.add(firstNumber, secondNumber);

        return “success”;
    }

    /**
     * Multiplies the first number and second number together.
     *
     * @return next logical outcome.
     */
    public String multiply() {

        result = calculator.multiply(firstNumber, secondNumber);
       
        return “success”;
    }
}

흠.. 이 코드를 보고서 좀 놀랐습니다. 스프링 2.5로 컨틀롤러를 만들어도, 가능하긴 한데, 수많은 애노테이션들이 붙고, 그 애노테이션들을 정말 잘 알고 있어야 했는데.. 결국 그 애노테이션들 없이는 스프링의 컨트롤러는 의미도 없고 재사용할 수도 없기 떄문에 엄밀히 따지면 POJO라고 하긴 좀 뭐했습니다. 그런데 JSF의 저 컨트롤러는 뭐.. 틈잡을것이 하나도 없습니다.

4. 뷰 만들기

4-1. index.jsp

<jsp:forward page=”/calc/calculator.jsp” />

흠.. 얘는 네비게이션에 등록하지도 않았고, JSF Servlet이 담당할 URL로 들어오지 않을테고.. 저기서 JSF  Servlet이 담당할 URL로 포워딩시키는 군요.

4-2. calculator.jsp

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

<f:view>
  <h:form id=”calcForm”>
     <h:panelGrid columns=”3″>
    <h:outputLabel value=”First Number” for=”firstNumber” />
    <h:inputText id=”firstNumber” value=”#{CalcBean.firstNumber}” required=”true” />
        <h:message for=”firstNumber” />   

    <h:outputLabel value=”Second Number” for=”secondNumber” />
    <h:inputText id=”secondNumber” value=”#{CalcBean.secondNumber}” required=”true” />
        <h:message for=”secondNumber” />
    </h:panelGrid>
   
    <h:panelGroup>
    <h:commandButton id=”submitAdd” action=”#{CalcBean.add}”  value=”Add” />
    <h:commandButton id=”submitMultiply” action=”#{CalcBean.multiply}” value=”Multiply” />
</h:panelGroup>
  </h:form>
</f:view>

흠~ 이제 좀 재밌는 코드가 나왔습니다.

맨위에 첫 줄은, 폼을 비록한 HTML 관련 태그들이 담겨있고, 두 번째 줄은 JSF가 사용할 로직, 벨리데이션, 컨트롤러등을 위한 태그들이 담겨있다고 합니다.

<f:view> 안에 들어가야 JSF 컴포넌트 트리를 만든다고 합니다. JSF 를 사용할 땐 저 안에 모든 JSF 컴포넌트를 넣어야 겠군요.

<h:form> 은, html 폼을 나타내는 JSF 컴포넌트인가 봅니다. 속성중에  column=3은 한 줄에 세칸이라는 뜻이고, 따라서 저 안에 있는 컴포넌트들은 세줄씩 마침 코드에서도 한 줄 띄어쓰기로 구분해주고 있네요.

<h:message>는 에러 메시지 보여줄 때 사용할 것 같은데, 뭘 참조하라는게 없군요. 자동으로 메시지 생성해서 넣어줄 것 같은데… 내가 원하는 메시지를 보여주고 싶을 땐 어떻게 해야되지??

의문 1. 내가 원하는 메시지를 보여주고 싶을 땐 <h:message>를 어떻게 설정해야 하나?

JSP의 EL 처럼 JSF에도 EL 이 있는데, 그건 바로 #{}. CalcBean은 아까 faces-config.xml이였나.. 거기에 등록했던 컨트롤러이고, value 속성은 해당 컨트롤러의 필드값을 참조하고, action은 메소드를 호출하고 그 결과를 가져오나 보군요.

의문 2. 인자가 있는 메소드도 지원할까? 지원한다면, 어떻게 써야 하나?

예상했던 것처럼 역시나 저 폼을 서브밋하면 어디로 갈지를 폼에다가 적지 않았습니다. 아까 저 페이지? URL?에 대한 네비게이션을 정의해두었죠.

4-3. result.jsp

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

<f:view>
  First Number: <h:outputText id=”firstNumber” value=”#{CalcBean.firstNumber}”/>
  <br />
  Second Number: <h:outputText id=”secondNumber” value=”#{CalcBean.secondNumber}”/>
  <br />
  Result: <h:outputText id=”result” value=”#{CalcBean.result}”/>
  <br />
</f:view>

흠.. 이번에도 역시 <f:view>를 써서 JSF 컴포넌트 트리로 감싸고, <h:outputText> 라는 컴포넌트를 쓰고 있군요. value를 사용해서 필드값들만 참조합니다. 간단하군요.

돌리는 건 내일해야 겠습니다. 11시 반도 안 됐는데 졸리네요;