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시 반도 안 됐는데 졸리네요;

불신자를 위한 JSF 시리즈

JSF for nonbelievers: Clearing the FUD about JSF
JSF for nonbelievers: The JSF application lifecycle
JSF for nonbelievers: JSF conversion and validation
JSF for nonbelievers: JSF component development

외국에선 상당히 많은 개발자들이 사용하고 있다는 JSF.. 저도 한 번 써보고 싶어서 공부를 시작합니다. 위에 두 개의 아티클은 한국 IBM DeveloperWorks에 번역된 기사를 찾긴 했는데, 번역이.. 좀;; 그래서 걍 원문을 읽기로 했습니다.

나머지 두 개는 제가 번역해야겠습니다. ㅋㅋ

15.3. JavaServer Faces

15.3.1. DelegatingVariableResolver

JSF 웹 계층과 스프링의 미들티어와 연동하는 가장 쉬운 방법은 DelegatingVariableResolver 클래스를 사용하는 것입니다.

faces-context.xml 파일을 다음과 같이 수정합니다.

<faces-config>
  <application>
    <variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
      <locale-config>
        <default-locale>en</default-locale>
        <supported-locale>en</supported-locale>
        <supported-locale>es</supported-locale>
      </locale-config>
      <message-bundle>messages</message-bundle>
    </application>
</faces-config>

DelegatingVariableResolver는 먼저 값을 가져오는 것을 JSF 구현체의 기본 리졸버에게 위임할 것이고, 그 다음에 스프링의 비즈니스 문맥이 담긴 WebApplicationContext에 위임합니다. 이 방법을 사용해서 간단하게 JSF에 의해 관리되는 bean들에 의존성을 주입할 수 있습니다.

JSF가 관리하는 bean들은 faces-config.xml에 정의 되어 있으며 아래의 예제에서 #{userManager}는 스프링의 비즈니스 문맥으로부터 가져오는 bean을 나타냅니다.

<managed-bean>
  <managed-bean-name>userList</managed-bean-name>
    <managed-bean-class>com.whatever.jsf.UserList</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>userManager</property-name>
    <value>#{userManager}</value>
  </managed-property>
</managed-bean>

15.3.2. FacesContextUtils

faces-config.xml에 있는 bean들(JSF 빈)의 한 프로퍼티를 주입할 때는 위와 같은 방법이 용이합니다. 그러나 명시적으로 스프링 bean이 왕창 필요할 때는  FacesContextUtils를 사요할 수 있습니다.

ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());

WebApplicationContextUtils 와 사용법은 유사하며 차이점은 인자로 facesContext를 넘겨준다는 것입니다.