JAVA SERVER FACES MANUAL 김승규

Similar documents
I T C o t e n s P r o v i d e r h t t p : / / w w w. h a n b i t b o o k. c o. k r

PowerPoint Template

다른 JSP 페이지호출 forward() 메서드 - 하나의 JSP 페이지실행이끝나고다른 JSP 페이지를호출할때사용한다. 예 ) <% RequestDispatcher dispatcher = request.getrequestdispatcher(" 실행할페이지.jsp");

Slide 1

Javascript

Microsoft Word - ntasFrameBuilderInstallGuide2.5.doc

제이쿼리 (JQuery) 정의 자바스크립트함수를쉽게사용하기위해만든자바스크립트라이브러리. 웹페이지를즉석에서변경하는기능에특화된자바스크립트라이브러리. 사용법 $( 제이쿼리객체 ) 혹은 $( 엘리먼트 ) 참고 ) $() 이기호를제이쿼리래퍼라고한다. 즉, 제이쿼리를호출하는기호

PowerPoint Presentation

var answer = confirm(" 확인이나취소를누르세요."); // 확인창은사용자의의사를묻는데사용합니다. if(answer == true){ document.write(" 확인을눌렀습니다."); else { document.write(" 취소를눌렀습니다.");

혼자서일을다하는 JSP. 이젠일을 Servlet 과나눠서한다. JSP와서블릿의표현적인차이 - JSP는 <html> 내에서자바를사용할수있는수단을제공한다. - 서블릿은자바내에서 <html> 을작성할수있는수단을제공한다. - JSP나서블릿으로만웹페이지를작성하면자바와다양한코드가

gnu-lee-oop-kor-lec06-3-chap7

제8장 자바 GUI 프로그래밍 II

Intro to Servlet, EJB, JSP, WS

PowerPoint Presentation

슬라이드 1

1. 자바프로그램기초 및개발환경 2 장 & 3 장. 자바개발도구 충남대학교 컴퓨터공학과

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

JAVA Bean & Session - Cookie

슬라이드 1

PowerPoint Presentation

JAVA PROGRAMMING 실습 08.다형성

MVVM 패턴의 이해

블로그_별책부록

Research & Technique Apache Tomcat RCE 취약점 (CVE ) 취약점개요 지난 4월 15일전세계적으로가장많이사용되는웹애플리케이션서버인 Apache Tomcat에서 RCE 취약점이공개되었다. CVE 취약점은 W

<4D F736F F F696E74202D20C1A63038C0E520C5ACB7A1BDBABFCD20B0B4C3BC4928B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

쉽게 풀어쓴 C 프로그래밍

KYO_SCCD.PDF

<4D F736F F F696E74202D20C1A63236C0E520BED6C7C3B8B428B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

PowerPoint Presentation

PowerPoint 프레젠테이션

<4D F736F F F696E74202D203130C0E52EBFA1B7AF20C3B3B8AE205BC8A3C8AF20B8F0B5E55D>

PowerPoint Presentation

Microsoft PowerPoint - chap01-C언어개요.pptx

Eclipse 와 Firefox 를이용한 Javascript 개발 발표자 : 문경대 11 년 10 월 26 일수요일

PowerPoint Presentation

JUNIT 실습및발표

Microsoft PowerPoint Android-SDK설치.HelloAndroid(1.0h).pptx

어댑터뷰

Interstage5 SOAP서비스 설정 가이드

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

Spring Boot/JDBC JdbcTemplate/CRUD 예제

iii. Design Tab 을 Click 하여 WindowBuilder 가자동으로생성한 GUI 프로그래밍환경을확인한다.

Apache Ivy

작성자 : 김성박\(삼성 SDS 멀티캠퍼스 전임강사\)

chapter6.doc

[ 그림 8-1] XML 을이용한옵션메뉴설정방법 <menu> <item 항목ID" android:title=" 항목제목 "/> </menu> public boolean oncreateoptionsmenu(menu menu) { getme

슬라이드 1

chapter1,2.doc

04장

14-Servlet

중간고사

Microsoft PowerPoint 웹 연동 기술.pptx

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션

Spring Boot

API STORE 키발급및 API 사용가이드 Document Information 문서명 : API STORE 언어별 Client 사용가이드작성자 : 작성일 : 업무영역 : 버전 : 1 st Draft. 서브시스템 : 문서번호 : 단계 : Docum

Microsoft PowerPoint - CSharp-10-예외처리

예제 2) Test.java class A intvar= 10; void method() class B extends A intvar= 20; 1"); void method() 2"); void method1() public class Test 3"); args) A

Web Service Computing

Microsoft PowerPoint - java1-lab5-ImageProcessorTestOOP.pptx

JavaGeneralProgramming.PDF

2009년 상반기 사업계획

Microsoft PowerPoint - Java7.pptx

제11장 프로세스와 쓰레드

Javascript

Network Programming

2장 변수와 프로시저 작성하기

Microsoft PowerPoint SDK설치.HelloAndroid(1.5h).pptx

윈도우시스템프로그래밍

Data Provisioning Services for mobile clients

쉽게


C# Programming Guide - Types

오버라이딩 (Overriding)

금오공대 컴퓨터공학전공 강의자료

슬라이드 1

Microsoft PowerPoint - 06-Chapter09-Event.ppt

INDEX 들어가기 고민하기 HTML(TABLE/FORM) CSS JS

PowerPoint 프레젠테이션

학습목표 함수프로시저, 서브프로시저의의미를안다. 매개변수전달방식을학습한다. 함수를이용한프로그래밍한다. 2

<4D F736F F F696E74202D20C1A63139C0E520B9E8C4A120B0FCB8AEC0DA28B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

Microsoft Word - src.doc

Mobile Service > IAP > Android SDK [ ] IAP SDK TOAST SDK. IAP SDK. Android Studio IDE Android SDK Version (API Level 10). Name Reference V

PowerPoint 프레젠테이션

- JPA를사용하는경우의스프링설정파일에다음을기술한다. <bean id="entitymanagerfactory" class="org.springframework.orm.jpa.localentitymanagerfactorybean" p:persistenceunitname=

Visual Basic 반복문

쉽게 풀어쓴 C 프로그래밍

No Slide Title

Microsoft PowerPoint - 2강

02 C h a p t e r Java

TP_jsp7.PDF

JSP 의내장객체 response 객체 - response 객체는 JSP 페이지의실행결과를웹프라우저로돌려줄때사용되는객체이다. - 이객체는주로켄텐츠타입이나문자셋등의데이터의부가정보 ( 헤더정보 ) 나쿠키 ( 다음에설명 ) 등을지정할수있다. - 이객체를사용해서출력의방향을다른

Windows 8에서 BioStar 1 설치하기

DataBinding

Week13

UI TASK & KEY EVENT

SK Telecom Platform NATE

Microsoft PowerPoint - 11주차_Android_GoogleMap.ppt [호환 모드]

PowerPoint Template

Spring Data JPA Many To Many 양방향 관계 예제

Transcription:

JAVA SERVER FACES MANUAL 2004. 6 김승규 http://www.java-inside.co.kr e-mail : zzzccc90@yahoo.co.kr

JAVA SERVER FACES MANUAL 1 1. JAVA SERVER FACES 개요 7 2.1 Tomcat 의환경설정 9 2.2 톰캣에서의한글 10 2.3 JSF 를위한환경구성 11 2.4 Ant 12 2.5 JSF 에서의한글 15 3. JSF 로구현한간단한예제 16 3.1 간단한예제 16 3.2 rendered 속성 21 3.3 컴포넌트의바인딩 23 3.4 이벤트 25 4장. JSF 의구성요소 29 4.1 컴포넌트 30 4.3 managed bean 35 4.4 렌더러 42 4.5 이벤트 43 4.6 validator & converter 45 4.6.1 Validator 45 4.6.2 Converter 46 2

4.7 navigation 47 5. JSF LIFE CYCLE 51 5.1 JSF Life Cycle 51 5.2 immediate 속성 53 6. JSF 컴포넌트 63 6.1 Html 컴포넌트 63 6.2 HtmlCommandButton 63 6.3 HtmlCommandLink 63 6.4 HtmlForm 65 6.5 HtmlGraphicImage 65 6.6 HtmlInputHidden 65 6.7 HtmlInputSecret 66 6.8 HtmlInputTextarea 66 6.9 HtmlInputText 67 6.10 HtmlMessage 67 6.11 HtmlMessages 70 6.12 HtmlOutputFormat 71 6.13 HtmlOutputLabel 72 6.14 HtmlOutputLink 73 6.15 HtmlOutputText 74 6.16 HtmlPanelGrid 74 6.17 HtmlPanelGroup 77 3

6.18 HtmlDataTable 컴포넌트 79 7. CORE 컴포넌트 90 7.1 <f:actionlistener> 90 7.2 <f:attribute> 91 7.3 <f:convertdatetime> 94 7.4 <f:convertnumber> 98 7.5 <f:converter> 100 7.6 <f:facet> 101 7.7 <f:loadbundle> 101 7.8 <f:param> 101 7.9 <f:selectitem>, <f:selectitems> 102 7.10 <f:subview> 112 7.11 <f:validatedoublerange>, <f:validatelength>, <f:validatelongrange> 113 7.12 <f:validator> 114 7.13 <f:valuechangelistener> 114 7.14 <f:view> 115 8. VALIDATOR, CONVERTER 를만들어보자. 116 8.1 Validator 만들기 116 8.2 Converter 124 9. CUSTOM COMPONENT 구현 -1 130 9.1 태그클래스작성 131 4

9.2 TLD 파일의작성 133 9.3 컴포넌트클래스 134 9.4 렌더러 134 9.5 bean 136 9.6 faces-config.xml config.xml 139 9.7 테스트 140 10. CUSTOM COMPONENT 2 142 10.1 ImageListComponent 의구현 143 10.1.1 태그클래스 143 10.1.2 TLD 파일의작성 147 10.1.3 컴포넌트클래스 148 10.1.4 bean클래스작성 152 10.2 ImageDisplayComponent 154 10.2.1 태그클래스 154 10.2.2 TLD 파일의작성 155 10.2.3 컴포넌트클래스 156 10.3 faces-config.xml config.xml 159 10.4 테스트 160 5

* 상업적인용도로사용하실수없습니다. 6

1. Java Server Faces개요 JSF를굳이정의하자면 컴포넌트를이용한웹어플리케이션의개발 이라고할수있겠다. 웹어플리케이션의개발에필요한여러가지 html요소들을컴포넌트화하고바인딩을통해서버사이드에서만들어진컴포넌트들을웹브라우저에표시함으로써, 기존의웹페이지에서만가능했던클라이언트환경의제어를서버측에서가능하게한다. 물론기존에사용하던자바스크립트도사용이가능하다. 또, JSF는렌더러라는개념을도입하여변화하는클라이언트의환경에대응할수있도록하고있다. 무슨뜻인가하면, 클라이언트의환경에웹브라우저가아니라 XML, WML, SVG 혹은핸드폰을위해사용할수도있다는뜻이되겠다. 만들어진컴포넌트의렌더러만변경함으로써, 컴포넌트를클라이언트의환경에맞는작업을수행하도록하는것이가능하다. JSF 는다음의기술에기반을두고있다. 커스텀태그 JSP2.0 의 EL(Expression Language) jakarta commons project JSF 는컴포넌트기반개발을위해다음의기능들을지원한다. 이벤트처리 Validator & Converter Localization Navigation JSF는클라이언트에의해 submit이발생하는경우, 입력되는값이변경되었을경우서버사이드에서지정된이벤트를처리할수있다. 또, 값의유효함을검사하기위한 Validator, 값을적절한타입으로변경하기위한 Converter, 페이지의이동을관리하는 navigation기능들을지원한다. JSF 는 jakarta struts 와유사한면이많다. 아마도 jakarta struts 를개발한 Craig McClanahan 이 JSF 의개발에참여하고있기때문일것이다. 컴포넌트를이용한웹어플리케이션의개발이가능해짐으로서이미 JSF를이용하여비주얼한환경에서웹어플리케이션을개발할수있는툴이나와있다. 또, 많은개발자사이트에서 JSF를이용하여유용한컴포넌트들이개발되고있다. 마지막장에서 Open Source로개발되고있는 MyFaces를살펴볼까한다. 7

2. JSF 를공부하기위한환경 이책의예제들을테스트하기위해서는다음의환경이필요하다. JDK1.4 이상 Jakarta Tomcat 5.0 - Servlet2.3/JSP1.2 이상의환경이요구된다. JSF-1.0 SCSL Jakarta-ant 1.5 MySql 4.0 JSF는 jwsdp(java Web Service Developer Pack) 에기본적으로포함되어있다. 그러나, 이책을쓰는현재 Sun에서제공하는 jwsdp에는 JSF1.0EA4가들어있다. JSF1.0EA4는이책에서사용하는예제가작동하지않으므로주의하기바란다. EA4 까지의 JSF와이후의 JSF는태그의이름이다르고, 몇몇클래스도변경이되었기때문이다. JSF의최신버전은 http://wwws.sun.com/software/communitysource/jsf/index.html에서받을수있다. 다운받은소스를직접 build하여사용하거나, 혹은이책의예제파일을다운받아 WEB-INF/lib에들어있는 jar파일을이용하기바란다. JSF 의최신버전을다운받아서압축을풀면다음과같은 4 개의폴더가생긴다. jsf-api jsf-demo jsf-ri jsf-tools 각각의폴더안에있는 build.xml을수행하면각폴더에 build폴더가생기고, 그안에 API문서와 jar파일이생길것이다. 각폴더의 build.xml파일은 jwsdp의 jar파일들을참조하도록설정되어있어서 build를할때어려움이있다. 제일편한방법은 jwsdp의최신버전을다운받고, 각폴더의 build.properties 파일의 tomcat.home 값을각자의환경에맞는 jwsdp경로명을지정해주면 build가보다쉽게된다. tomcat.home= jwsdp 폴더경로 다음의 jsr 파일들이이책의예제를테스트하는데필요하다. 8

commons-beanutils.jar commons-collections.jar commons-digester.jar commons-logging.jar jsf-api.jar jsf-impl.jar jstl.jar standard.jar mysql-connector-java-3.0.11-stable-bin.jar jakarta-commons 프로젝트의라이브러리와 jstl(java Standard Tag Library) 을기본으로 사용하고, jsf 에서구현한 jsf-api.jar 와 jsf-impl.jar 가들어있다. 또, 데이터베이스로는 mysql 을사용하였으므로, mysql 의 JDBC 를구하도록한다. 2.1 Tomcat 의환경설정 테스트를위한웹서버로 Jakarta-tomcat-5.0.19 를이용한다. Ant를이용한빌드를용이하게하기위해톰캣의 manager를이용한다. 톰캣의 manager는웹어플리케이션의배치를위해톰캣을종료할필요가없이동적인업데이트를가능하게한다. 아직불완전하지만, 테스트를위해서는쓸만한환경을제공한다. 우선, TOMCAT_HOME/conf/tomcat-users.xml파일을열어서아래의내용을추가한다. <?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager"/> <user username="zzzccc" password="zzzccc" roles="manager"/> </tomcat-users> 위와같이 manager를위한 role을추가하면된다. reload를계속하다보면가끔 OutOfMemoryError를일으키는경우가발생한다. 그때는톰캣을 shutdown하고다시구동하도록한다. 테스트하는데많은지장을주는부분은아니므로톰캣만재시작하면문제가없다. 9

2.2 톰캣에서의한글 언제나새로이테스트환경을구축할때직면하는문제가한글을표현하는부분일것이다. 다행이최근에는톰캣에서제공하는 filter기능을이용하여한글처리문제를해결할수있다. TOMCAT_HOME/conf/web.xml의파일을아래와같이수정한다. <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <filter> <filter-name>set Character Encoding</filter-name> <filter-class>jsf.proj.filter.setcharacterencodingfilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>euc_kr</param-value> </init-param> </filter> <filter-mapping> <filter-name>set Character Encoding</filter-name> <url-pattern>/faces/*</url-pattern> </filter-mapping> </web-app> SetCharacterEncodingFilter 클래스는톰캣의다음경로에위치한다. TOMCAT_HOME/webapps/servlet-examples/WEB-INF/classes/filters 여기에소스파일과컴파일된클래스가같이있으므로골라서사용하면되겠다. 이책에 서는패키지명만바꾸에사용하였다. 10

2.3 JSF 를위한환경구성 JSF 를이용하기위해필요한설정을 web.xml 파일에추가해보자. <servlet> <servlet-name>faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.facesservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping> 웹어플리케이션을위한 FacesServlet 클래스를설정한다. 그리고, /faces 가포함된 url 을처리할때 FacesServlet 이구동될수있도록한다. <context-param> <param-name>javax.faces.state_saving_method</param-name> <param-value>client</param-value> </context-param> context를위해파라미터로주어지는 STATE_SAVING_METHOD는웹페이지를구성하는 JSF 컴포넌트클래스들의직렬화된정보를어디에저장할지는결정하는값이다. 위의예에서처럼클라이언트를선택하면, 웹페이지에 hidden 타입으로직렬화된값들이저장된다. 서버를선택하면, 서버측에서정보를관리한다. 디폴트값은 server이다. <context-param> <param-name>com.sun.faces.validatexml</param-name> <param-value>true</param-value> </context-param> faces-config.xml 의 xml 형식이유효한가를검사하는옵션이다. <context-param> <param-name>javax.faces.application.config_files</param-name> 11

<param-value>/web-inf/faces-config.xml</param-value> </context-param> 웹어플리케이션을위한환경설정파일인 faces-config.xml의위치를지정한다. 생략하면, /WEB-INF/faces-config.xml을찾게될것이다. 컴포넌트를만들어배포하는경우, jar파일에생기는 /META-INF에 faces-config.xml 파일을두면, 컴포넌트를위한설정파일로사용될수있다. 2.4 Ant ant 를이용해서웹어플리케이션의자바코드컴파일및 WAR 파일의배치를편리하게 수행할수있다. 이책의예제를테스트해보기위해다음과같은 build.xml 을이용한 다. <?xml version="1.0" encoding="euc-kr"?> <project name="jsf_test" basedir="./" default="reload"> <property name="project-name" value="jsf-proj"/> <property name="src" value="./"/> <property name="jsp" value="./jsp"/> <property name="lib" value="./lib"/> <property name="dest" value="c:/temp/dest"/> <property name="tomcat_home" value="c:/jakarta-tomcat-5.0.19"/> <property name="class-dest" value="${dest/web-inf/classes"/> <property name="jsp-dest" value="${dest/jsp"/> <property name="img-dest" value="${dest/jsp/images"/> <property name="lib-dest" value="${dest/web-inf/lib"/> <property name="config-dest" value="${dest/web-inf"/> <property name="deploy-war" value="${tomcat_home/webapps"/> <property name="lib-common" value="${tomcat_home/common/lib"/> <property name="lib-server" value="${tomcat_home/server/lib"/> <property name="manager-url" value="http://localhost:8080/manager/"/> <property name="manager-id" value="zzzccc"/> <property name="manager-pass" value="zzzccc"/> 12

<property name="webapp-path" value="/${project-name"/> <path id="classpath"> <fileset dir="${lib-common"> <include name="**/*.jar"/> </fileset> <fileset dir="${lib-server"> <include name="**/*.jar"/> </fileset> <fileset dir="${lib"> <include name="**/*.jar"/> </fileset> </path> <!-- DEFINE RELOAD TASK ON TOMCAT --> <!-- ============================ --> <taskdef name="reload" classname="org.apache.catalina.ant.reloadtask" classpathref="classpath"/> <!-- COMPILE PROJECT --> <!-- =============== --> <target name="compile"> <mkdir dir="${class-dest"/> <mkdir dir="${lib-dest"/> <mkdir dir="${jsp-dest"/> <mkdir dir="${img-dest"/> <javac source="1.4" destdir="${class-dest"> <classpath refid="classpath"/> <src path="${src"/> </javac> <native2ascii encoding="euc-kr" src="${src" dest="${class-dest" includes="**/*.properties" ext="_ko.properties"/> </target> 13

<!-- COPY APP --> <target name="copy-src" depends="compile"> <copy todir="${jsp-dest"> <fileset dir="${jsp"> <include name="**/*.*"/> </fileset> </copy> <copy todir="${lib-dest"> <fileset dir="${lib"> <include name="**/*.*"/> </fileset> </copy> <copy todir="${config-dest"> <fileset dir="./"> <include name="*.xml"/> <include name="*.tld"/> </fileset> </copy> </target> <target name="makewar" depends="copy-src"> <jar jarfile="${project-name.war" basedir="${dest"> </jar> </target> <!-- COPY --> <!-- ========= --> <target name="copy-war" depends="makewar"> <copy todir="${deploy-war"> <fileset dir="${src"> <include name="*.war"/> </fileset> </copy> 14

</target> <delete dir="${dest"/> <!-- RELOAD PROJECT --> <!-- ============== --> <target name="reload" depends="copy-war"> <reload url="${manager-url" username="${manager-id" password="${manager-pass" path="${webapp-path"/> </target> <target name="clean"> </target> <delete dir="${tomcat_home/webapps/${project-name"/> </project> 2.5 JSF 에서의한글 톰캣의 filter기능을이용하여, 한글이처리되도록하였지만, JSF의 properties 파일을읽어오는경우여전히한글이깨지는문제가발생한다. 이런경우한글을유니코드로인코딩하여이용한다. ant의다음과같은작업으로, 확장자가 properties로끝나는모든파일은유니코드로인코딩되도록하였다. <native2ascii encoding="euc-kr" src="${src" dest="${class-dest" includes="**/*.properties" ext="_ko.properties"/> 파일명의끝에 _ko를붙이도록한이유는 JSF가클라이언트의언어환경에따라자동적으로파일을참고하기때문이다. 우리는앞으로의예제를통해 Messages.properties 라는파일과 Resources.properties 라는 property 파일을이용하게될것이다. 사용할브라우저의환경에한글이면, JSF는 Messages_ko.propertie 와 Resources_ko.properties 를참조할것이다. 15

3. JSF 로구현한간단한예제 간단한예제를통해서개략적이나마 JSF 가어떻게움직이는지를알아보도록하자. 여기서테스트해볼프로그램은간단하다. 단순히입력을받은문자열을다음페이지에서출 력하도록한다. 3.1 간단한예제 프로그램을테스트해보기위해아래의과정을따라해보자. 데이터를저장할빈의작성 jsf 페이지작성 빈의설정및페이지이동을위한 faces-config.xml 의작성 데이터를저장할 bean 의작성 아래의 bean 은문자열변수와변수의 setter/getter 메소드를가지는클래스이다. package jsf.proj.example; public class Example1{ private String msg="hello World!!"; public void setvalue(string msg){ this.msg=msg; public String getvalue(){ return msg; 위에서작성한 Example1 빈을이용하기위해간단한 jsf 페이지를아래와같이작성한다. <!-- 16

--> /jsp/example/example1.jsp <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <h:inputtext id="in1" value="#{example1.value"/><br> <h:commandbutton value="submit버튼 " action="success"/> </h:form> </f:view> </body> </html> jsf 의태그라이브러리를이용하기위해서는위와같이두개의태그라이브러리를이용한다. <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 첫번째태그라이브러리는 html컴포넌트를사용하기위한태그를정의한태그라이브러리이다. 두번째는 jsf의 core컴포넌트를나타내는태그라이브러리이다. html컴포넌트는웹브라우저에나타날수있는컴포넌트들, 예를들면버튼이나입력상자, 라디오버튼, 체크박스등등의태그를사용하기위한것이고, core컴포넌트는주로 html컴포넌트내부에쓰이거나, 브라우저에나타나지않는처리작업들, 예를들면입력된값을검사하는 validator나값을변환하는 converter등을사용하기위해 core컴포넌트가필요하다. 나중에자세히설명이되므로지금은여기의예제를살펴보도록하자. view태그내에사용될컴포넌트들이들어가야한다. 요청된페이지의컴포넌트들은 jsf에의해내부적으로컴포넌트트리로구성된다. 위에서예를든 jsf페이지의 form태그는 html form엘리먼트로, inputtext태그는 html input엘리먼트로 commandbutton은 html button엘리먼트로각각해석될것이다. 17

jsf는값을표현하는방법으로 EL(Expression Language) 를이용한다. #{ 로둘러싸인표현식은단순한문자열로해석되지않고, jsf에의해이문자열이의미하는값을매핑한다. 위의 inputtext태그의 value값인 #{Example1.value 는 Example1이라는클래스의 getvalue() 메소드를의미하게되는것이다. 여기서 Example1이라는클래스이름은 faces-config.xml 파일에등록된 jsf.proj.example.example1클래스의 id이다. commandbutton 의 action 속성은버튼이클릭되었을경우어디로이동할지를나타내는역 할을한다. 이버튼을클릭했을경우입력한결과를표시하는다음의페이지로이동한다. <!-- --> /jsp/example/example1_result.jsp <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="resultform"> <h:outputtext value="#{example1.value"/> </h:form> </f:view> </body> </html> 이번에는위에서작업한예제의 bean 과페이지이동을위해 faces-config.xml 파일을설정 해보자. <?xml version="1.0" encoding="euc-kr"?> <!DOCTYPE faces-config PUBLIC 18

"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <faces-config> <managed-bean> <managed-bean-name>example1</managed-bean-name> <managed-bean-class>jsf.proj.example.example1</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> <navigation-rule> <from-view-id>/jsp/example/example1.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/jsp/example/example1_result.jsp</to-view-id> </navigation-case> </navigation-rule> </faces-config> 위에서만든 bean은 faces-config.xml에서 <managed-bean> </managed-bean> 내부에위와같이사용하게될이름과패키지명을포함한클래스전체이름, 그리고이 bean이사용될 scope를기술하면되겠다. 페이지의이동은 <navigation-rule> </navigation-rule> 내부에설정된다. 위의예를보면, /jsp/example/example1.jsp 페이지로부터 action 문자열이 success이면 /jsp/example/example1_result.jsp 페이지로 forward된다. 19

example1.jsp 파일의결과가위의화면에나타나있다. 브라우저의소스보기를이용해서 jsf 가 html 로해석된결과를보도록하자. <!-- --> <html> <body> example1.jsp <form id="myform" method="post" action="/jsf-proj/faces/jsp/example/example1.jsp" enctype="application/x-www-form-urlencoded"> <input id="myform:in1" type="text" name="myform:in1" value="hello World!!" /><br> <input type="submit" name="myform:_id0" value="submit버튼" /> <input type="hidden" name="myform" value="myform" /></form> </body> </html> 위의내용과비슷한결과가나올것이다. 위의 html소스를보면 id를붙이지않은컴포넌트는 jsf에의해 id가부여되어있으며, html에서각엘리먼트들의 name속성이 formname:id 명과같이되어있는것을알수있다. 이렇게폼이름 : 컴포넌트id 와같은형식을취하므로, 자바스크립트를쓰는경우이와같이이름이부여되는것에유의해서작성할필요가있다. 20

3.2 rendered 속성 rendered 속성은 id 와마찬가지로모든컴포넌트가가지는속성이다. rendered 속성이 true 이 면컴포넌트는처리되어브라우저에디스플레이되고, false 이면해당컴포넌트는처리되지 않는다. rendered 속성은화면에표시되는컴포넌트를제어할때요긴하게이용할수있다. 위의예제를이용하여다음과같은작업을추가해보자. 우선 Example1.java 를다음과같이수정한다. package jsf.proj.example; public class Example1{ private String msg="hello World!!"; private boolean display=false; public void setvalue(string msg){ this.msg=msg; public String getvalue(){ return msg; public boolean getdisplay() { return display; public void setdisplay(boolean display) { this.display = display; 21

display 라는 boolean 값과이 boolean 값에대한 setter/getter 메소드가추가되었다. example1.jsp 파일은아래와같이수정한다. <!-- /jsp/example/example1.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <h:inputtext id="in1" value="#{example1.value" rendered="#{example1.display" /><br> <h:commandbutton value="submit버튼 " action="success" rendered="#{example1.display==false"/> </h:form> </f:view> </body> </html> 컴포넌트에 rendered속성이추가되었다. 입력박스의 rendered속성값은 getdisplay() 메소드가반환하는값이될것이고, 버튼의 rendered속성은 getdisplay() 메소드가반환하는값이 false이면표현식에의해 true가될것이고, getdisplay() 메소드가 true를반환하면 false가될것이다. EL은이보다더다양한표현식을사용가능하게한다. 더다양한표현식에대해서는차차공부하기로하자. Example1.java의 display속성값이 false이므로아래와같이입력박스는나타나지않고, 버튼만나타난다. 22

rendered 속성은모든컴포넌트가가지는속성중하나이다. rendered 속성을잘이용하면화 면처리를보다더유연하게할수있다. 3.3 컴포넌트의바인딩 id,rendered 속성이외에모든컴포넌트가가지는공통된속성하나가 binding 속성이다. binding 속성은 bean 에서생성된컴포넌트를웹페이지에 binding 하는기능을제공한다. 또다시 Example1.java 소스를수정해보자. package jsf.proj.example; import javax.faces.component.html.*; public class Example1{ private HtmlInputText input; public Example1(){ input=new HtmlInputText(); input.setvalue(" 컴포넌트바인딩 "); 23

public HtmlInputText getinput() { return input; public void setinput(htmlinputtext input) { this.input = input; HtmlInputText 컴포넌트는 html 의입력박스를나타내는컴포넌트이다. Exmaple1.java 에서 생성된이컴포넌트는아래와같이웹페이지에바인딩된다. <!-- /jsp/example/example1.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <h:inputtext id="in1" value="#{example1.value" /><br> <h:inputtext binding="#{example1.input"/><br> <h:commandbutton value="submit버튼 " action="success"/> </h:form> </f:view> </body> </html> 기존의웹페이지에서의작업대부분을서버사이드에서처리하고, binding속성을통해웹페이지에표현하도록할수있다. 위의웹페이지는다음과같이나타난다. 24

bean에서생성한 HtmlInputText컴포넌트를바인딩을이용해서웹브라우저에표시된결과다. 위의예를통해서알수있듯이 JSF는 html을처리하기위한모든요소들을서버사이드에서처리하고, 결과를브라우저에나타낼수있다. 물론바인딩을사용하지않고, JSF페이지에서처리하는것도가능하다. Sun Java Studio Creator 같은비주얼한개발툴은바인딩을통해작성한웹페이지의컴포넌트들을처리한다. 3.4 이벤트 컴포넌트에이벤트를이용해보자. JSF에서개발자들이이용하게될이벤트는 ActionEvent와 ValueChangeEvent일것이다. ActionEvent는 action이일어날수있는버튼과링크컴포넌트에이용하고, ValueChangeEvent는값의변경이일어날수있는입력상자, 라디오버튼, 메뉴, 체크박스등등에사용할수있다. 이벤트를추가할수있는방법은두가지가있는데여기서는컴포넌트의속성을이용해서메소드로정의하는방법을테스트해보도록한다. 위에서사용한 example1.jsp 를다음과같이수정한다. <!-- /jsp/example/example1.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 25

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <h:inputtext id="in1" value="#{example1.value" valuechangelistener="#{example1.valuechanged"/><br> <h:inputtext binding="#{example1.input"/><br> <h:commandbutton value="submit버튼 " action="success" actionlistener="#{example1.actionoccurred"/> </h:form> </f:view> </body> </html> 처음입력상자에 valuechangelistener 속성을이용하여메소드를바인딩한다. 그리고, 명령버튼에 actionlistener 속성을이용하여, 역시메소드를바인딩한다. 여기서, 지정된메소드를구현하면 Example1.java 의소스가다음과같이변경된다. package jsf.proj.example; import javax.faces.component.html.*; import javax.faces.component.*; import javax.faces.event.*; public class Example1{ public void valuechanged(valuechangeevent evt){ UIInput input=(uiinput)evt.getcomponent(); Object old=evt.getoldvalue(); Object newvalue=evt.getnewvalue(); msg=" 컴포넌트 ID : "+input.getid()+" 이전값 : "+old+", 변경된값 : "+newvalue; 26

input.setvalue(msg); public void actionoccurred(actionevent evt){ System.out.println("ActionEvent가발생되었습니다."); JSF의스펙에따라, 이벤트를처리할메소드는 public 이고, 리턴타입이 void이어야한다. 그리고, 파라미터로각각 ValueChangeEvent 개체와 ActionEvent 개체를설정한다. 메소드이름은상관없다. 각이벤트개체는이벤트를발생한컴포넌트를 getcomponent() 메소드를이용하여얻어낼수있다. 그리고, ValueChangeEvent는값이변경되기전과후의값들을얻어낼수있는메소드를위와같이제공하고있다. 다음과같이입력을하고버튼을클릭해보자. 다음과같은내용이출력된다. 27

주의할것은 valuechanged() 메소드에서 ValueChangeEvent 의 getnewvalue() 메소드가반환하는값이이컴포넌트의값이된다. 따라서, valuechanged() 메소드의마지막에 setvalue() 메소드를이용해값을설정하지않으면 getnewvalue() 메소드가반환하는값이여기화면에나타날것이다. valuechanged() 메소드의맨마지막라인에있는 input.setvalue(msg) 메소드를지우고결과를한번보기바란다. 이렇게메소드를바인딩하여이벤트를처리하는경우, 이메소드를다른곳에서사용하기에 는불편하다. 여기서는이정도로설명을마치고 6 장에서는이벤트를이용하는다른방법을 설명하도록하겠다. 이번장에서는간단하게 jsf의몇가지특징을살펴보았다. 위에서살펴본내용들이 JSF의가장큰특징이라할수있겠다. 앞으로살펴볼내용들은위의예제에서이용해보았던컴포넌트들에대한더자세한내용과, 컴포넌트를이용한프로그래밍을지원하는이벤트처리, Validator, Converter 그리고직접컴포넌트를만들어보고간단한웹어플리케이션을제작해보도록한다. 28

4 장. JSF 의구성요소 JSF 의구성요소를이해하기위해우선 JSF 가제공하는 API 의주요패키지를살펴보면다음 과같다. 패키지이름 javax.faces.application 설명 JSF 로개발된웹어플리케이션의전체적인기능들을관리하 기위해만들어진패키지이다. javax.faces.component 컴포넌트들의기본적인특징을구현한패키지. javax.faces.component.html javax.faces.component 에서구현된컴포넌트들을 html 환경 에서이용가능하도록구현한패키지. javax.faces.context request/response 를처리하기위한환경에관련된패키지. javax.faces.convert JSF 의기본 converter 와 converter 인터페이스가있는패키 지. javax.faces.el ValueBinding 과 MethodBinding 을수행하기위한패키지. javax.faces.lifecycle request/response 하나의싸이클을관리하는패키지. javax.faces.model JSF 가지원하는 Data Model 패키지. javax.faces.render 렌더러를위한패키지. javax.faces.validator JSF 의기본 validator 와 validator 인터페이스가있는패키지. javax.faces.webapp JSF 의컴포넌트를이용하기위한관련커스텀태그가있는 패키지. 위의패키지의내용을보면 JSF 는크게다음과같이구성된다. JSF를이용한웹어플리케이션의전체환경을지원하는패키지컴포넌트패키지컴포넌트를지원하는이벤트, Validator, Converter, EL, 렌더러 JSF에서사용되는데이터집합을처리하기위한데이터모델 29

이 4 가지항목이 JSF 의전체적인모습이되겠다. 가능하면많은예제를통해위의 4 가지 항목을설명하도록하겠다. 4.1 컴포넌트 아래의그림은 JSF 가제공하는 javax.faces.component 패키지의클래스다이어그램이다. javax.faces.component 패키지에서구현된클래스들은 JSF 에서사용되는컴포넌트들의기 본적인특징들을모두구현해놓았다. 이패키지의클래스들의이름을보면알겠지만, 클래스이름의첫두글자 UI 만빼면 html 과 30

유사한것들을발견할수있을것이다. 이패키지에서구현된클래스들은대응되는 html 요 소들의중요한특징들을모두구현하고있다. 이패키지의클래스들을상속받아서 javax.faces.component.html 패키지의클래스들이구성된다. 예를들어, html 버튼, html 링크를나타내는 javax.faces.component.html 패키지의클래스 HtmlCommandButton 과 HtmlCommandLink 의클래스다이어그램을보자. html에서버튼과링크는그모양만다를뿐, 특징이유사하다. 이특징을 UICommand 클래스가구현하고있다. HtmlCommandButton과 HtmlCommandLink 클래스는자바스크립트를위한속성값, html스펙을만족하기위한속성그리고 CSS를사용하기위한속성들을위한 getter/setter 메소드만을가지고있을뿐이다. javax.faces.component.html패키지의클래스들이모두이러한구조를가지고있다. 웹페이지에서컴포넌트를사용하기위해서어떤과정을거치게되는지를보자. 31

위의그림은입력상자가웹브라우저에나타나는과정을그린것이다. 다음의순서에따라 하나의컴포넌트가처리된다. 1. 접두어 'h' 는사용될태그라이브러리의위치를표시한다. 2. 태그라이브러리로부터 inputtext라는태그를찾는다. 3. inputtext태그가지시하는태그클래스 InputTextTag를얻는다. 4. InputTextTag 클래스는렌더러를지정하는문자열과컴포넌트는지정하는문자열을얻고있다. 렌더러가있으므로 encode/decode메소드는렌더러에위임된다. 5. HtmlInputText 컴포넌트가생성되고처리된다. 렌더러가있기때문에렌더러의 encode/decode 메소드가호출된다. 6. 웹브라우저에 <input type= > 와같이나타난다. 이과정사이에속성값을설정하는등의여러가지작업이있지만, 컴포넌트가 html 로해석되 는과정에중점을두어이해해주기바란다. 또, 위에서설명한처리과정이나중에우리가 직접컴포넌트를만드는과정과동일하다. 직접만들게될컴포넌트들도 jsf 가제공하는컴 32

포넌트들과동일한방식으로처리될것이기때문이다. 위의예에서알수있듯이컴포넌트 를만들게되면컴포넌트를나타낼태그클래스, 컴포넌트클래스, 렌더러클래스, 태그정 의파일이필요하게된다. 여기서, 렌더러클래스는선택적이다. UIComponent 클래스는모든컴포넌트의기본이되는클래스이다. 이클래스는 request간의상태를저장하는 StateHolder 인터페이스를구현하는추상메소드로이루어져있다. UIComponent클래스를사용하기쉽도록 UIComponentBase클래스를상속하여컴포넌트들이구성된다. 컴포넌트의기본이되는 UIComponentBase클래스의주요메소드를살펴보자. 다음은모든컴포넌트들이가지는주요메소드이다. public void decode(javax.faces.context.facescontext context) 이컴포넌트의이벤트가발생할때호출되는메소드이다. public void encodebegin(facescontext context) 컴포넌트를화면에표시하기위한작업을시작한다. public void encodechildren(facescontext context) 이컴포넌트의자식컴포넌트, 즉 jsf에서이컴포넌트의태그에둘러싸여있는포함된컴포넌트를처리할작업을이메소드에서구현한다. 단, 아래에나오는 getrenderschildren() 메소드가 true값을반환할때이다. public void encodeend(facescontext context) 이컴포넌트를화면에표시하는작업을종료한다. public UIComponent findcomponent(string id) 이컴포넌트의하위컴포넌트들중에지정된 id 를가지는컴포넌트를찾아그컴포넌트 를반환한다. public Map getattributes() 이컴포넌트의속성들을 java.utilmap 으로반환한다. 그러나, java.util.map 에서제공하 는 containskey() 나 remove() 와같은메소드를사용할수없다. public int getchildcount() 만약 getrenderschildren() 메소드가 true 를반환하면, 이컴포넌트의자식컴포넌트의 33

개수를반환한다. public List getchildren() 자식컴포넌트를 java.util.list 로반환한다. protected FacesContext getfacescontext() 현재웹어플리케이션과클라이언트의요청에의해생성된 FacesContext 개체를반환한 다. public UIComponent getfacet(string name) Facet개체를 Facet개체의속성인 name을가지고얻을수있다. Facet는 Facet을포함하고있는컴포넌트와부모-자식간의관계를가지지않는독립적인컴포넌트이다. public Map getfacets() 이컴포넌트가가지고있는모든 Facet 을 java.util.map 으로반환한다. public Iterator getfacetsandchildren() 이컴포넌트가가지고있는 Facet 와자식컴포넌트를반환한다. public String getid() 이컴포넌트의 id 를반환한다. id 는모든컴포넌트에서유일하며, id 를부여받지않은컴 포넌트는 jsf 가내부적으로부여한다. public UIComponent getparent() 이컴포넌트의부모컴포넌트를반환한다. public boolean getrenderschildren() 처리해야할컴포넌트가내부에다른컴포넌트를포함하는경우포함된컴포넌트의처리를어느컴포넌트가하는지를결정하는메소드이다. 각각의컴포넌트는기본적으로자기자신의 encode/decode메소드를호출하여작업을처리한다. 그러나, 컴포넌트를포함하고있는컴포넌트에서이메소드가 true를반환하면, 이컴포넌트가포함하는자식컴포넌트의 encode/decode메소드는사용되지않는다. 이컴포넌트의 encode/decode메소드가자식컴포넌트가해야할작업을구현하여처리한다는뜻이되겠다. jsf컴포넌트들은기본적으로 false를반환하도록되어있으며, 개발자가컴포넌트를개발 34

할때오버라이딩해서이용하면된다. 이컴포넌트가포함하고있는자식컴포넌트의화면표시 (encodebegin/encodeend) 와같은처리를어디서하는지를결정하는메소드이다. 기본적으로각컴포넌트는자신이가지고있는 encodebegin/encodeend메소드를이용해서자기자신을클라이언트의화면에표시하는작업을수행한다. 그러나, 이메소드가 true를반환하면, 자식컴포넌트들의 encodebegin/encodeend는실행되지않는다. 각컴포넌트는자신의 encodebegin/encodeend메소드를수행하도록되어있다. 다시말하면각컴포넌트의 getrenderschildren() 메소드는디폴트값으로 false를반환한다. 또한, false를반환하는경우자식컴포넌트의정보를알수없다. 예를들어위의 getchildcount() 같은메소드는 0을반환할것이다. public boolean isrendered() 이컴포넌트가화면에표시되는지여부를결정한다. 컴포넌트의 rendered 속성으로도지 정할수있다. public void setrenderertype(string renderertype) 이컴포넌트를화면에표시하는작업 (encodebegin/encodeend 메소드 ) 을하게된 renderer를지정한다. 이값이 null이면이컴포넌트자신의 (encodebegin/encodeend) 메소드를수행한다. public void setvaluebinding(string name, ValueBinding binding) 이컴포넌트의속성 (name) 에바인딩되는개체를지정한다. jsf페이지의아래와같은작업을예로들면, <h:inputtext id="in1" value="#{databean.value"/> UIInput컴포넌트의 value속성이가지는값은문자열 '#{DataBean.value' 이아니라, DataBean클래스인스턴스의 getvalue() 메소드가반환하는값이될것이다. 즉, ValueBinding클래스는입력된표현식을적절한값으로반환해주는작업을하는역할을한다. 4.3 managed bean faces-config.xml 에 <managed-bean> 태그를이용하여 bean 을등록할수있다. 여기에 35

등록된 bean들은웹페이지에의해처음사용되는경우생성되며, scope속성에따라 JSF 가알아서내부적으로관리하게된다. 물론 jsp에서처럼 <jsp:usebean > 과같이사용해도무방하다. faces-config.xml에등록되는 bean들은 bean의속성값들을설정하는것도가능하다. 테스트를위해 bean 을하나만들고 faces-config.xml 파일의설정을보도록하자. package jsf.proj.example; import java.util.*; public class TestBean{ private Map mapdata; private List listdata; private Integer number; private String name; public List getlistdata() { return listdata; public void setlistdata(list listdata) { this.listdata = listdata; public Map getmapdata() { return mapdata; public void setmapdata(map mapdata) { this.mapdata = mapdata; public String getname() { return name; public void setname(string name) { this.name = name; public Integer getnumber() { return number; public void setnumber(integer number) { 36

this.number = number; 테스트를위해위와같은멤버변수값들을 faces-config.xml 에다음과같이초기값을설정 할수있다. 물론, 위의 bean 클래스내에서설정하는것도가능하다. <?xml version="1.0" encoding="euc-kr"?> <!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> <managed-bean> <managed-bean-name>testbean</managed-bean-name> <managed-bean-class>jsf.proj.example.testbean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>listdata</property-name> <list-entries> <value-class>java.lang.string</value-class> <value> 홍길동 </value> <value> 이순신 </value> <value> 세종대왕 </value> <value> 강감찬 </value> </list-entries> </managed-property> <managed-property> <property-name>mapdata</property-name> <map-entries> <key-class>java.lang.string</key-class> 37

<map-entry> <key> 서울시 </key> <value> 강남구 </value> </map-entry> <map-entry> <key> 경기도 </key> <value> 이천시 </value> </map-entry> <map-entry> <key> 경상남도 </key> <value> 마산시 </value> </map-entry> </map-entries> </managed-property> <managed-property> <property-name>number</property-name> <value>3</value> </managed-property> <managed-property> <property-name>name</property-name> <null-value/>> </managed-property> </managed-bean> </faces-config> <managed-property> 태그를이용해서위와같이 bean 의초기값을설정하면된다. listdata, mapdata 및 number 변수의값을설정하고, name 변수는널값으로설정하였다. 웹페이지에서이들의결과값을출력해보자. 출력을위해다음과같은페이지를하나만든다. <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 38

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view > <h:form id="testform"> listdata 의세번째값 : <h:outputtext value="#{testbean.listdata[2]"/><br> mapdata 의키 ' 서울시 ' 의값 : <h:outputtext value="#{testbean.mapdata[' 서울시 ']"/><br> mapdata 의키 ' 경상남도 ' 의값 : <h:outputtext value="#{testbean.mapdata[\" 경상남도 \"]"/><br> number 의값 : <h:outputtext value="#{testbean.number"/><br> number 의값이 3 이면 true 를반환한다. : <h:outputtext value="#{testbean.number==3"/><br> name 의값 : <h:outputtext value="#{testbean.name"/><br> </h:form> </f:view> </body> </html> 값이바인딩되는표현들을몇가지볼수있다. 데이터집합의요소들을이용하기위해위와 같은표현들이자주이용될것이다. 키값을사용하는경우 는중복되므로위와같이 \ 를 이용해야한다. 또, "#{TestBean.number==3" 과같은논리값을검사하는것도가능하다. 가능한표현식은다음과같다. 39

+-*/ 연산 <, <=, >=, ==,!=, &&,,! 연산?: 연산 위의결과는브라우저에다음과같이나타난다. 이러한표현식을처리하는클래스가 javax.faces.el.valuebinding클래스이다. ValueBinding 클래스를이용하여 bean에서도위와같이 #{ 로둘러싸인표현식을이용하는것도가능하다. 위와같은표현식이어떻게처리되는지 ValueBinding클래스를이용해서다음과같은테스트를해보자. 테스트를위해 TestBean클래스를다시한번이용한다. package jsf.proj.example; import javax.faces.el.*; import javax.faces.context.*; import javax.faces.application.*; public class TestBean{ 40

public String getname() { FacesContext fc=facescontext.getcurrentinstance(); Application app=fc.getapplication(); ValueBinding vb=app.createvaluebinding("#{testbean.mapdata[' 서울시 ']"); name=(string)vb.getvalue(fc); return name; 테스트를위해간단히 name변수의값에 #{TestBean.mapData[' 서울시 '] 식의결과값을설정하였다. 이러한표현식을처리하고결과를얻어내는클래스가 ValueBinding클래스이다. FacesContext는현재 request를관리하는 context개체이고, 이개체로부터동적인 ValueBinding을수행하기위해 Application개체를얻어와서 ValueBinding을수행한다. 결과로 서울시 를키값으로가지는개체가 name 변수의값으로설정되어아래와같이출력 된다. JSF 는이렇게표현식문자열을처리하는 ValueBinding 뿐만아니라, 앞장의예제에서본것 처럼컴포넌트를처리할수도있고, 또, 이벤트를위해메소드를바인딩할수도있다. 메소 41

드를바인딩하는부분은이벤트를처리하는부분에서다루기로하자. 4.4 렌더러 jsf는다른프레임워크와달리렌더링을분리했다. 렌더링이란특별한개념이아니고, 컴포넌트들이클라이언트의화면에어떻게디스플레이가되는지를지정한다는것이다. 예를들에 UICommand 컴포넌트는브라우저에버튼이나링크를나타내는 HtmlCommandButton 컴포넌트나 HtmlCommandLink 컴포넌트의부모클래스이다. 이들컴포넌트클래스는클라이언트의화면에어떻게표시되는지에대한정보가없다. 컴포넌트를처리한결과가웹브라우저에표시되는지, xml파일로저장되는지컴포넌트는알수없다. 지정된렌더러가이결과를처리한다. 아래의표는 jsf가제공하는컴포넌트들과컴포넌트의상위클래스그리고, 각컴포넌트에지정된렌더러를표로나타낸것이다. 컴포넌트 상위클래스 렌더러 타입 ( 단순한 (javax.faces.component.html (javax.faces.component) 문자열이다.) 패키지 ) HtmlCommandButton UICommand javax.faces.button HtmlCommandLink UICommand javax.faces.link HtmlDataTable UIData javax.faces.table HtmlForm UIForm javax.faces.form HtmlGraphicImage UIGraphic javax.faces.image HtmlInputHidden UIInput javax.faces.hidden HtmlInputSecret UIInput javax.faces.secret HtmlInputText UIInput javax.faces.text HtmlInputTextarea UIInput javax.faces.textarea HtmlMessage UIMessage javax.faces.message HtmlMessages UIMessages javax.faces.messages HtmlOutputFormat UIOutput javax.faces.format HtmlOutputLabel UIOutput javax.faces.label HtmlOutputLink UIOutput javax.faces.link HtmlOutputText UIOutput javax.faces.text HtmlPanelGrid UIPanel javax.faces.grid HtmlPanelGroup UIPanel javax.faces.group HtmlSelectBooleanCheckbox UISeleceBoolean javax.faces.checkbox HtmlSelectManyCheckbox UISelectMany javax.faces.checkbox HtmlSelectManyListbox UISelectMany javax.faces.menu 42

HtmlSelectManyMenu UISelectMany javax.faces.listbox HtmlSelectOneListbox UISelectOne javax.faces.listbox HtmlSelectOneMenu UISelectOne javax.faces.menu HtmlSelectOneRadio UISelectOne javax.faces.radio 렌더링을분리함으로써, jsf 로개발된컴포넌트들이브라우저가아닌다른클라이언트의환 경에맞도록적절히변화될수있다. 4.5 이벤트 JSF 가제공하는이벤트는 javax.faces.event 패키지에현재다음의 3 가지가있다. ActionEvent ValueChangeEvent PhaseEvent PhaseEvent는 JSF의각단계별 life cycle에따라발생할수있는이벤트이다. 웹어플리케이션을개발하는경우 ActionEvent와 ValueChangeEvent를사용하게될것이다. ActionEvent는 submit이발생할수있는컴포넌트즉, 명령버튼이나링크에사용한다. 그리고, ValueChangeEvent는값의변경이가능한컴포넌트들에이용된다. 이벤트는 html form으로부터 submit이일어나야만발생한다는사실에유의하자. 자바스크립트처럼클라이언트환경에서작동하는것이아니기때문이다. 자바스크립트에의한 submit이나버튼에의한 submit이발생하는경우 JSF는지정된 ActionEvent가있다면, 이벤트를처리하고, 값이변경될수있는라디오버튼이나, 체크박스, 입력상자등의값이변경되는경우지정된 ValueChangeEvent를실행할수있다. ActionEvent 를이용할수있는컴포넌트는 javax.faces.component 패키지의 UICommand 클래스를상속한클래스들이다. ValueChangeEvent 를이용할수있는컴포넌트는 javax.faces.component 패키지의 UIInput 클래스를상속한 HtmlInputHidden, HtmlInputSecret, HtmlInputText, HtmlInputTextarea, UISelectBoolean, UISelectMany, UISelectOne 들이다. JSF 는이벤트를처리하는다음과같은두가지방법을제공한다. 43

하나는, 컴포넌트의속성을이용해서메소드를바인딩하는것이다. <h:commandbutton value="submit 버튼 " action="success" actionlistener="#{example1.actionoccurred"/> 위의명령버튼에지정된 actionlistener 속성은버튼이나링크가가지는속성이다. 여기에바인딩된문자열의의미는 Example1클래스의 actionoccurred 라는메소드를실행한다는것이다. JSF의스펙에따라, actionoccurred메소드는 public 이고, 리턴타입이 void 이며, 파라미터로 ActionEvent를가져야한다. 다음과같이될것이다. public void actionoccurred(actionevent evt){ 두번째방법은, Listener 인터페이스를구현하면된다. javax.faces.event.actionlistener 인터페이스를구현하면된다. package jsf.proj.event; import javax.faces.event.*; public class MyActionListener implements ActionListener{ public void processaction(actionevent evt){ 이때에는태그를사용하는방법이조금달라진다. 위의예에서사용한명령버튼에 MyActionListener 를이용하기위해서는다음과같이변경한다. <h:commandbutton value="submit버튼 " action="success"> <f:actionlistener type="jsf.proj.event.myactionlistener"/> </h:commandbutton> 44

위와같이리스너를구현한클래스를직접만들어이용할수도있다. 이때 type 속성으로 패키지명을포함한클래스이름을지정해야한다. 그러면해당인터페이시의지정된메소드 가수행될것이다. 이와같은방식은 ValueChangeEvent 를처리할때에도동일하다. 또한, 여기서설명한두가 지방식은 Validator 나 Converter 를사용할때에도위의두가지동일한방법을사용할수있 다. 4.6 validator & converter 4.6.1 Validator JSF 가기본적으로제공하는 validator 는다음세가지이다. DoubleRangeValidator LengthValidator LongRangeValidator DoubleRangeValidator 와 LongRangeValidator 는값의유효한범위를지정할수있는방법 을제공하고, LengthValidator 는문자열의길이의유효한범위를지정한다. validation 에서 에러가발생하는경우 ValidatorException 이발생한다. 위에서설명한이벤트의경우처럼 validator를이용하는방법도두가지가있다. 하나는 validator 속성을이용하여 validator 메소드를바인딩하는것이고, 두번째는 javax.faces.validator패키니지의 Validator 인터페이스를구현하는것이다. Validator 인터페이스를구현하여 validator를사용하는경우 faces-config.xml에정의된 validator에대한설정이필요하다. validator 메소드역시 JSF 의스펙에따라 public 이고, 리턴타입은 void 이고, 파라미터로는 FacesContext, UIComponent, Object 를가져야한다. 다음과같은형태가될것이다. Validator 인터페이스의파라미터도역시동일하다. public void validatetest(facescontext context,uicomponent comp, Object value){ 45

현재 request 가가지는 FacesContext 와이메소드를호출하는컴포넌트그리고, 평가될값 이 Object 형태로넘어올것이다. 4.6.2 Converter Converter 는웹페이지에서처리되는문자열을적절한타입의값으로변환하는역할을한다. Converter 역시 converter 속성을이용해서 다음은 JSF 가기본적으로제공하는 converter 이다. BigDecimalConverter BigIntegerConverter BooleanConverter ByteConverter CharacterConverter DateTimeConverter DoubleConverter FloatConverter IntegerConverter LongConverter NumberConverter ShortConverter converter 는사용하는방법이조금틀리다. 메소드를바인딩하는방법은사용되지않고, javax.faces.convert 패키지의 Converter 인터페이스를구현하여이용한다. Converter 인터 페이스는다음과같은두가지메소드가있다. public Object getasobject(facescontext context, UIComponent component, String value) public String getasstring(facescontext context, UIComponent component, Object value) getasobject() 메소드는파라미터로넘어오는문자열 value 값이설정되는컴포넌트의속성 의적절한타입에맞도록문자열을변환한다. 또, getasstring() 메소드는컴포넌트의값을 문자열로변환한다. 적절한타입으로변환할수없을경우, ConvertException 이발생한다. 46

4.7 navigation 이번에는 JSF에서의웹페이지의이동에대해서알아보자. 어느페이지에서어느페이지로이동할지를결정하는모든설정은 faces-config.xml에서이루어진다. 웹페이지에 submit 이발생하는경우 navigation handler에 action속성에지정된문자열값이건네어지고, 이문자열값으로다음에나타날페이지가결정된다. 만약, 주어진 action 속성의문자열값과일치하는 navigation을찾지못하면, 현재페이지를다시표시한다. faces-config.xml 에페이지의이동을위한 navigation 은다음의형식을따라설정된다. <navigation-rule> <from-view-id>/from.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/to.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>fail</from-outcome> <to-view-id>/to_fail.jsp</to-view-id> </navigation-case> </navigation-rule> 경로를표시하는문자열은 / 로시작해야한다는것에유의하자. from.jsp 라는페이지로부터 action 속성의문자열 success가반환된다면, from.jsp 페이지로부터 to.jsp라는페이지로 request는 forward 된다. fail 이라는문자열이얻어진다면, to_fail.jsp 페이지로 forward 될것이다. from.jsp 페이지의다음과같은버튼에의해 action 이발생한다고하면 <h:commandbutton value= OK action= #{Login.confirm /> Login 클래스의 confirm 메소드를구현하자. 47

public class Login{ public String confirm(){ if (login ok) return success ;// 적절한로직을구현하자!!! else return fail ; confirm 메소드가반환하는문자열에따라 forward 되는페이지가달라질것이다. 위의예에서, from-view-id 태그가없을수도있다. 그러면어떠한페이지에서건, action 속 성이문자열 success 나 fail 을반환하면 to-view-id 에지정된 url 로 forward 될것이다. from-view-id 태그에와일드카드 * 를사용하여도결과는마찬가지이다. <navigation-rule> <from-view-id>*</from-view-id> </navigation-rule> 다음과같이하나의 navigation-rule 에동일한두문자열이얻어지는경우를보자. from.jsp 페이지에다음과같이버튼이두개있다고하자. <h:commandbutton value= OK action= #{Login.confirm /> <h:commandbutton value= OK action= #{Login.check /> 각각의메소드가다음과같다고하자. public class Login{ public String confirm(){ if (login ok) return success ; else return fail ; 48

public String check(){ if (ok) return success ; else return chkfail ; navigation-rule에서는 confirm() 메소드에서얻어지는 success 인지 check() 메소드에서얻어지는문자열인지상관없이 to.jsp로 forward 될것이다. 개발자가 check() 메소드가반환하는문자열이 success 이면 check.jsp로의이동을원한다면, 다음과같이 from-action 태그를이용하면된다. <navigation-rule> <from-view-id>/from.jsp</from-view-id> <navigation-case> <from-action>#{example1.confirm</from-action> <from-outcome>success</from-outcome> <to-view-id>/to.jsp</to-view-id> </navigation-case> <navigation-case> <from-action>#{example1.check</from-action> <from-outcome>success</from-outcome> <to-view-id>/check.jsp</to-view-id> </navigation-case> </navigation-rule> from-action 태그에지정된문자열값은클래스그이름과메소드이름을나타낸다. 그러나이문자열이메소드를호출하는것은아니다. 단순히 success라는문자열을구분하는키값정도에불과하다. 이렇게함으로써, 각버튼에의해발생하는 action을이용해서서로다름페이지로보내도록할수도있다. 마지막으로, JSF 는기본적으로 forward 로페이지이동을한다. redirect 를원한다면, redirect 태그를지정하면된다. 위의예에서맨마지막의경우 check.jsp 페이지로 redirect 를원한다면, 다음과같이수정한다. <navigation-rule> 49

<to-view-id>/check.jsp</to-view-id> <redirect/> 50

5. JSF Life Cycle 5.1 JSF Life Cycle 클라이언트의 request를처리하고, response를생성하기위해 JSF는내부적으로많은일들을처리한다. request에포함된컴포넌트트리를구성하고, 파라미터로넘어온값들을지정된 converter 나 validator 를이용해서유효한값인가를검사하고, 각단계에서필요한이벤트를처리한다. 아주짧은시간에일어나는이러한작업단계들을 JSF는아래와같이구분하고있다. 이번장에서는이러한각단계 (Phase) 에서어떠한작업들이처리되는지와이러한처리작업단계를변경할수있는 immediate속성에대해알아보도록한다. 가장일반적인 JSF 의 request 처리는다음의단계를거친다. 51

각각의단계에대해알아보자. 1. Restore View request 를처리하기위한 FacesContext 개체가생성된다. request 에포함된 JSF 컴포넌트들의컴포넌트트리를구성한다. 컴포넌트트리는 UIViewRoot 라는클래스인스턴스를최고상위노드로하는트리이다. 2. Apply Request Values 컴포넌트의변경되는값들을임시로관리한다. validation, conversion 과정을거치지않았기 때문에변경될값들은아직컴포넌트에적용되지않았다. 3. Validation 각컴포넌트마다지정된 validation이있는경우 validation을수행한다. validation에실패하면 validation에러가발생한다. 값의변경이발생하는경우를위해컴포넌트가 Value Change Event를지정했다면 Validation이끝난후이벤트가수행된다. 물론, validation이성공적으로수행된경우이다. 4. Update Model Values 모든값들이유효하므로이단계에서컴포넌트의값들을변경한다. 이제컴포넌트들이가지 는이전값들은없어지고, 새로운값들이설정된다. 이새로운값들을컴포넌트의값으로적 절히변환될수없는경우 conversion 에러가발생한다. 5. Invoke Application 에러가발생하지않고이단계까지오면, 모든컴포넌트의값들이에러없이성공적으로갱 신되었다는뜻이되겠다. 이제버튼이나링크의지정된 ActionEvent 있으면, 이이벤트가 수행된다. 6. Render Response 현재상태를저장하고, 이동할페이지로 request 가 forward 된다. JSF 는기본적으로모든 52

submit 을 forward 한다. forward 가필요치않은경우 faces-config.xml 의 navigation 에 redirect 를지정하면된다. 5.2 immediate 속성 immediate 속성은버튼이나링크컴포넌트그리고값이변경될수있는입력상자, 라디오버튼, 체크박스, 메뉴등의컴포넌트들만가지고있다. immediate속성은 true/false 를값으로가질수있으며, 이속성이지정되지않는경우는 false이다. immediate속성값이 false인경우 JSF의 life cycle은위의그림과동일한과정을따른다. 그러나, 해당컴포넌트에 immediate속성값이 true로설정되면조금복잡하다. 예를들어어떤웹페이에입력상자와명령버튼이있다고하자. 입력상자의 immediate 속성 은 false 이고, 명령버튼의 immediate 속성이 true 이면아래와같은과정을거치게될것이다. 53

ActionEvent란쉽게말하면, 웹브라우저에서버튼이나링크를클릭해서 submit이발생하는경우이다. 따라서, ActionEvent가발생하면, 진행과정을모두멈추고, 결과를브라우저에출력하기위해 html을생성하는 render response상태로넘어가게된다. 물론입력박스의지정된 validation이나 Value Change 와같은이벤트는발생하지않는다. 위의불투명하게터리된과정들을건너뛰기때문이다. 그러나, Action Event가발생하지않는다면위의불투명하게처리된과정들을모두수행하게된다. 이번에는값이변경될수있는컴포넌트인입력상자의 immediate 속성값이 true 라고가정해 보자. 그러면다음의그림과같이처리된다. 54

위의그림은입력상자와버튼으로구성된웹페이지의두컴포넌트가모두 immediate 속성 을 true 로가질때 JSF 가 request 를처리하는과정을나타내는것이다. immediate 속성은지정된컴포넌트의이벤트처리시점을옮기게함으로써 JSF 의기본적인 lifecycle 을개발자들이요구하는데로조정될수있는유연성을제공한다고볼수있다. 그러면, 어떤경우에 immediate 속성을써야할까? 예를들어다음과같이국가를선택하고, 도시명을적어야하는페이지가있다고가정하자. 55

우리는이페이지에서국가를선택하고, 도시명을적도록한다. 이페이지의소스는다음과 같다. <!-- /jsp/example/cycle_test.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="testform"> 나라이름 : <h:selectonemenu value="ko" onchange="submit()" valuechangelistener="#{lifecycletest.valuechanged"> <f:selectitem itemvalue="ko" itemlabel=" 한국 " /> <f:selectitem itemvalue="jp" itemlabel=" 일본 "/> <f:selectitem itemvalue="usa" itemlabel=" 미국 "/> 56

</h:selectonemenu> <br> 도시 : <h:inputtext id="in1" value="#{lifecycletest.city" required="true"/><br> <h:commandbutton value="ok" action="success"/> <br> <h:message for="in1"/> </h:form> </f:view> </body> </html> selectonemenu 태그는메뉴를만들어내는태그이다. 메뉴에서국가이름을선택하면 valuechanged라는메소드가호출되도록되어있고, submit이발생한다. immediate 속성이어떤역할을하는지를살펴보는보기위해아직사용해보지않은컴포넌트인메뉴박스를사용하였다. 각컴포넌트의사용법은다음장에서자세히설명하도록한다. 메뉴에서국가이름을바꿀때마다, LifeCycleTest 클래스의 valuechanged 메소드가호출되도록되어있다. 또, 도시이름을입력받는입력상자는반드시입력을하도록하는 required속성이 true로되어있다. 아무값을입력하지않으면, message 컴포넌트에의해에러메시지가브라우저에나타난다. message컴포넌트의 for 속성이가리키는값은컴포넌트의 id이다. 위의페이지를위해사용될 LifeCycleTest.java 소스는다음과같다. package jsf.proj.example; import javax.faces.event.*; import javax.faces.context.*; public class LifeCycleTest{ private String city; public void valuechanged(valuechangeevent evt){ System.out.println("value change event 가발생되었습니다."); 57

public String getcity() { return city; public void setcity(string city) { this.city = city; 위의 bean 을보면, 도시명을변수로가지고, 메뉴선택시발생하는이벤트를처리하는 valuechanged 메소드를가지고있다. ok 버튼을클릭하면도시명을출력하는다음의페이지로이동한다. <!-- /jsp/example/cyce_test_dest.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="testform"> 도시이름 : <h:outputtext value="#{lifecycletest.city"/><br> </h:form> </f:view> </body> </html> 이제마지막으로 navigation 과 bean 을 faces-config.xml 에등록하자. <?xml version="1.0" encoding="euc-kr"?> <!DOCTYPE faces-config PUBLIC 58

"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <faces-config> <managed-bean> <managed-bean-name>lifecycletest</managed-bean-name> <managed-bean-class>jsf.proj.example.lifecycletest</managed-bean- <managed-bean-scope>request</managed-bean-scope> </managed-bean> class> <navigation-rule> <from-view-id>/jsp/example/cycle_test.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/jsp/example/cycle_test_dest.jsp</to-view-id> </navigation-case> </navigation-rule> </faces-config> 이제 ok 버튼을클릭하면 cycle_test_dest.jsp 페이지로이동을하고도시이름을나타낼것이 다. 이제 cycle_test.jsp 페이지를불러서국가이름을변경하면다음과같은에러메시지가발생한 다. 59

이페이지의국가이름을변경하는경우를보면다음과같은과정을거칠것이다. 메뉴에지정된 valuechanged 메소드가호출되고처리된다. submit 이발생한다. 현재페이지를갱신할것이다. 도시이름을입력받는컴포넌트에값이없으므로, message 태그에의해 context 내의 에러메시지가출력된다. message 태그는 FacesContext내에서발생한에러를출력하는역할을할뿐이다. message 태그를없애고, 에러가화면에나타나지않는다고해서에러가발생하지않은것은아니라는얘기다. 또, 도시이름을입력하지않고, ok버튼을클릭하면페이지는이동하지않는다. 여전히 validation error가발생하고있기때문이다. JSF를이용해서웹어플리케이션의개발시, 위와같은유사한상황들이많이발생할것이다. 이러한경우를해결하기위해 immediate속성이필요하다. cycle_test.jsp 메뉴부분을다음과같이변경한다. 위치는상관없다. <h:selectonemenu value="ko" onchange="submit()" valuechangelistener="#{lifecycletest.valuechanged" immediate= true > 60

메뉴의 immediate 속성을 true로설정한다. 그러나위와같이해도여전히 JSF의 life cycle 은진행되어서, 입력상자의 validation error는계속발생한다. 우리는단지 immediate 속성을이용해서이벤트가처리되는시점을앞당겼을뿐이다. 입력상자를나타내는컴포넌트는 JSF의 life cycle을수행하게되므로여전히 validation error가발생할것이기때문이다. 따라서, LifeCycleTest bean 의 valuechanged 메소드가실행되는경우, 강제로 render response 상태로넘어가도록하여 validation 을건너뛰도록한다. LifeCycleTest bean 을다음과같이수정한다. public void valuechanged(valuechangeevent evt){ System.out.println("value change event가발생되었습니다."); FacesContext fc=facescontext.getcurrentinstance(); fc.renderresponse(); FacesContext 의 renderresponse 메소드는호출되는순간바로현재페이지의나머지단계 들을생략하고 render response 상태로넘긴다. 따라서 validation 에러를피할수있다. 명령버튼이나링크에의한액션은이러한메소드호출이필요없다. 액션이발생하면바로 render response 상태로넘어간다. JSF 에서 immediate 속성을이해하는것이조금까다로운부분일것이다. 나름대로여러가 지테스트를해보면서감을잡기바란다. 이장에서의설명을요약하면다음과같다. immediate 속성이 JSF 의 life cycle 에서이벤트가발생하는시점을조절한다. immediate 속성이 true인명령버튼이나링크가클릭되는경우해당페이지의나머지모든과정이생략되고, 화면표시를위한 render response상태로넘어가게된다. Value Change Event가발생할수있는컴포넌트들, 예를들면메뉴, 입력상자, 라디오버튼등의 immediate 속성이 true이면이벤트가발생하는시점이조절되고, 61

render response 로넘어가기위해서는 FacesContext 의 renderresponse() 메소드를 호출해야한다. 62

6. JSF 컴포넌트 6.1 Html 컴포넌트 이번장에서는 JSF에서제공되는기본컴포넌트에대한설명을하도록한다. 모든컴포넌트는다음세가지속성을각각가지고있다는것을명심하자. id rendered binding 6.2 HtmlCommandButton HTML input 요소를생성하는컴포넌트이다. 다음의속성들을이용할수있다. 구분 속성 JSF id, rendered, binding, action, actionlistener, immediate, value html accesskey, alt, dir, disabled, image, lang, readonly, tabindex, title, type javascript onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect CSS style, styleclass 이컴포넌트는앞의예제에서많이이용하였으므로, 중요한속성에대해서만설명을하도록하겠다. type 속성은 submit 이거나 reset 값이다. type 속성을생략하면, 기본적으로 submit이된다. action 속성이반환하는문자열은 JSF의 navigation handler에의해 facesconfig.xml 설정파일의 navigation-rule 태그의 from-outcome 값이될것이다. actionlistener 속성에지정되는메소드는 public 이고, 리턴타입이 void 이며, javax.faces.event.actionevent 개체를파라미터로가져야한다. ActionEvent 개체는이벤트를발생한컴포넌트를 getcomponent() 메소드를이용하여얻을수있다. 6.3 HtmlCommandLink HTML a 앵커를생성한다. 구분 속성 63

JSF html javascript CSS id, rendered, binding, action, actionlistener, immediate, value accesskey, charset, coords, dir, hreflang, rel, rev, shape, tabindex, target, title, type onblur, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup style, styleclass HtmlCommandLink 컴포넌트와위에서설명한 HtmlCommandButton 컴포넌트는똑같이 UICommand 클래스를상속한다. HtmlCommandLink 컴포넌트역시 HtmlCommandButton 과 같은방법으로이벤트를처리할수있도록 action 속성과 action Listener 속성을지원한다. 이컴포넌트가생성하는 html 을살펴보도록하자. 다음의예를보자. <h:form id="testform"> <h:commandlink id="link1" action="success"> <h:outputtext value="click!!"/> <f:param name="name" value="kimsk"/> </h:commandlink> </h:form> 위의 commandlink태그는다음과같은자바스크립트를생성하도록되어있다. 위에사용된 param 태그는파라미터를설정하는 core 컴포넌트이다. 브라우저의소스보기를선택하여소스를보면, 다음과같은내용이생성되어있음을알수있다. 파라미터값이자바스크립트의 onclick이설정된다. <a id="testform:link1" href="#" onclick="document.forms['testform']['testform:link1'].value='testform:link1'; document.forms['testform']['name'].value='kimsk'; document.forms['testform'].submit(); return false;">click!!</a> HtmlCommandLink 는내부적으로위와같은자바스크립트를생성하여, submit 을처리한다. 64

6.4 HtmlForm HTML form 요소를생성하는컴포넌트이다. 구분 속성 JSF id, rendered, binding html accept, acceptcharset, dir, enctype, lang, target, title javascript onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onreset, onsubmit CSS style, styleclass form 의 method 속성은 POST 로설정된다. 6.5 HtmlGraphicImage html 의 img 태그를생성한다. 구분 속성 JSF id, rendered, binding, url, value html alt, dir, height, width, ismap, lang, longdesc, title, usemap javascript onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup CSS style, styleclass url 속성을이용하여상대경로나절대경로로이미지파일명을지정하면된다. 사용할수있다. 다음과같이 <h:graphicimage url= images/img1.jpg alt= 이미지예제입니다. /> 6.6 HtmlInputHidden HTML input 태그의타입이 hidden인요소를만든다. 구분속성 JSF converter, id, rendered, binding, immediate, required, validator, value, valuechangelistener html 없음 javascript 없음 65

CSS 없음 html 의 input type 이 hidden 인 html 을생성한다. <h:inputhidden id="hid1" value="test"/> 위의내용은아래와같이 html 로번역된다. <input id="myform:hid1" type="hidden" name="myform:hid1" value="test" /> 6.7 HtmlInputSecret html 의 input type 이 password 인 html 을생성한다. 구분 JSF html javascript CSS 속성 converter, id, rendered, binding, immediate, required, validator, value, valuechangelistener accesskey, alt, dir, disabled, lang, maxlength, readonly, redisplay, size, tabindex, title onblur, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect style, styleclass html 의 input type 이 password 인 html 을생성한다. <h:inputsecret /> 위의소스는아래와같이번역된다. <input type="password" name="myform:_id1" value="" /> 6.8 HtmlInputTextarea HTML textarea 요소를생성한다. 구분 속성 JSF converter, id, rendered, binding, immediate, required, validator, value, valuechangelistener html accesskey, cols, dir, disabled, lang, readonly, rows, tabindex, title javascript onblur, onchange, onclick, ondblclick, onfocus, onkeydown, 66

CSS onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect style, styleclass <h:inputtextarea id="ta1" cols="10" rows="10" /> 위의소스는아래와같이번역된다. <textarea id="myform:ta1" name="myform:ta1" cols="10" rows="10"></textarea> 6.9 HtmlInputText 구분 속성 JSF converter, id, rendered, binding, immediate, required, validator, value, valuechangelistener html accesskey, alt, dir, disabled, lang, maxlength, readonly, size, tabindex, title javascript onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect CSS style, styleclass HtmlInputHidden, HtmlInputSecret, HtmlInputTextarea 와여기서설명하는 HtmlInputText 컴포넌트는모두 UIInput 클래스를상속받았다. 따라서여기서설명하는 컴포넌트의주요속성은위의 4 개의컴포넌트에모두적용된다. UIInput클래스가가지는주요속성은다음과같다. required 속성은이컴포넌트의 value 값을반드시입력받도록한다. submit 이 발생하는 경우 이 컴포넌트의 값이 변경되었을 경우 ValueChangeEvent를발생하도록할수있다. validator 를이용하여유효한값이입력되었는지체크할수있다. converter 를이용하여, 값을적적한타입으로변경할수있다. 6.10 HtmlMessage 67

JSF 가제공하는에러메시지를출력하는컴포넌트이다. 구분 JSF html javascript CSS 속성 id, rendered, for, binding, showdetail, showsummary, title, tooltip, 없음없음 errorclass, errorstyle, fatalclass, fatalstyle, infoclass, infostyle, style, styleclass, warnclass, warnstyle 위의그림에서도시이름을입력하지않고, ok 버튼을클릭하는경우 Validation error 가 발생한다. 이에러를브라우저에표시하는역할을하는것이 HtmlMessage 컴포넌트가 수행한다. 도시 : <h:inputtext id="in1" value="#{lifecycletest.city" required="true"/><br> <h:commandbutton value="ok" action="success"/><br> <h:message for="in1"/> message 태그가가지는속성 for 는컴포넌트의 id 를나타낸다. 어느컴포넌트에서발 생한에러를표시할것인지를나타내는것이다. 따라서, message 태그를이용하는경우 for 속성은반드시지정한다. 68

이러한메시지들은 Messages.properties 파일에있다. 기본적으로 JSF 가가지는 Messages.properties 파일은 jsf-impl.jar 파일에있다. 위의그림과같이언어환경에따라, 영어, 독일어, 스페인어, 프랑스어로된메시지를나타낼수있다. 한글로된메시지를출력하고싶으면, Messages_ko.properties 를작성하고, 위치를 faces-config.xml에작성하면, 사용가능하다. 이때한글은자바유틸리티 native2ascii를이용하여유니코드로인토딩하는작업이필요하다. 이책의예제를위해 build.xml에 native2ascii task를작성해놓았으므로참고하도록하자. 한글로된메시지를이용하기위해서 faces-config.xml 에다음과같이설정하도록한다. <faces-config> <application> <message-bundle>jsf.proj.resource.messages</message-bundle> <locale-config> <default-locale>ko</default-locale> <supported-locale>de</supported-locale> <supported-locale>fr</supported-locale> <supported-locale>es</supported-locale> </locale-config> </application> 69

다국에환경의웹어플리케이션이아니라면, <locale-config> 는생략해도된다. ant 에의해 Messages.properties 파일이 Messages_ko.properties 파일로이름이변경 되고, 유니코드로인코딩되에톰갯에배치된다. 위와같이설정을하면, 한글로된에 러메시지를볼수있다. 이책의예제소스에 Messages.properties 파일의메시지들이한글로처리되어있으므 로살펴보도록하자. 6.11 HtmlMessages 구분 JSF html javascript CSS 속성 globalonly, id, rendered, binding, showdetail, showsummary, title, tooltip 없음없음 errorclass, errorstyle, fatalclass, fatalstyle, infoclass, infostyle, style, styleclass, warnclass, warnstyle HtmlMessage 와동일한역할을하지만, 현재 JSF 의 context 에포함되어있는모든에 러메시지가나타난다. HtmlMessage 처럼하나의컴포넌트에대한에러메시지가아니 다. 에러들이죽나열되므로디버깅시에는사용할만한컴포넌트인것같다. 70

6.12 HtmlOutputFormat 파라미터를처리한문자열을출력한다. 구분 JSF html javascript CSS 속성 converter, id, binding, rendered, value, escape, title 없음없음 style, styleclass 다음의예제를살펴보자 <!-- /jsp/example/example2.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:loadbundle basename="jsf.proj.resource.resources" var="bundle"/> <f:view > <h:form id="myform" > <h:outputformat value="#{bundle.testmessage"> <f:param value=" 자바 "/> <f:param value=" 듀크 "/> </h:outputformat> </h:form> </f:view> </body> </html> <f:loadbundle /> 은웹어플리케이션에사용될메시지나기타내용들을모아놓은 properties 파일을읽어들이는데이용하는 core 컴포넌트이다. Messages.properties 파일과마찬가지로, 위에서지정된 Resources.properties 파일은 ant 에의해 Resources_ko.properties 파일로변경되에배치된다. 한글역시유니코드로인코딩 71

되어야브라우저에제대로나타난다. 파라미터로넘겨질값 자바 와 듀크 라는문자열은 Resources.properties 파일의다음과 같은메시지의 {0 과 {1 에각각매핑되어브라우저에나타난다. testmessage= 첫번째파라미터값은 {0 이고, 두번째파라미터값은 {1 입니다... 다음과같이사용해도결과는동일하다. 꼭 Resources.properties 를사용할필요는없 다. <h:outputformat value 첫번째파라미터값은 {0 이고, 두번째파라미터값은 {1 입니다."> <f:param value=" 자바 "/> <f:param value=" 듀크 "/> 6.13 HtmlOutputLabel 구분 속성 72

JSF converter, id, binding, for, rendered, value, escape html accesskey, dir, lang, tabindex, title javascript onblur, onclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup CSS style, styleclass HTML label 요소를만들어낸다. label 요소는어느컴포넌트를위한 label 인지를지정 해야하는 for 속성을가지고있으므로 for 속성을지정해야한다. HTML 스펙에따라, 여기서지정된 label 문자열을클릭하면해당컴포넌트로포커스가맞추어질것이다. 6.14 HtmlOutputLink 구분 속성 JSF converter, id, binding, rendered, value html accesskey, charset, coords, hreflang, rel, rev, shape, tabindex, target, title, type javascript onblur, onclick, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup CSS style, styleclass HTML 앵커를만들어낸다. <!-- /jsp/example/example3.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform-result"> <h:outputlink value="mailto:zzzccc90@yahoo.co.kr" > <h:outputtext value="mail me"/> 73

</h:outputlink><br> <h:outputlink value="example2.jsp"> <h:outputtext value=" 이전페이지로 "/> <f:param name="id" value="zzzccc"/> <f:param name="name" value="kimsk"/> </h:outputlink> </h:form> </f:view> </body> </html> 파라미터를넘기고싶은경우위와같이 param 태그를이용하여, 작성하면된다. 6.15 HtmlOutputText 구분 JSF html javascript CSS 속성 converter, id, binding, rendered, value,escape title 없음 style, styleclass value 속성으로지정된문자열을화면에출력한다. 6.16 HtmlPanelGrid 구분 속성 JSF id, binding, rendered, value, columns, frame, rules html bgcolor, cellpadding, cellspacing, dir, lang, title, width, summary javascript onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, CSS columnclass, footerclass, headerclass, rowclasses, style, styleclass HTML 테이블을생성하는컴포넌트이다. 74

<!-- /jsp/example/example4.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform" > <h:panelgrid id="pg1" title="panel grid test" columns="2" border="1" cellpadding="0"> <h:outputtext value=" 이름 "/> <h:outputtext value="email"/> <h:outputtext value=" 홍길동 "/> <h:outputtext value="zzzccc90@yahoo.co.kr"/> <h:outputtext value=" 이순신 "/> <h:outputtext value="lee@jsf.co.kr"/> </h:panelgrid> </h:form> </f:view> </body> </html> columns 속성에의해컬럼의수가결정된다. 컬럼의수만큼반복되면서값이매핑되고 다음과같이테이블로만들어진다. 75

다음장에서다룰 core 컴포넌트의 facet 을이용해서테이블의머리말과꼬리말을설정할 수도있다. <!-- /jsp/example/example4.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform" > <h:panelgrid id="pg1" title="panel grid test" columns="2" border="1" cellpadding="0"> <f:facet name="header"> <h:outputtext value=" 머리말입니다."/> </f:facet> <f:facet name="footer"> <h:outputtext value=" 꼬리말입니다."/> </f:facet> 76

<h:outputtext value=" 이름 "/> <h:outputtext value="email"/> <h:outputtext value=" 홍길동 "/> <h:outputtext value="zzzccc90@yahoo.co.kr"/> <h:outputtext value=" 이순신 "/> <h:outputtext value="lee@jsf.co.kr"/> </h:panelgrid> </h:form> </f:view> </body> </html> facet 태그의이름을 header, footer 로지정해서테이블에다음과같은작업이가능하다. facet 태그는 facet 태그를포함한컴포넌트에서보통이러한작업들을처리한다. 6.17 HtmlPanelGroup 구분 JSF html javascript CSS 속성 id, binding, rendered 없음없음 style, styleclass 77

panelgorup 으로묶여진컴포넌트들은하나의컬럼으로처리된다. <!-- /jsp/example/example4.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform" > <h:panelgrid id="pg1" title="panel grid test" columns="2" border="1" cellpadding="0"> <f:facet name="header"> <h:outputtext value=" 머리말입니다."/> </f:facet> <f:facet name="footer"> <h:outputtext value=" 꼬리말입니다."/> </f:facet> <h:outputtext value=" 이름 "/> <h:outputtext value="email"/> <h:outputtext value=" 홍길동 "/> <h:outputtext value="zzzccc90@yahoo.co.kr"/> <h:outputtext value=" 이순신 "/> <h:outputtext value="lee@jsf.co.kr"/> <h:panelgroup> <h:outputtext value=" 세종대왕 "/> <h:outputtext value="king@jsf.co.kr"/> </h:panelgroup> <h:panelgroup> <h:outputtext value=" 장보고 "/> 78

<h:outputtext value="jang@jsf.co.kr"/> </h:panelgroup> </h:panelgrid> </h:form> </f:view> </body> </html> html 소스를보면테이블을처리하는방법이조금복잡해보일것이다. panelgrid 컴포넌트나 panelgroup 컴포넌트의테이블생성기능을사용하는경우다음에나오는 DataTable 컴포넌트를이용하는경우가더자주있을것이라고생각한다. 다음에나오는 DataTable 컴포넌트는데이터를불러들여동적인테이블을생성하는강력한컴포넌트이다. 데이터의집합을표현하기위해개발자는루프를돌릴필요가없다. JSF가내부적으로데이터집합을표현하기위해루프를처리할것이다. 6.18 HtmlDataTable 컴포넌트 구분 JSF html 속성 first, id, binding, rendered, rows, value, var, frame, rules bgcolor, border, cellpadding, cellspacing, dir, lang, summary, title, width 79

javascript onclick, ondblclick, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup CSS columnclasses, footerclass, headerclass, rowclasses, style, styleclass DataTable 컴포넌트는웹어플리케이션에서아마도한번이상은기본적으로이용되지않을까한다. 데이터베이스로부터쿼리한결과를테이블로표현하는경우가가장보편적일것이다. 지금까지이러한처리를위해사용된루프문을 ldatatable 컴포넌트에서는 JSF가대신한다. 개발자는 JSF가지원하는데이터집합의타입에맞는데이터만넘겨주면 JSF가데이터집합을테이블형태로처리한다. 많은사이트에서이 DtaTable 컴포넌트를확장하여, 보다사용하기좋은형태로만들어진컴포넌트들을많이개발하고있다. 예를들어각컬럼의링크를클릭하면그컬럼의값들을 sort 할수있도록하는기능등이첨부되는것이다. 여기서설명하는 JSF의 DataTable 컴포넌트는그런기능을아직가지고있지는않다. 이 DataTable 컴포넌트를이용하면쉽게개발할수있으리라본다. 우선, 이컴포넌트의몇가지속성을살펴보자. value 속성은데이터집합을가진다. 이컴포넌트에서표현될데이터는 JSF 가지원하는것 이라야한다. 그러면어떤데이타형들을 DataTable 컴포넌트가지원하는가알아보자. 클래스 ArrayDataModel ListDataModel ResultDataModel ResultSetDataModel ScalarDataModel SelectItem SelectItemGroup 설명 java.lang.object[] 타입의데이타 java.util.list 타입의데이터예 ) ArrayList, LinkdedList, Vector javax.servlet.jsp.jstl.sql.result타입의데이타 java.sql.resultset 타입의데이타 Object타입의데이타 core컴포넌트의 selectitem컴포넌트의 DataModel core컴포넌트의 selectitem컴포넌트의 DataModel 위의표를보면, 자바에서흔히사용하는데이타집합의대부분을지원한다. 위의타입들이 value값으로넘어오면 DataTable 컴포넌트는내부적으로루프를돌려데이터들을표현할수있다. 여기서는 SelectItem과 SelectItemGroup을제외한나머지 DataModel 에대해알아보고, SelectItem과 SelectItemGroup은다음장의 core컴포넌트에서설명하도록한다. 80

우선, 간단하게위에서설명한데이터모델들을테스트해보기위해간단한데이터집 합을만들어보자. package jsf.proj.example; import java.sql.*; import java.util.*; public class DataTableExample{ private Connection conn=null; private Statement stmt=null; private ResultSet rs=null; private List list=null; public DataTableExample(){ //String 배열을생성하고반환한다. public String[] getdataasstring(){ String[] str=new String[5]; str[0]="korea"; str[1]="america"; str[2]="india"; str[3]="brazil"; str[4]="britain"; return str; // java.sql.resultset 을생성하고반환한다. public ResultSet getdataasresultset(){ try{ Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection conn=drivermanager.getconnection("jdbc:mysql://localhost:3306/test?user=id&password=pa ss"); 81

Statement stmt=conn.createstatement(); rs=stmt.executequery("select * from test2"); catch(exception e){e.printstacktrace(); return rs; //java.util.list 형의데이터를생성하고반환한다. // List내부에는 DataTableBean이라는테스트용클래스가저장된다. public List getdataaslist(){ list=new ArrayList(); list.add(new DataTableBean("hong","HongGilDong")); list.add(new DataTableBean("kim","KimGabDol")); list.add(new DataTableBean("lee","LeeSunShin")); list.add(new DataTableBean("jang","JangGilSan")); list.add(new DataTableBean("robert","Robert Junior III")); return list; 여기서사용되는데이터베이스의테이블 test2 를위해다음의스크립트를수행한다. create table test2( email varchar(30), name varchar(20)); insert into test2 values('zzzccc90@yahoo.co.kr', 'LeeSunShin'); insert into test2 values('lim@time.co.kr', 'LimGukJung'); insert into test2 values('hong@hong.co.kr', 'HoneGilDong'); insert into test2 values('jun@cool.co.kr', 'PARK'); insert into test2 values('who@yahoo.co.kr', 'WHO'); DataTableExample 클래스는 jsf 페이지에직접사용되므로 faces-config.xml 에다음과같 이설정을할필요가있다. <managed-bean> <managed-bean-name>datatableexample</managed-bean-name> <managed-bean-class>jsf.proj.example.datatableexample</managed- 82

bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> 위의소스에서 List 타입의데이타를반환하는세번째메소드에사용되는 DataTableBean 클래스는다음과같다. package jsf.proj.example; public class DataTableBean{ private String id; private String name; public DataTableBean(){ public DataTableBean(String id,string name){ this.id=id; this.name=name; public String getid(){ return id; public void setid(string id){ this.id=id; public String getname(){ return name; public void setname(string name){ this.name=name; 83

위의클래스를 DataTable컴포넌트를어떻게사용하는지살펴보자. <!-- /jsp/example/datatable_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="testform"> String배열을읽어들인다. <br> 태그에의해 2번째데이타부터 5번째데이타까지만읽는다. <h:datatable value="#{datatableexample.dataasstring" var="data" first="2" rows="5"> <h:column> <h:outputtext value="#{data"/> </h:column> </h:datatable> <hr> <br> <h:datatable value="#{datatableexample.dataasresultset" var="data" border="1"> <f:facet name="header"> <h:outputtext value=" 데이타베이스를통해 ResultSet을넘겨받는다."/> </f:facet> <f:facet name="footer"> <h:outputtext value=" 데이타베이스를통해 ResultSet을넘겨받는다."/> </f:facet> <h:column> <f:facet name="header"> 84

<h:outputtext value="id"/> </f:facet> <h:outputtext value="#{data.email"/> </h:column> <h:column> <f:facet <h:outputtext </f:facet> name="header"> value="name"/> <h:outputtext value="#{data.name"/> </h:column> </h:datatable> <hr> java.util.list형의데이타를읽어들인다. <h:datatable value="#{datatableexample.dataaslist" var="data"> <h:column> <h:outputtext value="#{data.id"/> </h:column> <h:column> <h:outputtext value="#{data.name"/> </h:column> </h:datatable> </h:form> </f:view> </body> </html> 제일처음에 DataTable 예제를보자.... <h:datatable value="#{datatableexample.dataasstring" var="data" first="2" rows="5"> <h:column> <h:outputtext value="#{data"/> </h:column> 85

</h:datatable>... DataTable 컴포넌트는 DataTableExample 클래스의 getdataasstring() 메소드가반환하는분자배열을값으로가진다. var이라는속성은 datatable태그내에서 value로반환받은값들을사용할때쓰게될일종의별칭 (alias) 이다. 데이터집합의하나의행에포함된값에접근하고자할때이용된다. first 속성은제일처음표시하고자하는데이터인덱스 ( 번호 ) 를 0을기준으로지정한다. 위의예에서 first= 2 라고하였으므로 0을기준으로 3번째데이터부터표시될것이다. rows는 fitsr속성이지정한값을기준으로해서몇개의열을표시할것인지를지정한다. 위의예에서는 5개이상이더라도 5개의열을표시한다. 따라서, 위의경우 3번째데이터부터 5개까지의데이터행을표시한다. 만약, 위의결과배열의 3번째배열요소만표현하고싶다면, outputtext의 value값을다음과같이표현하면된다. <h:outputtext value="#{datatableexample.dataasstring[2]"/> 이렇게하면, 결과는 first 와 rows 에의해정해진횟수만큼루프를돌면서, 배열의 3 번 째요소만 (0 부터시작하므로 ) 루프의횟수만큼표현될것이다. column컴포넌트는 DataTable컴포넌트가루프를돌면서생성하는테이블의하나의컬럼을나타낸다.... <h:datatable value="#{datatableexample.dataasresultset" var="data" border="1"> <f:facet name="header"> <h:outputtext value=" 데이타베이스를통해 ResultSet을넘겨받는다."/> </f:facet> <f:facet name="footer"> <h:outputtext value=" 데이타베이스를통해 ResultSet을넘겨받는다."/> </f:facet> <h:column> <f:facet name="header"> <h:outputtext value="id"/> </f:facet> <h:outputtext value="#{data.email"/> 86

</h:column> <h:column> <f:facet <h:outputtext </f:facet> name="header"> value="name"/> <h:outputtext value="#{data.name"/> </h:column> </h:datatable>... 위의예는두번째 DataTable컴포넌트이다. <f:facet name="header"> 와 <f:facet name="footer"> 는각각생성되는테이블의머리말과꼬리말을표시한다. 또, 컬럼태그안에들어있는 <f:facet...> 는각각그컬럼의제목을표시하고있다. 이 DataTable의 value값은 ResultSet이반환되는데, ResultSet의 email컬럼의값과 name컬럼의값을각각표시하고있다. 여기서 id와 name은데이터베이스로부터얻은 ResultSet에포함된컬럼의이름이다.... <h:datatable value="#{datatableexample.dataaslist" var="data"> <h:column> <h:outputtext value="#{data.id"/> </h:column> <h:column> <h:outputtext value="#{data.name"/> </h:column> </h:datatable>... 세번째예는 List타입의데이타를반환하는데, List타입의데이타내에 DataTableBean 이라는클래스가포함되어있다. <h:outputtext value="#{data.id"/> 를보면, List타입의데이타내에 DataTableBean이라는클래스의 getid() 라는메소드를호출한값이표현될것이다. 87

위의작업결과가브라우저에다음과같이출력된다. DataTable컴포넌트의특징은부엇보다도, 정해진데이타집합을이용해내부적으로루프를사용해서데이타를표현해낼수있다는것이다. 루프문이내부적으로처리됨으로좀더깔끔한코드의작성이가능하다. 나중에다루게될 custom component부분에서도이러한데이타집합을처리하는부분에대해자세히설명한다. 88

89

7. Core 컴포넌트 core컴포넌트는렌더링이필요없는컴포넌트들이다. 대부분 html 컴포넌트를지원하는컴포넌트로이용되거나, core 컴포넌트홀로이용된다. JSF페이지에작성해야하는다음의문자열은 core컴포넌트의태그정의파일을가리킨다. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 7.1 <f:actionlistener> 앞장에서 UICommand로되변되는 HtmlCommandButton, HtmlCommandLink 컴포넌트에서액션이발생하는경우이벤트를처리하는방법을공부했다. Core컴포넌트를이용하여이벤트를처리할수도있는데이때사용되는것이 actionlistener 이다. actionlistener태그의 type속성에 javax.faces.event.actionlistener인터페이스를구현한클래스를파라미터로넘기면 ActionListener인터페이스에지정된 processaction메소드가실행된다. 이클래스를 faces-config.xml에등록할필요는없다. package jsf.proj.event; import javax.faces.component.*; public class MyActionOccurred implements ActionListener{ // ActionListener인터페이스의구현해야할메소드 public void processaction(javax.faces.event.actionevent event){ UIComponent comp=event.getcomponent(); System.out.println(" 컴포넌트 id = "+comp.getid()+" 에서이벤트가발생되었습니다."); core 컴포넌트의 actionlistener는다음과같이사용된다. <!-- /jsp/example/action_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> 90

<body> <f:loadbundle basename="javax.faces.resources" var="bundle"/> <f:view> <h:form id="myform"> <h:commandbutton id="myaction" value="myaction" action="success"> <f:actionlistener type="jsf.proj.event.myactionoccurred"/> </h:commandbutton> </h:form> </f:view> </body> </html> 버튼을클릭하면, MyActionOccurred 클래스의 processaction 메소드가호출된다. type 속 성에패키지명을포함한클래스이름만적어주면된다. 7.2 <f:attribute> 컴포넌트의속성과속성값을넘길수있다. 다른컴포넌트태그내부에서이용해야한다. 이태그를포함하는컴포넌트는 getattributes() 메소드를통해 java.util.map형으로속성값전체를얻어필요한값을얻어낸다. 다음의예를보자. <!-- /jsp/example/attr_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> 91

<h:inputtext id="in1" value="#{attrexample.name"> <f:attribute name="attr1" value="alpha"/> <f:attribute name="attr2" value="this is message"/> </h:inputtext> <h:commandbutton action="success" value="submit" /> </h:form> </f:view> </body> </html> 컴포넌트의 getattribute() 메소드를이용해서반환된 java.util.map 클래스로부터 put() 메소드를이용하여값을변경할수있다. 단, 변경이가능한값에한해서이다. 변경이불가능한값에대해 put() 메소드를사용하면 IllegalArgumentException이발생한다. 이해를돕기위해여기서이페이지의컴포넌트들에대한정보를 bean에서얻어내어이용하는방법에대해알아보자. 위의예를테스트해보기위해 AttrExample.java 파일을다음과같이만든다. 물론 이클래스는 faces-config.xml 에설정이되어야한다. package jsf.proj.example; public class AttrExample{ private String name; public String getname() { return name; public void setname(string name) { this.name = name; 이클래스에서 jsf 페이지의컴포넌트 id 가 in1 인입력상자의컴포넌트에대한정보를 어떻게얻어올수있는지를알아보자. 위의예에서입력상자의속성값 attr1 과 value 92

값을어떻게얻어내는지알아보자. JSF의컴포넌트들이트리구조를이룬다는사실과트리의최고상위노드가 UIViewRoot 클래스라는것을이미앞서설명했었다. 현재의웹페이지를위해이러한정보를담고있는개체가 FacesContext 이다. 따라서, 우리는 FacesContext 개체를얻고, context 개체로부터 UIViewRoot 개체를얻어서, UIViewRoot 개체의 findcomponent() 메소드를이용하여우리가원하는컴포넌트를찾아서조작할수있다. 이런작업을수행하는 findcomp() 메소드를위의 AttrExample.java클래스에다음과같이추가한다. package jsf.proj.example; import javax.faces.context.*; import javax.faces.component.*; import java.util.*; public class AttrExample{ public AttrExample(){ findcomp(); public void findcomp(){ FacesContext fc=facescontext.getcurrentinstance(); UIViewRoot root=fc.getviewroot(); UIInput comp=(uiinput)root.findcomponent("myform:in1"); Map attrs=comp.getattributes(); Iterator keys=attrs.keyset().iterator(); while(keys.hasnext()){ Object key=keys.next(); System.out.println("key : "+key+" value : "+attrs.get(key)); 이메소드를보면, 현재 context 정보를담고있는 FacesContext 개체로부터 UIViewRoot 개체를얻고, findcomponent() 메소드를사용하고있다. findcomponent() 메소드를이용하 93

여컴포넌트의 id가 in1 인컴포넌트를찾는데 myform:in1 과같이 form의 id를사용하고있다는것에유의하자. UIViewRoot의하위노드에 form 컴포넌트가여러개있을수있기때문이다. form 컴포넌트로부터컴포넌트 id를이용하여 findcomponent() 메소드를수행하는경우는 from id를사용할필요가없을것이다. 위의메소드에의해컴포넌트 in1에지정된속성값들을다음과같이얻을수있다. JSF 를이용하여웹어플리케이션을개발하는경우, 위의예처럼서버사이드에서컴포 넌트를찾아서작업을해야하는경우가생길수있다. 서버사이드에서컴포넌트를생성 하여바인딩하는경우는바로컴포넌트에접근할수있으므로문제될것이없을것이다. 7.3 <f:convertdatetime> 컴포넌트의지정된값을날짜와시간에관련된값으로변경해준다. 먼저, 날짜를생성할빈을만들자. 이클래스를 DateTimeExample 이라는이름으로 faces-config.xml 에등록한다. package jsf.proj.example; import java.util.date; public class DateTimeExample{ private Date currentdate; public DateTimeExample(){ currentdate=new Date(); public Date getcurrentdate() { return currentdate; public void setcurrentdate(date currentdate) { this.currentdate = currentdate; 94

<!-- /jsp/example/datetime_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform" > 시간과날짜를변경하는 converter<br> 날짜패턴 (yyyy-mm-dd-hh-mm) :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime pattern="yyyy-mm-dd-hh-mm" /> </h:outputtext><br> 날짜패턴 (G yyyy년 MM월 dd일 a hh시 mm분 ss초 ) :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime pattern="g yyyy년 MM월 dd일 E요일 a hh시 mm분 ss초 " /> </h:outputtext><br> 날짜패턴 (yyyy/mm/dd) :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime pattern="yyyy/mm/dd" /> </h:outputtext><br> 날짜패턴 : 없음 :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime /> 95

</h:outputtext><br> 날짜패턴 : 없음, type=date :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime type="date"/> </h:outputtext><br> 날짜패턴 : 없음, type=time :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime type="time"/> </h:outputtext><br> 날짜패턴 : 없음, type=both :: <h:outputtext value="#{datetimeexample.currentdate"> <f:convertdatetime type="both"/> </h:outputtext><br> </h:form> </f:view> </body> </html> 위의작업의결과는아래와같다. 위의입력과비교해보자. 96

아래는 convertdatetime 컴포넌트를이용할때사용되는주요속성이다. 속성이름기본값유효한값참고 full timestyle default long short medium Type속성값이 time이거나 both 일때 default full datestyle default long short medium Type속성값이 date이거나 both 일때 default date type date both time 97

pattern 없음 G : AD or BC y : 년도 M : 월 d : 일 E : 요일 a : 오전 / 오후 h : 시 m : 분 s : 초 7.4 <f:convertnumber> 입력되는숫자의포맷을여러가지형식으로지정한다. 여기에사용되는숫자는원시타 입이아니다. 다음과같은클래스를만들어테스트해보자. package jsf.proj.example; public class ConvertExample{ private Integer num1=new Integer(3500); private Float num2=new Float(3.141592); private Float num3=new Float(0.4321); public Integer getnum1() { return num1; public void setnum1(integer num1) { this.num1 = num1; public Float getnum2() { return num2; public void setnum2(float num2) { this.num2 = num2; public Float getnum3() { 98

return num3; public void setnum3(float num3) { this.num3 = num3; 위의클래스의값을이용하는웹페이지를만들었다. <!-- /jsp/example/converter_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform" > <h:inputtext value="#{convertexample.num2"> <f:convertnumber type="number" integeronly="false" maxfractiondigits="2" maxintegerdigits="5" /> </h:inputtext><br> <h:inputtext value="#{convertexample.num3"> <f:convertnumber type="percent"/> </h:inputtext><br> <h:inputtext value="#{convertexample.num3"> <f:convertnumber type="percent" pattern="###.##"/> </h:inputtext><br> <h:outputtext value="#{convertexample.num1"> <f:convertnumber type="currency" /> </h:outputtext><br> 99

</h:form> </f:view> </body> </html> 위의예제의결과는브라우저에다음과같이나타난다. 다음의표는 convertnumber 에사용되는주요속성들이다. 속성이름 기본값 유효한값 참고 integeronly false True/false maxfractiondigits 없음 숫자 최대소수자리수 maxintegerdigits 없음 숫자 최대정수자리수 minfractiondigits 없음 숫자 최소소수자리수 minintegerdigits 없음 숫자 최소정수자리수 pattern 없음 ### 예 ) ###.## type number number currency percent 7.5 <f:converter> 개발자가직접작성한 converter 를사용하도록하는태그이다. 이태그는 converterid 라는하나의속성만을가진다. 여기에직접작성한 converter 클 100

래스를패키지명을포함하여기술하면된다. converter 클래스는 javax.faces.convert.converter 인터페이스를구현해야한다. 7.6 <f:facet> facet은위의예에서도보았듯이, facet을포함한컴포넌트와특별한관계를가지는컴포넌트를표시하고자할때이용한다. HtmlPanelGrid 컴포넌트나 HtmlDataTable컴포넌트내에서테이블의제목이나컬럼의제목을나타낼때유용하게이용된다. facet을포함한컴포넌트에서 getfacets() 메소드를이용하여 java.util.map 형으로 facet을얻어내어이용할수있다. 7.7 <f:loadbundle> 지정된로케일에따른 Resouecrs.properties파일을로드한다. var 속성은 Resources.properties 파일을참조하는별명이다. 브라우저의언어환경이한글이라면, Resources_ko.properties 파일을찾고, 이파일이없다면 Resources.properties 파일을참조한다. 다음과같은형식으로이용하면된다. <HTML> <BODY> <f:loadbundle basename="javax.faces.resources" var="bundle"/> <f:view> <h:form id="myform">... <h:outputext value="#{bundle.msg"/>... 7.8 <f:param> 파라미터값을 ReousrceBundle이나컴포넌트에전달하거나, request에포함될파라미터값들을지정할때에이용된다. 아래와같이이용하는경우 {0 과 {1 에파라미터가각각매핑되어출력될것이다. <%@page contenttype="text/html;charset=euc-kr"%> 101

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <BODY> <f:view> <h:form id="myform">... <h:outputformat value="{0, {1"> <f:param value=" 홍길동 "/> <f:param value=" 이순신 "/> </h:outputformat> 7.9 <f:selectitem>, <f:selectitems> core 컴포넌트중에서조금복잡한것들이이두컴포넌트인것같다. JSF 컴포넌트중 메뉴, 리스트박스, 라디오버튼, 체크박스에대한컴포넌트들을살펴보자. 컴포넌트 상위클래스 렌더러타입 (javax.faces.component.html 패키지 ) (javax.faces.component) HtmlSelectBooleanCheckbox UISeleceBoolean javax.faces.checkbox HtmlSelectManyCheckbox UISelectMany javax.faces.checkbox HtmlSelectManyListbox UISelectMany javax.faces.menu HtmlSelectManyMenu UISelectMany javax.faces.listbox HtmlSelectOneListbox UISelectOne javax.faces.listbox HtmlSelectOneMenu UISelectOne javax.faces.menu HtmlSelectOneRadio UISelectOne javax.faces.radio 위의표를보면하나이상의값을선택하는경우에이용될컴포넌트들은 UISelectMany 클래스를상속하고, 하나의값만을선택가능한컴포넌트들은 UISelectOne 클래스를상속하고있음을알수있다. boolean 체크박스는따로구현되어있다. 여기서알수있는것은 HtmlSelectOneRadio 로이용되던컴포넌트들은쉽게 102

HtmlSelectOneMenu 로변경할수있다. 내부적인작동방식은 UISelectOne 을따르고있 기때문이다. 클라이언트환경즉, 브라우저에표현되는방법을지정하는렌더러만위 와같이변경되면된다. 먼저아래와같은 html을살펴보자. 하나만선택가능한메뉴를만드는 html이다. <select id="myform:menu1" name="myform:menu1" size="1" title="menu"> <option value="brazil"> 브라질 </option> <option value="china"> 중국 </option> <option value="vietnam"> 베트남 </option> <option value="japan"> 일본 </option> </select> 위와동일한결과를만들수있는웹페이지를 jsf 를이용하여구성해보자. <!-- /jsp/example/select_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <h:selectonemenu> <f:selectitem itemvalue="brazil" itemlabel=" 브라질 " /> <f:selectitem itemvalue="china" itemlabel=" 중국 "/> <f:selectitem itemvalue="vietnam" itemlabel=" 베트남 "/> <f:selectitem itemvalue="japan" itemlabel=" 일본 "/> </h:selectonemenu> </h:form> </f:view> </body> 103

</html> 위의두결과는동일하다. 위의예처럼사용하게된다면, html 을작성하는것과별반 다르지않다. 이번에는 <f:sekectitems > 태그를사용해보자. <!-- /jsp/example/select_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <b>javax.faces.model.selectitem, javax.faces.model.selectitemgroup를이용한바인딩 </b><br> 라디오버튼 : <h:selectoneradio id="radio2" title="radio1"> <f:selectitems value="#{selectexample.selectdata"/> </h:selectoneradio> 104

<br> 메뉴 : <h:selectonemenu id="menu2" title="menu1"> <f:selectitems value="#{selectexample.selectdata"/> </h:selectonemenu> <br> 리스트박스 : <h:selectonelistbox id="list2" title="list1"> <f:selectitems value="#{selectexample.selectdata"/> </h:selectonelistbox> </h:form> </f:view> </body> </html> selectitems 속성에이용되는 value 값은 SelectItem 클래스의인스턴스가저장된 List 나 Array 타입이어야한다. 위의 selectitems 를이용하기위해다음과같은클래스가 필요하다. package jsf.proj.example; import java.util.*; import javax.faces.component.*; import javax.faces.model.*; public class SelectExample{ private ArrayList data; private SelectItem options1[] = { new SelectItem("SEOUL"," 서울 "), new SelectItem("DAEJUN"," 대전 "), new SelectItem("TAEGU"," 대구 "," 대전시 "), // 이값은비활성화될것이다. new SelectItem("PUSAN"," 부산 "," 부산시 ",true) 105

; public ArrayList getselectdata(){ data=new ArrayList(); SelectItemGroup group1 = new SelectItemGroup(" 우리나라의대도시 : ", null, true, options1); data.add(group1); return data; public void setselectdata(collection data) { this.data = new ArrayList(data); SelectItem 의생성자는다음과같다. SelectItem() SelectItem(Object value) SelectItem(Object value, String label) SelectItem(Object value, String label, String description) SelectItem(Object value, String label, String description, boolean disabled) 각각순서대로값, 라벨, 설명, 그리고활성화비활성화를나타내는파리미터로구성된 다. 위의예처럼 boolean 값이 true 이면비활성화될것이다. SelectItemGroup 의생성자는다음과같다. SelectItemGroup() SelectItemGroup(String label) SelectItemGroup(String label, String description, boolean disabled, SelectItem[] selectitems) 위의페이지의결과를보면라디오버튼, 메뉴, 리스트박스를만들거나변경하는것이 별로어렵지않다는것이다. 비슷한성격의컴포넌트들을렌더러만달리하여표현될수 106

있도록하여지금처럼유연한처리를할수있도록하고있다. 이번에는위와같은작업을 UISelectOne 컴포넌를이용한다. 다음의예제를작성해보자. <!-- /jsp/example/select_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> <b>javax.faces.component.uiselectone를이용한바인딩 </b><br> 라디오버튼 : <h:selectoneradio id="radio1" title="radio" binding="#{selectexample.createselectone" /><br> 메뉴 : <h:selectonemenu id="menu1" title="menu" 107

binding="#{selectexample.createselectone" valuechangelistener="#{selectexample.selectoneclicked" /><br> 리스트박스 : <h:selectonelistbox id="list1" title="list" binding="#{selectexample.createselectone"/> </h:form> </f:view> </body> </html> 위의소스를살펴보면, 라디오버튼과메뉴와리스트가모두동일하게 getcreateselectone() 메소드가반환하는값을바인딩하고있다. 그러나브라우저에나타나는모양은틀리다. 각각의태그에의해렌더러가변경될것이기때문이다. 값이변경되는경우이벤트를처리하는것을보이기위해메뉴상자에 valuechangelistener 를간단히추가하였다. 위의예제에사용되는 SelectExample 클래스를보면다음과같다. package jsf.proj.example; import java.util.*; import javax.faces.application.*; import javax.faces.component.*; import javax.faces.context.*; import javax.faces.event.*; import javax.faces.model.selectitem; import javax.faces.model.selectitemgroup; public class SelectExample{ UISelectOne selectone;//select one컴포넌트 public SelectExample(){ public UISelectOne getcreateselectone(){ //FacesContext fc=facescontext.getcurrentinstance(); //Application app=fc.getapplication(); //selectone=(uiselectone)app.createcomponent("javax.faces.selectone"); selectone=new UISelectOne(); //UISelectItem item1=(uiselectitem)app.createcomponent("javax.faces.selectitem"); 108

UISelectItem item1=new UISelectItem(); UISelectItem item2=new UISelectItem(); UISelectItem item3=new UISelectItem(); UISelectItem item4=new UISelectItem(); item1.setitemlabel(" 브라질 ");item1.setitemvalue("brazil"); item2.setitemlabel(" 중국 ");item2.setitemvalue("china"); item3.setitemlabel(" 베트남 ");item3.setitemvalue("vietnam"); item4.setitemlabel(" 일본 ");item4.setitemvalue("japan"); selectone.getchildren().add(item1); selectone.getchildren().add(item2); selectone.getchildren().add(item3); selectone.getchildren().add(item4); return selectone; public void setcreateselectone(uiselectone comp){ this.selectone=comp; public void selectoneclicked(valuechangeevent valuechange){ System.out.println("ValueChangeEvent 발생..."); UIComponent comp=valuechange.getcomponent(); Object oldvalue=valuechange.getoldvalue(); Object newvalue=valuechange.getnewvalue(); System.out.println("selectOneClicked메소드에서 component id = "+comp.getid()+" 의 select one 값이 "+oldvalue+" 에서 "+newvalue+" 로바뀌었습니다."); 바인딩되는 UISelectOne 클래스의인스턴스가가지는 getter/setter 메소드를구현하고있다. get 메소드호출시 UISelectOne 인스턴스와 SelectItem 인스턴스를생성하고, 반환한다. 위의클래스인스턴스를생성할때주석처리한부분과같이코딩을하여도된다. 참고하기바란다. UISelectOne 인스턴스의 add() 메소드로 UISelectItem을담으면된다. UISelectItem 인스턴스가버튼이나메뉴의요소로화면에처리되어다음과같 109

이나타난다. 라디오버튼이나메뉴, 리스트박스의한가지아이템만을선택가능하게하는컴포넌 트이다. UISelectMany 컴포넌트도위와동일한과정을따르게된다. <!-- /jsp/example/select_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="myform"> =============================== <br> 110

<b>javax.faces.component.uiselectmany를이용한바인딩 </b> <br> 리스트박스 : <h:selectmanylistbox title="select boolean checkbox" binding="#{selectexample.createselectmany" /><br> 메뉴 : <h:selectmanymenu title="select boolean checkbox" binding="#{selectexample.createselectmany" /><br> 체크박스 : <h:selectmanycheckbox title="select boolean checkbox" binding="#{selectexample.createselectmany" /> </h:form> </f:view> </body> </html> UISelectOne 과동일하므로, 설명은생략하도록한다. 여기서사용될클래스의소스 는다음과같다. package jsf.proj.example; import java.util.*; import javax.faces.component.*; import javax.faces.model.*; public class SelectExample{ UISelectMany selectmany;//select many컴포넌트 public UISelectMany getcreateselectmany(){ //selectmany=(uiselectmany)app.createcomponent("javax.faces.selectmany"); selectmany=new UISelectMany(); UISelectItem item1=new UISelectItem(); UISelectItem item2=new UISelectItem(); UISelectItem item3=new UISelectItem(); UISelectItem item4=new UISelectItem();//(UISelectItem)app.createComponent("javax.faces.SelectItem"); item1.setitemlabel(" 브라질 ");item1.setitemvalue("brazil"); item2.setitemlabel(" 중국 ");item2.setitemvalue("china"); 111

item3.setitemlabel(" 베트남 ");item3.setitemvalue("vietnam"); item4.setitemlabel(" 일본 ");item4.setitemvalue("japan"); selectmany.getchildren().add(item1); selectmany.getchildren().add(item2); selectmany.getchildren().add(item3); selectmany.getchildren().add(item4); return selectmany; public void setcreateselectmany(uiselectmany comp){ this.selectmany=comp; 결과화면을보자. 7.10 <f:subview> 다른 jsf페이지에 include되어사용될수있는 jsf페이지를작성할때사용되는최상위태그이다. JSF의모든기능을사용할수있으며, subview내부에 view태그가있으면안된다. 112

<!-- /jsp/example/subpage_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <HTML> <body> <f:subview id="subview1"> <h:form id="subpageform1"> <h:outputtext value=" 서브페이지 1" /> </h:form> </f:subview> </body> </html> JSF 에서이페이지를불러들이는경우 jsp 에서처럼사용한다. <jsp:include page="subpage1.jsp"/> 7.11 <f:validatedoublerange>, <f:validatelength>, <f:validatelongrange> 각각 Double 형데이타와문자열의길이 Long 형데이타가범위에있는지를결정한다. 이컴포넌트들은속성값으로각각 maximum 과 minimum 을가지고있다. <!-- /jsp/example/validate_example.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="/web-inf/validation.tld" prefix="ph" %> <html> <body> <f:view> 113

<h:form id="testform"> <h:inputtext id="in1" required="true" > <f:validatelength maximum="10000.34" minimum="3.14"/> </h:inputtext> <h:message for="in1"/><br> <h:inputtext id="in2" required="true" > <f:validatedoublerange maximum="10000.34" /> </h:inputtext> <h:message for="in2"/><br> <h:inputtext id="in3" required="true"> <f:validatelength minimum="3"/> </h:inputtext> <h:message for="in3"/><br> <h:commandbutton id="cmd1" value="submit" action="success"/><br> </h:form> </f:view> </body> </html> message 태그는 for 속성으로지정된컴포넌트에에러가발생할경우메시지를출력한다. 7.12 <f:validator> 다음장의 validator 부분에서직접정의한 validator 를구현해보도록한다. 7.13 <f:valuechangelistener> 값이변경되는컴포넌트들의이벤트를처리한다. javax.faces.event.valuechangelistener 인터페이스를구현한클래스를속성으로지정하면 된다. ValueChangeListener 인터페이스를구현하는클래스는다음과같이 processvaluechange() 메소드를정의하여야한다. package jsf.proj.event; 114

import javax.faces.event.*; import javax.faces.component.*; public class MyValueChanged implements ValueChangeListener{ public void processvaluechange(valuechangeevent event) { UIComponent comp=event.getcomponent(); Object oldvalue=event.getoldvalue(); Object newvalue=event.getnewvalue(); System.out.println("MyValueChanged 클래스 component id = "+comp.getid()+" 의 select one 값이 "+oldvalue+" 에서 "+newvalue+" 로바뀌었습니다."); <h:inputtext value= test <f:valuechangelistener type="javax.faces.event.myvaluechanged"/> </h:inputtext> type 속성으로패키지명을포함한클래스이름만적어주면된다. 7.14 <f:view> JSF의모든 html컴포넌트와 core컴포넌트는이태그의내부에있어야한다. 속성값으로로케일을설정할수있다. <f:view locale="ko">... </f:view> 115

8. Validator, Converter 를만들어보자. 8.1 Validator 만들기 이번에는직접 JSF 의 validation 을구현해보도록하자. 여기서만들어볼 validator 는유저 가입력하는핸드폰번호가유효한번호인지아닌지를구별하도록만들어본다. 이 validator 는다음의조건이면유효한번호로간주한다. 010,016,017,018,019로시작한다. 국번호는 3자리혹은 4자리숫자이다. 번호는 4자리숫자이다. 번호사이에 '-' 이올수있으며, '-' 없이번호전체를입력해도된다. 우리가만들어볼 validator 에서위의조건을다만족하는입력값이면, 입력값은유효한것으 로처리한다. validator 를구현하기위해서는다음과같은파일이필요하다. 태그정의 TLD 파일 validator 를사용하기위한태그클래스 Validator 인터페이스를구현한 validator 클래스 그리고, 구현한 validator 클래스를 faces-config.xml 에설정하면된다. validator 가처리되는과정을간단히도식화하였다. 116

validator가처리되는과정을보면, 웹페이지로부터, 태그에대한정보를 TLD 파일로부터얻고, 해당태그를 TLD 파일에서찾는다. 그리고나서, 태그를구현한태그클래스 PhoneNumberValidatorTag 로부터 PhoneNumberValidator 인스턴스를얻는다. 이렇게얻어진 PhoneNumberValidator 클래스인스턴스의 validate() 메소드를수행한다. 이와같은과정을따라 validator 를구현해보자. 우선웹페이지에서다음과같은패턴을가지는 validator 를지정했다고가정하고과정을설 명한다. <!-- 117

/jsp/example/custom_validator.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="/web-inf/phonevalidator.tld" prefix="ph" %> <html> <body> <f:view> <h:form id="add-form"> <h:inputtext id="in1" required="true"> <ph:phonevalidator validatepatterns="01[0 1 6 7 8 9]-(\\d{4 \\d{3)- \\d{4;01[0 1 6 7 8 9](\\d{7 \\d{8)"/> </h:inputtext> <h:message for="in1"/><br> <h:commandbutton id="cmd1" value=" VALIDATE" action="success"/><br> </h:form> </f:view> </body> </html> 입력상자에는값을반드시입력하도록 required 속성을설정하고, validatepatterns의정규식을만족하면, 유효한전화번호로처리하게한다. 정규식은맨위에서설명한유효한전화번호의조건을나타낸다. 만약, 이조건을만족하지않으면, 웹브라우저에에러메시지가나타날것이다. 정규식은 ; 로구분되어있으며, validator 클래스에서각각의정규식을입력한값과비교하여유효한번호인지를검사하게된다. TLD 파일 TLD 파일을정의한다. 태그이름과태그클래스, 그리고태그의속성을정의하면된다. <?xml version="1.0" encoding="euc-kr"?> 118

<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>0.03</tlib-version> <jsp-version>1.2</jsp-version> <short-name>javaserverfaces Framework Tag Library</short-name> <description> </description> <!-- ===================== PhoneNumberValidator tags ====================== --> <tag> <name>phonevalidator</name> <tag-class>jsf.proj.validator.phonenumbervalidatortag</tag-class> <description> </description> <attribute> <name>validatepatterns</name> <required>true</required> <rtexprvalue>false</rtexprvalue> <description> </description> </attribute> </tag> </taglib> 태그의속성 validatepatterns 는전화번호의형식패턴을가질것이다. 태그클래스 package jsf.proj.validator; import javax.faces.validator.validator; import javax.faces.webapp.validatortag; 119

public class PhoneNumberValidatorTag extends ValidatorTag{ private String validatepatterns; public PhoneNumberValidatorTag(){ super(); super.setvalidatorid("phonenumbervalidator"); protected Validator createvalidator() throws javax.servlet.jsp.jspexception{ PhoneNumberValidator phonevalidator=null; phonevalidator=(phonenumbervalidator)super.createvalidator(); phonevalidator.setvalidatepatterns(validatepatterns); return phonevalidator; public void release(){ super.release(); validatepatterns=null; public String getvalidatepatterns() { return validatepatterns; public void setvalidatepatterns(string validatepatterns) { this.validatepatterns = validatepatterns; TLD 파일에지정된태그클래스는 ValidatorTag 인터페이스를구현해야한다. createvalidator 메소드가 validate를수행할클래스인스턴스를반환하고, release() 메소드는사용한자원을반환하는메소드이다. 그리고, 속성값 validatepatterns를위한 getter/setter메소드를정의한다. Validator 클래스 package jsf.proj.validator; 120

import java.util.*; import java.util.regex.*; import javax.faces.application.*; import javax.faces.component.*; import javax.faces.context.*; import javax.faces.validator.*; public class PhoneNumberValidator implements Validator{ //jsf페이지에서넘어오는정규식을포함하는문자열변수다. private String validatepatterns; // 정규식이하나이상이므로 ; 를구분자로배열로저장한다. private ArrayList patternlist; public PhoneNumberValidator(){ //validation을수행하는 Validator 인터페이스의메소드를구현한다. public void validate(facescontext context,uicomponent comp,object value){ boolean valid=iscorrect(value); if(!valid){ FacesMessage fm=new FacesMessage(" 핸드폰번호가아닙니다."); //jsf페이지에에러메시지를출력한다. throw new ValidatorException(fm); public boolean iscorrect(object value){ boolean isvalid=false; System.out.println("Value = "+value); if(patternlist==null patternlist.size()==0 value.tostring()=="") return false; String str=value.tostring(); Object[] patterns=patternlist.toarray(); for(int i=0;i<patterns.length;i++){ // 정규식으로문자를포함하는지찾는다. Pattern p = Pattern.compile(patterns[i].toString()); Matcher m = p.matcher(str); 121

if (m.matches()){ isvalid=true; break;// 매칭이되면루프를종료한다. else{ isvalid=false; System.out.println("input = "+str+", Validation = "+isvalid); return isvalid; public void parsepatterns(){ if (validatepatterns == null validatepatterns.length() == 0 ) return; else{ patternlist=new ArrayList(); StringTokenizer st=new StringTokenizer(validatePatterns,";"); while(st.hasmoretokens()) { String token=st.nexttoken(); patternlist.add(token); System.out.println(" 토큰 : "+token); public String getvalidatepatterns() { return validatepatterns; public void setvalidatepatterns(string validatepatterns) { this.validatepatterns = validatepatterns; parsepatterns(); validatepattern 값을얻으면, validator 클래스는패턴을 StringTokenizer 를이용해서 ; 를기 준으로패턴을구분하여저장한다. 이메소드가 parsepatterns() 이다. 이렇게지정된패턴을 저장한후에 validate() 메소드가호출하는 iscorrect() 메소드가정규식을이용하여, 패턴과 122

입력된값을비교한다. 입력된값이패턴과일치하면 true 를, 아니면 ValidatorException 을 발생한다. 이렇게발생한예외는웹페이지에 message 컴포너트에의해브라우저에출력 된다. 이제마지막으로 validator 를 faces-config.xml 에다음과같은형식으로설정을해야한다. <faces-config> <validator> <validator-id>phonenumbervalidator</validator-id> <validator-class>jsf.proj.validator.phonenumbervalidator</validatorclass> <attribute> <attribute-name>formatpatterns</attribute-name> <attribute-class>java.lang.string</attribute-class> </attribute> </validator> 전화번호를입력하고, VALIDATE 버튼을클릭해보기바란다. 번호가잘못입력되면다음과 같이메시지가나타난다. 123

번호를제대로입력하는경우에러메시지는없어지고, 현재페이지를다시표시한다. 왜냐 하면, 이동할페이지에대한설정을하지않았기때문이다. validator 를만드는과정이좀복잡하다고느낄지도모르겠다. 이것을만드는과정과동일하다. 조금만연습해보면금방익숙해질것이다. 컴포넌트를만드는과정도 8.2 Converter 브라우저에서입력되는파라미터들을 bean 을위한적절한타입으로변경하기위해사용되 는기능이다. JSF 가만들어놓은 converter 에대해서는 core 컴포넌트부분에서설명이되 었고, 이번에는개발자가직접작성한 converter 를등록하고, 이용해보도록한다. Converter 는 validator에비해좀더간단히만들어진다. converter를정의하는클래스는 javax.faces.convert패키지의 Converter 인터페이스를구현하면된다. 이 Converter인터페이스의 getasobject(), getasstring() 메소드를구현한다. 웹페이지의값이 bean에설정되는경우 getasobject() 메소드가이용되고, bean의값이웹페이지에출력될경우 getasstring() 메소드가이용된다. getasstring(facescontext context, UIComponent component, Object value) getasobject(facescontext context, UIComponent component, String value) 이두메소드는파라미터로현재웹페이지의 FacesContext, 이 converter 가속해있는컴포 넌트, 그리고값을파라미터로가진다. 테스트를위해다음과같은웹페이지를만들자. <!-- /jsp/example/custom_converter.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> 124

<f:view> <h:form id="converter-example-form"> Converter Example<br> <h:inputtext id="in1" required="true" value="#{converterbean.value" > <f:converter converterid="converterexample"/> </h:inputtext> <h:message for="in1"/><br> <h:commandbutton id="cmd1" value="submit" action="success"/><br> </h:form> </f:view> </body> </html> 다음페이지는 submit 이발생하면, 단순히변경된값을브라우저에출력한다. <!-- /jsp/example/custom_converter_result.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <html> <body> <f:view> <h:form id="result-form"> ConvertBean.value = <h:outputtext value="#{converterbean.value"/><br> </h:form> </f:view> </body> </html> 125

converter 되는값을저장하기위해간단한 bean 을하나만든다. package jsf.proj.converter; public class ConverterBean { private Integer value; public Integer getvalue() { return value; public void setvalue(integer value) { this.value = value; 이제 converter 를만들어보자. 여기서만드는 converter 는간단하다. 입력된값에서숫자만 을얻어내어, Integer 형으로저장한다. 여기서도입력된값으로부터숫자를얻어내기위해 정규식을이용한다. package jsf.proj.converter; import javax.faces.component.uicomponent; import javax.faces.context.facescontext; import javax.faces.convert.converter; import javax.faces.convert.converterexception; public class ConverterExample implements Converter { public ConverterExample(){ public Object getasobject(facescontext context, UIComponent component, String oldvalue) throws ConverterException{ // 정규식을이용해서, 숫자가아닌문자를없앤다. String newvalue=oldvalue.replaceall("\\d",""); if (newvalue.length()==0) throw new ConverterException(); 126

System.out.println("getAsObject oldvalue = "+oldvalue+", newvalue = "+newvalue); return new Integer(newValue); public String getasstring(facescontext context,uicomponent component, Object oldvalue) throws ConverterException{ return oldvalue.tostring(); converter 에서값을변경할수없는경우는 ConverterException 이발생한다. getasstring() 메소드는 bean 의값을문자로반환하는것이므로여기서는특별히하는일이없다. faces-config.xml 에다음과같이 converter 와 bean 그리고 navigation 을설정한다. <faces-config> <converter> <description> Converter TEST </description> <converter-id>converterexample</converter-id> <converter-class>jsf.proj.converter.converterexample</converter-class> </converter> <managed-bean> <managed-bean-name>converterbean</managed-bean-name> <managed-bean-class>jsf.proj.converter.converterbean</managed-bean- <managed-bean-scope>request</managed-bean-scope> </managed-bean> <navigation-rule> <from-view-id>/jsp/example/custom_converter.jsp</from-view-id> <navigation-case> class> 127

<from-outcome>success</from-outcome> <to-view-id>/jsp/example/custom_converter_result.jsp</to-view-id> </navigation-case> </navigation-rule> </faces-config> 다음과같은값을입력해보자. 그러면, 다음과같이 ConverterBean 의 value 값이지정되어출력된다. 128

129

9. Custom Component 구현 -1 JSF를이용하여재사용가능한컴포넌트를만들어보자. JSF를이용하는궁극적인목적은가능한한재사용이쉬운컴포넌트로웹어플리케이션을개발하는것이다. 이번장에서만들어볼컴포넌트는쿼리만주어지면 DB로부터데이터를읽어서브라우저에출력하게끔하는컴포넌트이다. 쿼리된컬럼이름과결과를 Vector클래스에저장하고, 반환한다. 컴포넌트를만드는방법은앞서설명한 Validator와거의동일한과정을따른다. 컴포넌트를만들기위해필요한작업은다음과같다. 작업 설명 태그 태그로사용될클래스를작성한다. 태그로사용될클래스가하는일은컴포 넌트의이름과렌더러의이름 ( 있다면 ) 을반환하고, 입력되는파라미터의 ValueBinding을수행한다. 그리고, 사용된리소스를해제한다. TLD 태그클래스정의 TLD파일 컴포넌트 컴포넌트로 사용될 클래스이다. 이 예제에서는 javax.faces.component.uidata클래스를바로이용하였다. 렌더러 컴포넌트는렌더러에 encode/decode작업을위임할수있다. 위임된 encode/decode작업을 처리한다. 위임하지 않고, 컴포넌트 자체의 encode/decode 메소드를이용해도된다. bean DB로부터얻어진쿼리결과를처리할 bean이다. 먼저 DB 관련환경설정을 Resources.properties 파일에다음과같이하도록한다. DATABASE_DRIVER=com.mysql.jdbc.Driver CONNECTION_URL=jdbc:mysql://localhost:3306/test?user=zzzccc&password=zzzccc&char acterencoding=euc-kr&useunicode=true QUERY=select * from test1 db 에 test1 테이블에다음과같이임시데이터를넣어준다. create table test1( id varchar(20), name varchar(20)); insert into test1 values('zzzccc','kimsk'); insert into test1 values('lee','leesunshin'); insert into test1 values('hong','honggildong'); insert into test1 values('park','parkch'); 130

insert into test1 values('lim','limgj'); 이 QUERY 키값으로주어지는쿼리를수행하고, 결과를브라우저에나타내게될것이다. 9.1 태그클래스작성 태그클래스는다음과같다. package jsf.proj.component1; import javax.faces.webapp.uicomponenttag; import javax.faces.context.*; import javax.faces.component.*; import javax.faces.el.*; public class DataFromDBTag extends UIComponentTag{ // 이 tag 가가지는속성값이다. private String value=null; public void setvalue(string value){ print("setvalue.."); this.value=value; // 리소스를해제한다. public void release() { super.release(); print("release.."); value=null; // 어떤컴포넌트를이용할것인가? public String getcomponenttype(){ print("getcomponenttype.."+uidata.component_type); // 컴포넌트로 UIData를이용한다. //JSF가내부적으로 UIData클래스를정의하는문자열이다. return UIData.COMPONENT_TYPE; 131

//Renderer 가 rendering을수행한다. // 컴포넌트가수행하게하고싶으면 null을반환한다. public String getrenderertype(){ print("getrenderertype.."); return "DBRenderer"; //jsf페이지로부터입력되는속성값을바인딩한다. public void setproperties(uicomponent component){ print("setproperties."); super.setproperties(component); FacesContext context = FacesContext.getCurrentInstance(); if (value!=null){ if (isvaluereference(value)){ ValueBinding vb=context.getapplication().createvaluebinding(value); component.setvaluebinding("value",vb); else{ component.getattributes().put("value",value); public void print(string msg){ System.out.println("DataFromDBTag."+msg); 태그클래스는 javax.faces.webapp.uicomponenttag 클래스를상속하여구현한다. 태그 클래스를구성하는메소드에대해알아보자. 메소드이름설명 setvalue() jsp 페이지로부터주어질 value 속성을저장한다. release() 사용하고있는리소스를해제한다. getcomponenttype() 컴포넌트의이름을반환한다. 여기서는 UIData를컴포넌트로이용하므로 UIData의이름을반환하고있다. 132

getrenderertype() setproperties() 렌더러의이름을반환한다. 여기서반환하는렌더러이름 DBRenderer는 faces-config.xml에서참조한다. 이메소드가 null값을반환하면컴포넌트클래스의 encode/decode 메소드가수행된다. 렌더러가있다면 (null 이아니면 ) 주어진렌더러의 encode/decode 메소드를수행한다. 이메소드는 jsp페이지로부터주어지는 value속성에주어지는값이평가되어야할식인지를알아보기위해 isvaluerederende() 메소드를수행한다. 만약, 평가되어야할식이라면단순한문자열로처리되지않고, ValueBinding을수행한다. 만약, 평가되어야할식이아니라면, 속성값으로저장한다. 9.2 TLD 파일의작성 <?xml version="1.0" encoding="euc-kr"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>0.03</tlib-version> <jsp-version>1.2</jsp-version> <short-name>datafromdb Component</short-name> <description> </description> <tag> <name>datafromdb</name> <tag-class>jsf.proj.component1.datafromdbtag</tag-class> <body-content>jsp</body-content> <attribute> <name>value</name> <required>true</required> 133

<rtexprvalue>false</rtexprvalue> </attribute> </tag> </taglib> 태그로사용될이름과태그클래스그리고, 사용될속성을정의한 TLD 파일이다. 9.3 컴포넌트클래스 이번예제에서는컴포넌트클래스를구현하지않고, 이미구현되어있는 javax.faces.uidata 를컴포넌트로이용하였다. 9.4 렌더러 렌더러의소스는다음과같다. package jsf.proj.component1; import java.io.ioexception; import java.util.iterator; import java.util.list; import javax.faces.component.*; import javax.faces.context.*; import javax.faces.render.renderer; public class DataFromDBRenderer extends Renderer { public DataFromDBRenderer(){ super(); public void encodebegin(facescontext context, UIComponent component) throws IOException { super.encodebegin(context, component); print("encode begin..."); ResponseWriter writer = context.getresponsewriter(); // 현재컴포넌트는 tag 클래스에서정의한컴포넌트 UIData이다. UIData data = (UIData) component; 134

print("row AVAILABLE = "+data.isrowavailable() +", ROW INDEX = "+data.getrowindex() ); // 컴포넌트로부터 List를얻는다. List list=(list)data.getvalue(); Iterator iter=list.iterator(); writer.write("<table border='1'>"); while(iter.hasnext()){ writer.write("<tr>"); String[] rs=(string[])iter.next(); for(int i=0;i<rs.length;i++){ if(rs[i].length()>0) writer.write("<td>"+rs[i]+"</td>"); else writer.write("<td> * </td>");// 컬럼값이없으면 -을출력한다. writer.write("<tr>"); writer.write("</table>"); public void encodeend(facescontext context, UIComponent component) throws IOException { print("encode end..."); public void print(string msg){ System.out.println("DataFromDBRenderer."+msg); 렌더러는현재 JSF의 context와이렌더러에 encode/decode메소드를위임한컴포넌트를파라미터로가진다. encodebegin 메소드가 FacesContext개체로부터 ResponseWriter를얻어브라우저로의출력을위한작업을수행한다. 출력될데이터를가진 UIData컴포넌트로부터쿼리된결과를 List형으로받아서테이블을생성한다. encodeend 메소드는따로작업할내용이없다. 135

9.5 bean DataFromDBBean 클래스는 Resources.properties 에있는 QUERY 키값으로주어진쿼리 를수행하고, 그결과를 TableData 클래스에저장한다. package jsf.proj.component1; import java.sql.connection; import java.sql.drivermanager; import java.sql.resultset; import java.sql.resultsetmetadata; import java.sql.statement; import java.util.list; import java.util.resourcebundle; public class DataFromDBBean { // ResourceBundle파일이있는경로 private String RESOURCE_BUNDLE_PATH="jsf.proj.resource.Resources"; private String DRIVER_CLASS; private String CONNECTION_URL; private String QUERY; private Connection conn=null; private Statement stmt=null; private ResultSet rs=null; private ResultSetMetaData rsmetadata=null; private List list=null; public DataFromDBBean() { // 에러시문자열을출력하기위해 Resource.properties에서필요한문자열을읽어온다. ResourceBundle rb=resourcebundle.getbundle(resource_bundle_path); DRIVER_CLASS=rb.getString("DATABASE_DRIVER"); CONNECTION_URL=rb.getString("CONNECTION_URL"); QUERY=rb.getString("QUERY"); public List getlist() { 136

print("getlist"); try{ Class.forName(DRIVER_CLASS).newInstance(); conn=drivermanager.getconnection(connection_url); stmt=conn.createstatement(); rs=stmt.executequery(query); // 컬럼이름을얻기위해메타데이타를얻는다. rsmetadata=rs.getmetadata(); // 컬럼이름을 vector에넣는다. TableData tabledata=new TableData(rsMetaData); while(rs.next()) //ResuleSet을 vector에넣는다. tabledata.setdata(rs); list=tabledata.gettabledata(); stmt.close(); conn.close(); catch(exception e){ System.out.println(e.toString()); try{ stmt.close(); conn.close(); catch(exception ee){ return list; public void print(string msg){ System.out.println("DataFromDBBean."+msg); TableData 클래스는 ResultSet 을파라미터로받아서, 배열의맨처음에컬럼이름을저장하고, 그다음부터각각의레코드셋을저장한다. ResultSetMetaData 클래스는 ResultSet 에서컬럼 이름을얻기위한데타데이타를포함하고있는클래스이다. 137

TableData 클래스의소스는다음과같다. package jsf.proj.component1; import java.sql.*; import java.util.*; public class TableData{ private List table; private int columncount; TableData(ResultSetMetaData rsmetadata) throws SQLException{ table=new ArrayList(); // 컬럼이름을저장한다. setcolumndata(rsmetadata); public List gettabledata(){ return table; //ResultSetMetaData를이용해서컬럼이름을얻는다. // 컬럼이름은 vector의 firstelement로저장된다. private void setcolumndata(resultsetmetadata rsmetadata) throws SQLException{ this.columncount=rsmetadata.getcolumncount(); String[] data=new String[columnCount]; // 컬럼의인덱스는 1부터시작한다. for (int i=1;i<=columncount;i++) data[i-1]=rsmetadata.getcolumnname(i); table.add(data); // ResultSet의결과를 Vector에넣는다. public void setdata(resultset rs) throws SQLException{ String[] data=new String[columnCount]; for(int i=1;i<=columncount;i++) data[i-1]=rs.getstring(i); 138

table.add(data); 9.6 faces-config.xml 컴포넌트를사용하기위해컴포넌트를 faces-config.xml 에등록하는작업이필요하다. 다음과같이설정한다. 이예제에서사용된컴포넌트 UIData 는 JSF 가이미알고있으므로 따로등록할필요는없다. <?xml version="1.0" encoding="euc-kr"?> <!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> <application> <message-bundle>jsf.proj.resource.messages</message-bundle> <locale-config> <default-locale>ko</default-locale> <supported-locale>de</supported-locale> <supported-locale>fr</supported-locale> <supported-locale>es</supported-locale> </locale-config> </application> <!-- DataFromDB Bean --> <managed-bean> <managed-bean-name>datafromdbbean</managed-bean-name> <managed-bean-class>jsf.proj.component1.datafromdbbean</managedbean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> 139

<render-kit> <renderer> <component-family>javax.faces.data</component-family> <renderer-type>dbrenderer</renderer-type> <rendererclass>jsf.proj.component1.datafromdbrenderer</renderer-class> </renderer> </render-kit> </faces-config> 9.7 테스트 이것으로컴포넌트의작성이모두끝났다. 테스트를위해다음의 jsp 페이지를만들어보자. <!-- /jsp/component1/datafromdb.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="/web-inf/datafromdb.tld" prefix="db" %> <html> <body> <f:view> <h:form id="data-db-form"> <db:datafromdb value="#{datafromdbbean.list"/> </h:form> </f:view> </body> </html> 태그를사용하기위해 TLD 파일의위치와별명을다음과같이지정해야한다. <%@ taglib uri="/web-inf/datafromdb.tld" prefix="db" %> 140

http://localhost:8080/jsf-proj/faces/jsp/component1/datafromdb.jsp 위와같은결과가브라우저에나타날것이다. CSS 를지원할속성을몇가지추가하여, 보기 좋도록꾸미면될것이다. 처음컴포넌트를만들때복잡하다고느낄수있지만, 몇번만들어보면의외로간단하다. 서블릿과커스텀태그를섞어쓴다는느낌으로만들어보면될것같다. 다음장에서는다른컴포넌트를하나더구현해보도록하자. 141

10. Custom Component 2 이번에만들어볼컴포넌트는 Resources.properties에지정된이미지를나열하고, 선택된이미지에대한설명과이미지를보여주는간단한이미지게시판정도로보면되겠다. 여기서구현할컴포넌트는 MethodBinding을이용하여이벤트를처리하도록하는부분을유의해서보도록하자. 나머지부분은앞서설명한컴포넌트와별반다를것이없다. 컴포넌트를작성하는과정역시앞의과정과동일하므로, 바로소스에대한설명을하도록 한다. 먼저 Resources.properties 에다음의내용을추가하자. # image-list.jsp 파일에나타날제목 imglist_title=image 를 Display 하는 custom component # 화면에표시할이미지의총갯수를표시한다. imglist_total=9 # 이미지파일의경로 imglist_img0=/jsp/images/img1.jpg imglist_img1=/jsp/images/img2.jpg imglist_img2=/jsp/images/img3.jpg imglist_img3=/jsp/images/img4.jpg imglist_img4=/jsp/images/img5.jpg imglist_img5=/jsp/images/img6.jpg imglist_img6=/jsp/images/img7.jpg imglist_img7=/jsp/images/img8.jpg imglist_img8=/jsp/images/img9.jpg # 각이미지파일에대한설명 imglist_desc_img0= 그림 1에대한설명입니다. imglist_desc_img1= 그림 2에대한설명입니다. imglist_desc_img2= 그림 3에대한설명입니다. imglist_desc_img3= 그림 4에대한설명입니다. imglist_desc_img4= 그림 5에대한설명입니다. imglist_desc_img5= 그림 6에대한설명입니다. 142

imglist_desc_img6= 그림 7 에대한설명입니다. imglist_desc_img7= 그림 8 에대한설명입니다. imglist_desc_img8= 그림 9 에대한설명입니다. 우선모든이미지가나열될때표시할제목과, 이미지파일의개수, 각이미지파일의경로명그리고, 각이미지에대한간단한설명을추가한다. 위의 9개의이미지가 image-list.jsp 파일에서열리고, 이미지를클릭하면해당이미지에대한설명과앞뒤의이미지를볼수있는링크가생성되도록컴포넌트를구성한다. 이러한작업을수행하도록하기위해이미지를나열하는작업을하는컴포넌트, 그리고하나의이미지에대한표시와이미지의이동을담당할링크를생성하는작업을하는컴포넌트이렇게두개의컴포넌트가구현될것이다. 먼저, Resources.properties 를읽어모든이미지를표시할컴포넌트를만들어보자. 10.1 ImageListComponent 의구현 10.1.1 태그클래스 ImageListComponent 클래스를위한태그클래스는다음과같다. package jsf.proj.component2; import javax.faces.component.uicomponent; import javax.faces.webapp.uicomponenttag; import javax.faces.context.*; import javax.faces.component.*; import javax.faces.el.*; import javax.faces.event.*; public class ImageListTag extends UIComponentTag{ private String actionlistener; private String title; private String width; private String height; private String value; public void setactionlistener(string actionlistener) { this.actionlistener = 143

actionlistener; public void settitle(string title) { this.title= title; public void setwidth(string width) { this.width=width; public void setheight(string height) { this.height=height; public void setvalue(string value) { this.value=value; public String getcomponenttype(){ return "ImageListComponent"; public String getrenderertype(){ return null; public void release(){ super.release(); title=null; width=null; height=null; actionlistener=null; value=null; public void setproperties(uicomponent component){ super.setproperties(component); ValueBinding vb=null; FacesContext context = FacesContext.getCurrentInstance(); if (actionlistener!= null) { if (isvaluereference(actionlistener)) { Class args[] = { ActionEvent.class ; MethodBinding mb = FacesContext.getCurrentInstance().getApplication().createMethodBinding(actionListener, args); ((ImageListComponent)component).setActionListener(mb); print(" actionlistener"); else { Object params [] = {actionlistener; throw new javax.faces.facesexception(); 144

if (title!=null){ if (isvaluereference(title)){ vb = context.getapplication().createvaluebinding(title); component.setvaluebinding("title",vb); print(" title"); else{ component.getattributes().put("title",title); if (width!=null){ if (isvaluereference(width)){ vb = context.getapplication().createvaluebinding(width); component.setvaluebinding("width",vb); print(" width"); else{ component.getattributes().put("width",width); if (height!=null) { if (isvaluereference(height)){ vb = context.getapplication().createvaluebinding(height); component.setvaluebinding("height",vb); print(" height"); else{ component.getattributes().put("height",height); if (value!=null){ if (isvaluereference(value)){ vb = context.getapplication().createvaluebinding(value); component.setvaluebinding("value",vb); print(" value"); else{ 145

component.getattributes().put("value",value); public void print(string msg){ System.out.println("ImageListTag."+msg); 이태그클래스는표시될이미지의크기를결정할속성 width, heght와이미지의배열을반환할 value 속성, 그리고, 지정된이벤트를처리할 actionlistener속성을가진다. 사실, actionlistener속성에지정된메소드는그냥단순히분자열만출력하고하는일은없는메소드이다. 여기에 actionlistener 속성을사용한이유는 actionlistener속성에지정되는문자열은 ValueBinding으로처리되지않고, MethodBinding을이용한다는것을설명하기위해서이다. 위의소스중다음부분을살펴보자. Class args[] = { ActionEvent.class ; MethodBinding mb = FacesContext.getCurrentInstance().getApplication().createMethodBinding(actionListener, args); ((ImageListComponent)component).setActionListener(mb); ValueBinding과마찬가지로, MethodBinding역시평가되어야할문자열을받아서, 해당문자열을처리하여야하는데, ValueBinding처럼결과값이있는것이아니다. 그리고, 파리미터로처리될클래스역시필요하다. 따라서, 파라미터로처리될클래스정보를배열로만들어해당클래스타입을 args[] 로넘길필요가있다. 앞으로작성하게될 ImageListComponent는 UICommand를상속받아사용하므로, UICommand클래스의메소드 setactionlistener() 에평가된 MethodBinding을설정한다. JSF가제공하는기본적인컴포넌트들의 actionlistener 나 valuechangelistener역시내부적으로이와동일한과정을처리하도록되어있다. 이컴포넌트는렌더러를따로구현하지않으므로 getrenderertype() 메소드가 null을반환한다. 따라서, 컴포넌트가자체적으로 encode/decode 메소드를구현할것이다. 나머지부분은앞장의태그클래스와동일하므로설명을생략하도록한다. 146

10.1.2 TLD 파일의작성 ImageListComponent 태그에서이용될속성들을지정한다. <?xml version="1.0" encoding="euc-kr"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>0.03</tlib-version> <jsp-version>1.2</jsp-version> <short-name>imagelist component tag library</short-name> <tag> <name>imagelist</name> <tag-class>jsf.proj.component2.imagelisttag</tag-class> <body-content>jsp</body-content> <description>resorces.properties에서읽은이미지들을표시한다.</description> <attribute> <name>title</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <name>width</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <name>height</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <attribute> <name>value</name> <required>false</required> <rtexprvalue>false</rtexprvalue> 147

</attribute> <attribute> <name>actionlistener</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> </taglib> 10.1.3 컴포넌트클래스 아래에컴포넌트클래스의소스를나타내었다. 이컴포넌트클래스는 jsp 페이지에서읽은 속성값을얻어내고, 자바스크립트생성을위해 form 컴포넌트를찾아서 (form 이 view 태그내 에여러개있을수있으므로 ) 해당 form 컴포넌트의 id 를얻고, html 을생성한다. package jsf.proj.component2; import java.io.ioexception; import javax.faces.el.*; import javax.faces.component.*; import javax.faces.context.*; import javax.faces.context.responsewriter; import javax.faces.el.methodbinding; import javax.faces.event.*; import java.util.*; public class ImageListComponent extends UICommand{ public ImageListComponent() { super(); public String getfamily(){ return"imagedisplaycomponent"; 148

public void encodebegin(facescontext context) throws IOException { print("encode begin..."); public void encodeend(facescontext context) throws IOException{ print("encodeend..."); ExternalContext ec=context.getexternalcontext(); ResponseWriter writer = context.getresponsewriter(); //width,height는표현식이아니고값으로들어오므로, 속성값에서얻을수있다. String width=(string)getattributes().get("width"); String height=(string)getattributes().get("height"); // 표현식으로부터값을얻어낸다. ValueBinding t = getvaluebinding("title"); String title=(string)t.getvalue(context); ValueBinding vb = getvaluebinding("value"); Vector imglist=(vector)vb.getvalue(context); //context path를얻는다. String path=ec.getrequestcontextpath(); // 스크립트생성을위해 form의 id를얻어낸다. String id=getformid(context); //html을생성한다. writer.write("<input type='hidden' name='currentindex'>"); writer.write("<h2 align='center'>"+title+"</h2><p>"); writer.startelement("table",null); writer.startelement("tr",null); for(int i=0;i<imglist.size();i++){ ImageBean img=(imagebean)imglist.elementat(i); writer.startelement("td",null); writer.write("<a href='#' onmousedown=\"document.forms['"+id+"']['currentindex'].value='"+i+"';document.forms ['"+id+"'].submit()\">"); writer.write("<img src='"+path+(img.getimg())+"' width='"+width+"' height='"+height+"' >"); 149

writer.endelement("a"); writer.endelement("td"); writer.endelement("tr"); writer.endelement("table"); public void decode(facescontext context){ print("decode"); setaction(new MethodBindingImpl("success")); queueevent(new ActionEvent(this)); //form의 id를찾는다. protected String getformid(facescontext context){ String formid=null; UIViewRoot root=context.getviewroot(); Iterator children=root.getchildren().iterator(); while(children.hasnext()){ UIComponent comp=(uicomponent)children.next(); if(comp instanceof UIForm) { formid=comp.getid(); break; return formid; public void print(string msg){ System.out.println("ImageListComponent."+msg); 위의소스를보면, 이미지의크기를나타내는 width, height 는 getattributes() 메소드를이용 해값을얻고있다. width, height 속성이 jsp 페이지로부터 #{ 로둘러싸인표현식이아닌단 순히값만주어지기때문이다. 또, <a href > 링크에사용될 form 의 id 를얻기위해 150

getformid() 라는메소드를구현하였다. decode() 메소드를살펴보자. decode() 메소드는이미지를클릭하는경우 MethodBinding개체를생성하고, 이 MethodBinding개체를컴포넌트에설정한다. MethodBinding개체에파라미터로주어지는 success라는문자열은 faces-config.xml의 <navigation-rule> 의 <fromoutcome> 에지정된문자열이다. queueevent() 메소드로컴포넌트의 ActionEvent를저장한다. 아래의 MethodBinding인터페이스를구현한클래스의 invoke() 메소드가반환하는문자열이 (success 가될것이다 ) NavigationHandler에전달되어페이지가이동될것이다. 다음은 MethodBindingImpl 클래스의소스이다. package jsf.proj.component2; import javax.faces.context.facescontext; import javax.faces.el.methodbinding; public class MethodBindingImpl extends MethodBinding { private String action = null; public MethodBindingImpl() { print("constructor"); public MethodBindingImpl(String action) { print("constructor action = "+action); this.action = action; public Object invoke(facescontext context, Object params[]) { print("invoke"); return action; public Class gettype(facescontext context) { return String.class; public void print(string msg){ System.out.println("MethodBindingImpl"+msg); 151

10.1.4 bean 클래스작성 bean 클래스는 Resources.properties 로부터이미지에관련된정보를읽고배열로저장하고, 앞서설명한 actionlistener 에지정된메소드를구현하는클래스이다. package jsf.proj.component2; import javax.faces.component.*; import javax.faces.context.facescontext; import javax.faces.event.actionevent; import java.util.*; public class ImageListBean{ private Vector list=null; private String RESOURCE_BUNDLE_PATH="jsf.proj.resource.Resources"; private String IMAGE_FILE="imgList_img"; private String IMAGE_DESC="imgList_desc_img"; private int IMAGE_TOTAL; private ResourceBundle rb; public ImageListBean() { public Vector getlist(){ rb=resourcebundle.getbundle(resource_bundle_path); IMAGE_TOTAL=Integer.parseInt(rb.getString("imgList_total")); list=new Vector(); for(int i=0;i<image_total;i++){ String file=rb.getstring(image_file+i); String desc=rb.getstring(image_desc+i); list.addelement(new ImageBean(file,desc)); //System.out.println("getList() 메소드가호출되었습니다."); return list; public void processclickevent(actionevent event) { FacesContext context = FacesContext.getCurrentInstance(); UIComponent component = event.getcomponent(); System.out.println(" 컴포넌트 id = "+component.getid()+" 에서이벤트가 152

발생했습니다."); Resources.properties 에서읽은이미지파일경로와이미지에대한설명을저장하는 ImageBean 클래스는다음과같다. package jsf.proj.component2; public class ImageBean { private String img;// 이미지경로 private String desc;// 이미지에대한설명 public ImageBean(){ public ImageBean(String img,string desc){ this.img=img; this.desc=desc; public String getdesc() { return desc; public void setdesc(string desc) { this.desc = desc; public String getimg() { return img; public void setimg(string img) { this.img = img; 153

10.2 ImageDisplayComponent ImageDisplayComponent 컴포넌트는이미지리스트에서선택된이미지와이미지에대한설 명을표시하고, 이미지를앞뒤로이동하는링크를생성한다. 역시, 컴포넌트를만드는과정 은동일하다. 10.2.1 태그클래스 ImageListComponent 에서반환하는이미지를포함하는 Vector 클래스를 value 속성으로가진 다. 소스는다음과같다. package jsf.proj.component2; import javax.faces.component.uicomponent; import javax.faces.webapp.uicomponenttag; import javax.faces.el.*; import javax.faces.context.*; import javax.faces.application.*; import javax.faces.*; public class ImageDisplayTag extends UIComponentTag{ private String value; public void setvalue(string value) { this.value=value; public String getcomponenttype(){ return "ImageDisplayComponent"; public String getrenderertype(){ return null; public void release(){ super.release(); value=null; 154

public void setproperties(uicomponent component){ super.setproperties(component); ValueBinding vb=null; FacesContext context = FacesContext.getCurrentInstance(); if (value!=null){ if (isvaluereference(value)){ vb = context.getapplication().createvaluebinding(value); ((ImageDisplayComponent)component).setValueBinding("value",vb); else{ component.getattributes().put("value",value); 10.2.2 TLD 파일의작성 10.1.2 에서작성한 TLD 파일에다음을추가한다. <tag> <name>imagedisplay</name> <tag-class>javax.faces.componentimg.imagedisplaytag</tag-class> <body-content>jsp</body-content> <description> 하나의이미지를표시하는컴포넌트태그이다. </description> <attribute> <name>value</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> 155

10.2.3 컴포넌트클래스 선택된이미지를표시하고, prev, next 링크를생성한다. package jsf.proj.component2; import java.io.ioexception; import java.util.map; import java.util.vector; import javax.faces.component.*; import javax.faces.context.externalcontext; import javax.faces.context.facescontext; import javax.faces.context.responsewriter; import javax.faces.el.*; public class ImageDisplayComponent extends UIComponentBase { private Vector vec; public String getfamily(){ print("getfamily called..."); return "ImageDisplayComponent"; public ImageDisplayComponent() { super(); print("constructor"); public void encodebegin(facescontext context) throws IOException { print("encode begin..."); ValueBinding vb = getvaluebinding("value"); 156

Vector imglist=(vector)vb.getvalue(context); //Vector imglist=getvalue(); if(imglist==null imglist.isempty()){ ExternalContext ec=context.getexternalcontext(); ec.redirect("image-list.jsp"); else{ print("vector size = "+imglist.size()); public void encodeend(facescontext context) throws IOException{ print("encodeend..."); ExternalContext ec=context.getexternalcontext(); Map request=ec.getrequestparametermap(); String path=ec.getrequestcontextpath(); ValueBinding vb = getvaluebinding("value"); Vector imglist=(vector)vb.getvalue(context); int inx=0; try{ inx=integer.parseint((string)request.get("currentindex")); catch(exception e){ ec.redirect("image-list.jsp"); ImageBean currentimgbean=(imagebean)imglist.elementat(inx); ResponseWriter writer = context.getresponsewriter(); writer.write("<input type='hidden' name='currentindex' value='"+inx+"'>"); writer.write("<table border='1'>"); writer.write("<tr align='center'><td colspan='3'>"); writer.write(currentimgbean.getdesc()); writer.write("</td></tr>"); writer.write("<tr align='center'><td colspan='3'>"); 157

writer.write("<img src='"+path+currentimgbean.getimg()+"'>"); writer.write("</td></tr>"); writer.write("<tr align='center'><td>"); writer.write(getprev(inx,imglist)); writer.write("</td><td>"); writer.write("<a href='image-list.jsp'>home</a>"); writer.write("</td><td>"); writer.write(getnext(inx,imglist)); writer.write("</td></tr>"); writer.write("</table>"); writer.writecomment(" 이것은연습입니다."); public void decode(facescontext context){ print("decode"); // previous 링크를생성하는문자열을반환한다. public String getprev(int inx,vector imglist){ if (inx==0) return "PREV"; String p="document.forms[0].currentindex.value='"+(--inx)+"';"; p+="document.forms[0].submit();"; String prev="<a href='#' onmousedown=\""+p+"\">prev</a>"; return prev; // next 링크를생성하는문자열을반환한다. public String getnext(int inx,vector imglist){ if (inx==(imglist.size()-1)) return "NEXT"; String p="document.forms[0].currentindex.value='"+(++inx)+"';"; p+="document.forms[0].submit();"; String next="<a href='#' onmousedown=\""+p+"\">next</a>"; return next; public void print(string msg){ System.out.println("ImageDisplayComponent."+msg); 158

public Vector getvec() { return vec; public void setvec(vector vec) { this.vec = vec; 10.3 faces-config.xml 드디어설정이다. 다음의내용을 faces-config.xml 에추가하자. <faces-config> <!-- ImageListComponent 등록 --> <component> <component-type>imagelistcomponent</component-type> <componentclass>jsf.proj.component2.imagelistcomponent</component-class> </component> <component> <component-type>imagedisplaycomponent</component-type> <componentclass>jsf.proj.component2.imagedisplaycomponent</component-class> </component> <!-- ImageList Bean --> <managed-bean> <managed-bean-name>imagelistbean</managed-bean-name> <managed-bean-class>jsf.proj.component2.imagelistbean</managedbean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> 159

<navigation-rule> <from-view-id>/jsp/component2/image-list.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/jsp/component2/image-list-result.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/jsp/component2/image-result.jsp</from-view-id> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/jsp/component2/image-list.jsp</to-view-id> </navigation-case> </navigation-rule> </faces-config> 10.4 테스트 테스트는간단하다. 다음의두개의 jsp 파일을만들자. 이미지의리스트를나타낼 jsp페이지이다. <!-- /jsp/component2/image-list.jsp --> <%@ page contenttype="text/html;charset=euc-kr" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="/web-inf/imagelist.tld" prefix="imglist" %> <html> 160

<f:loadbundle basename="jsf.proj.resource.resources" var="resource" /> <body> <f:view> <h:form id="imglistform"> <imglist:imagelist title="#{resource.imglist_title" width="100" height="100" actionlistener="#{imagelistbean.processclickevent" value="#{imagelistbean.list"/> </h:form> </f:view> </body> </html> 선택된이미지와설명을나타낼페이지이다. <!-- /jsp/component2/image-list-result.jsp --> <%@page contenttype="text/html;charset=euc-kr"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="/web-inf/imagelist.tld" prefix="imglist" %> <html> <body> <f:view> <h:form id="img-list-result-form"> <imglist:imagedisplay value="#{imagelistbean.list" /> </h:form> </f:view> </body> </html> 161

다음과같이이미지들이나타난다. 아무이미지나클릭해보자. 162

* 마이크로소프트의인터넷익스플로러는잘작동하는반면에, 넷스케이프에서는가끔링크 가이상하게나타나기도한다. 자바스크립트의문제인지 JSF 의문제인지아직확실치않다. 이컴포넌트는인터넷익스플로러로테스트해보기바란다. 163