<!-- This should NEVER be present in live --> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> .....
[code] function resultProc(req) { var xmlDoc = req.responseXML; var result = xmlDoc.getElementsByTagName("result")[0].firstChild.nodeValue; console( result ); } [/code]
* 일단 공통적으로 J2SDK1.4와 톰캣 4.1.18 이상 버전을 설치해야 된다. 편의상 J2SDK 가 설치된 디렉토리를 <JAVA_HOME>으로 톰캣 4.1.X가 설치된 디렉토리를 <CATALINA_HOME>으로 표시하겠다.
* 공통 설정 부분은 세 가지 방법 모두 필요한 부분이다. 주로 환경변수 설정이나<CATALINA_HOME>/conf/server.xml 등을 변경해서 톰캣을 재시동해야 변경사항이 적용되는 경우이다.
가장 권장하는 방식은 ant를 이용하는 방식이고, 편한 방식은 war 파일을 이용하는 것이다. 그리고, 가장 까다롭고, 노가다성의 방식이 직접 컴파일하고 배치하는 방법이다. 각 장의 소스는 chap1.zip, chap2.zip 등으로 압축이 되어 있고, 압축을 풀면 필요한 jar파일들, 소스들, build.xml, 안내 파일들이 디렉토리별로 구분되어 있다.
일반적인 디렉토리 구조는 다음과 같다.
[그림 A-1]소스의 일반적인 디렉토리 구조
공통 설치
-. 컨텍스트 추가와 DBCP 설정법
압축을 풀고 conf 디렉토리에 있는 Context.xml 파일을 편집기로 열어서 jdbc 정보를 수정한 다음 전체를 복사한다. 톰캣의 server.xml 파일을 열어서 다음 부분 뒤에 복사한 부분을 붙여넣는다.
src 디렉토리 아래 있는 java 파일들과 properties 파일들 그리고 컴파일 배치 파일(compile.bat)을 모두 컨텍스트 루트의 /WEB-INF/classes 디렉토리 아래로 디렉토리 통채로 복사한다. 다음 예제처럼 디렉토리를 이동한 후에 compile.bat를 실행한다.
커스텀태그를 공부하면서 jsp페이지에서 자바코드를 추출하는데 탁월한 성능을 발휘하는 모습을 보았다. 허나 이것을 만든다고 생각하면, 아직도 좀 아찔한 감이 남아있다. 개발시간은 촉박한데, 로직 부분을 빼낸다고, tld 만들고, 태그핸들러 만들고, 컴파일하고, 잘 동작하는지 테스트해보고 하려면 사실 시간이 더 많이 소요되기 때문에 망설여지는 것이 사실이다.
하지만 커스텀태그 소개하면서 계속 얘기했던 것은 자주 쓰이는 커스텀태그들을 표준으로 정해서 모아놓은 것이 있으니까, 이것들만 잘 찾아서 쓰면 된다고 얘기했다. 정말 알짜들만 모아놓았다. 최정예 커스텀태그들을 모아서 이름을 붙여준 것이 JSP Stadard Tag Library 이고, 줄여서 JSTL 이라고 부른다. JCP(http://www.jcp.org )에서 표준을 제정했다. JSR-52 에서 각계의 전문가들이 심사 후에 알짜들을 모아놓은 것이다.
집필하는 현재 JSTL은 JSP2.0 스펙에 포함되었다. 따라서 JSP2.0 의 표준을 제일 먼저 구현해서 참고를 삼는 톰캣5.0 이 정식으로 발표된다면, 별다른 설치를 하지 않고 바로 JSTL을 사용할 수 있게 된다. (이미 발표된 톰캣5.0 알파버전에는 JSTL의 기능은 빠져 있다. EL 과 심플 커스텀태그와 태그파일의 기능은 들어가 있고, JSTL과 JSF는 빠져있다.)
JSTL 은 EL 을 이용해서 객체의 접근을 쉽게 할 수 있다. jsp 에서 객체의 접근은 먼저 선언부터 되어야 되지만, EL에서는 별다른 선언없이도 객체를 받아서 처리할 수 있게 되었다. 이에 관해서는 후에 예제를 통해서 살펴보겠다.
JSTL은 태생이 커스텀태그이기 때문에 jsp와 밀접하게 관계가 있다. application, session, request, response, pageContext 등의 내장객체에 쉽게 접근하며, 그 외에도 파라메터, 헤더, 쿠키 등을 복잡한 코드를 사용하지않고, 쉽게 직관적으로 사용할 수 있다. 또한 기본적인 연산이나 객체의 비교 등을 .equals() 메소드 등을 이용하는 대신 == 와 같이 쉽게 구현했으며, 조건, 반복, 이동에 대한 태그를 지원하기 때문에 태그만으로도 반복 기능을 구현할 수 있다.
JSTL 의 처리영역은 크게 4가지로 나누어진다. core, format, xml, sql 로 기능이 구분되고, 각각의 기능은 이름이 말해 주듯, 기본기능, 형식화, xml처리, sql처리를 담당한다.
jstl의 기본 설명도 충실하게 잘 나와있고, 웹서비스에 관련된 좋은 안내서이기에 꼭 읽어보기 바란다.
JSTL은 현재 자카르타의 taglibs 서브 프로젝트에서 받아올 수 있다.http://jakarta.apache.org/taglibs에 접속하고, Downloads 링크를 클릭한다. Release 버전과 Nightly Build 버전이 있는데, 정식 릴리스 버전은 현재 1.0.3 까지 나와있다.
jakarta-taglibs 디렉토리 아래 standard 에서 standard-examples.war 파일을 jstl.war 파일로 이름을 바꾼 뒤에 Tomcat 4 가 설치된 디렉토리의 webapps 디렉토리에 복사한다.
server.xml 의 <Host> 에 unpackWARs="true" 로 설정되어 있다면, jstl 이라는 디렉토리가 자동으로 생기면서 jstl.war 의 압축이 풀린 뒤에 브라우저에서 http://localhost:포트/jstl 로 예제 웹 어플리케이션을 실행해 볼 수 있다.
기존의 컨텍스트에서 JSTL을 사용하기 위해서는 웹 어플리케이션의 WEB-INF/lib 디렉토리에 필요한 라이브러리를 복사하면 된다. JSTL 의 주된 라이브러리 파일은 jstl.jar, standard.jar 이고, xml에서 지원되는 기능을 사용하기 위해서 jaxen-full.jar, saxpath.jar,jaxp-api.jar 파일 등이 필요하다. 이 파일들을 웹어플리케이션의 WEB-INF/lib 에 복사하고, 컨텍스트를 리로드한다. 구체적인 파일들의 명세는 다음과 같다.
이름
설명
파일
JSTL 1.0 API 클래스
jstl 1.0 API 클래스
jstl.jar
JSTL 1.0 구현 클래스
JSTL 구현
standard.jar
Jaxen 1.0
Xpath 엔진
jaxen-full.jar
Saxpath 1.0
Xpath 파싱에 필요 sax 방식
saxpath.jar
Jdbc 2.0 선택 패키지
JDBC 구현 클래스 j2se1.4, 톰캣4 기본포함
jdbc2_0-stdext.jar
Jaxp 1.2 구현
jaxp 1.2 호환 파서 필요
jaxp-api.jar dom.jar sax.jar xercesImpl.jar
Xalan
아파치 xslt 변환기
xalan.jar
만일 톰캣4의 ROOT 컨텍스트에 설치를 한 경우에는 webapps/ROOT/WEB-INF/ 아래에 lib 디렉토리를 만들고 jstl 의 필요한 JAR 파일들을 복사해 놓으면 된다.
tld파일이 없거나, web.xml 파일에 8개의 tld 파일을 등록하지 않아도 표준태그는 사용할 수 있도록 되어 있다.
4가지의 JSTL 태그마다 EL 기반과 RT 기반의 태그로 나눠진다. 태그핸들러의 종류는 똑같지만 차이가 있다면 value 값으로 EL 을 사용하느냐 아니면 스크립트의 표현식을 허용하느냐의 차이다. 이에 관해서는 후에 살펴보겠다.
서버에 따라서 필요한 경우가 있으므로 간략히 방법을 설명하면, jakarta-taglibs 의 standard 디렉토리에 있는 tld 디렉토리의 8개의 tld 파일을 웹 어플리케이션의 WEB-INF/ 아래에 적당한 위치에 복사한 뒤에 이 파일들의 위치를 web.xml 에 등록한다.
<c:forEach/> 를 통해서 pageScope 의 값을 모두 출력해보면, String 배열변수인 abc 는 접근할 수 없게 되어있다. <c:set/> 과 <c_rt:set/> 으로 정해놓은 t와 color 만 나타나는 것이 보인다.
color 의 값을 출력하는 코드를 보면 color 의 인덱스를 이용해 배열 내용을 뽑아 왔다. varStatus 대신 var 변수를 통해서 바로 내용을 출력할 수도 있다.
value 가 들어가는 자리에 표현식을 쓰기 위해서는 RT 기반의 태그를 사용해야 된다. 만일 <c_rt:set var="color" value="<%=abc%>"/> 에서 <c_rt:set/> 대신 <c:set/> 를 사용하면 color 가 갖게 되는 값은 "<%=abc%>" 문자열이 되어버린다.
value 속성에 들어가는 값은 문자열과 EL 이다. EL의 경우 ${} 안에서 나온 결과값을 표시한다. 부등호 > 과 gt 는 같은 뜻이다. "${" 를 표시하기 위해서는 EL 로 감싸야 된다. 즉 ${'${'} 로 해야 표시된다.
escapeXml 속성은 브라우저에서 특수한 기능을 하는 문자 표시 여부를 결정한다. false 값일 경우는 태그가 먹힌 굵은 글씨의 bold 가 나오고, true 일 경우는 '<'과 '>'를 각각 '<','>' 로 변환한다. 결과는 <b>bold</b> 로, 브라우저에서 태그가 보이도록 나온다.
큰따옴표와 작은따옴표는 바꿔서 쓸 수 있다. 대신 짝이 맞아야 된다. 또한 하나로 다 이어서 쓸 경우 변환과정에서 에러가 나기 때문에 주의해야된다.
따옴표 사용 예
외부
문자열내부
사용
<c:out value='<font color="blue">파랑</font>' />
작은
큰
가능
<c:out value="<font color='blue'>파랑</font>" />
큰
작은
가능
<c:out value="<font color="blue">파랑</font>" />
큰
큰
불가
session 스코프에 name 이라는 key 로 "하늘"을 넣는다. <c:out value="${name}"/> 으로 scope 를 지정하지 않아도 내장 객체를 훑어서 sessionScope에서 걸리는 "name"키를 찾아서 출력한다. 이 값은 스크립틀릿에서도 참고할 수 있다.
page 스코프에 같은 key 에 "hello" 라는 값을 넣으면 <c:out value="${name}"/> 은 더 이상 session 에 있는 값을 가져오지 않는다.
<c:remove/> 를 통해서 scope 속성에 지정된 key 값을 제거한다.
예제에서 <c:catch/> 태그는 body 실행 도중에 <%=1/0 %> 에서 예외가 발생한 것을 errmsg 라는 변수에 넣는다. 이 후에 <c:out/> 을 통해서 에러 메시지를 표시한다.
<c:forEach/> 태그는 여러가지로 활용이 가능하다. 원하는 구간만큼 반복할 수도 있고, 객체를 받아와서 그 객체의 길이만큼 반복할 수도 있다. begin , end 속성은 시작번호와 끝번호를 지정하고, step 속성을 이용해서 증가 구간을 정할 수 있다. var 속성에서 정한 변수로 반복되는 내부 구간에서 사용할 수 있다.
<c:forTokens/> 는 java.util.StringTokenizer 를 이용한 것이다. 형식은 다음과 같다.
이제 소개할 <c:import/> 는 아주 강력한 도구이다. 웹 어플리케이션 내부의 자원 접근은 물론이고, http, ftp 같은 외부에 있는 자원도 가져와서 페이지 내에 귀속시킨다. 자유롭게 가공할 수도 있고, 편집도 가능하다. <c:import/> 의 형식은 다음과 같다.
<c:url/> 태그는 컨텍스트를 자동으로 추가해서 주소를 자동으로 생성해준다. context 속성이 지정되었을 경우 value 와 context 의 값은 / 로 시작을 해야된다. context 속성이 생략되면 당연히 현재의 컨텍스트가 적용된다. <c:url/> 의 형식은 다음과 같다.
페이지 인코딩이 적용된 경우 request 에서 가져오는 parameter 와 맞지 않는 경우에 사용한다. 서버마다 차이가 있기 때문에, 테스트를 통해서 자신의 환경에 맞게 사용해야 된다. 톰캣4.1.19 의 경우 인코딩을 적용하지 않고도, 해결되는데 다음과 같은 형태로 사용할 수 있다.
properties 확장자를 사용하는 자원 파일을 읽어오는 역할을 하는 <fmt:bundle/> 에 대해서 알아보자. 형식은 다음과 같다.
Syntax
<fmt:bundle basename="basename"
[prefix="prefix"]>
body content
</fmt:bundle>
basename 속성에 지정된 properties 파일을 찾아서 locale 에 따라 읽어들인다.
properties 파일은 보통 WEB-INF/classes 아래에 위치하며 디렉토리의 깊이에 따라서 패키지형식의 이름을 취한다. TestBundle.properties 파일이 com/itexpert/chap9/msg 디렉토리에 있다면 basename="com.itexpert.chap9.msg.TestBundle" 이라고 지정하면 된다.
locale 이 ko 라면 TestBundle_ko.properties 파일을 읽어오게 되며, locale 이 맞지 않는 경우에는 TestBundle.properties 처럼 코드가 붙지 않은 파일을 읽어온다.
prefix 속성은 key 명칭이 공통적인 부분을 지정해서 body 에서 표현되는 key 를 단축시킨다. import 에서 패키지명을 지정하면 클래스명만 쓸 수 있는 것과 같이 생각할 수 있다.
properties 파일의 경우 j2sdk의 /bin/native2ascii.exe 를 이용해서 유니코드로 변환될 필요가 있으나, 파일 수가 많을 경우 ant 의 <native2ascii> 태스크를 이용해서 쉽게 처리할 수 있다. chap9.zip 소스에 포함된 build.xml 파일에 보면 다음과 같은 태스크가 지정되어 있다.
페이지 전체에서 사용할 수 있는 번들을 지정할 수 있는데, 이에 대한 지정은 <fmt:setBundle/> 태그가 담당한다. var 속성에서 정한 변수를 이후에 나오는 <fmt:message/> 태그에서 basename 속성에 변수명으로 대체할 수 있다. <fmt:setBundle/> 의 형식은 다음과 같다.
dataSource="jdbc/myora81" 의 JNDI는 7장에서 설명한 것처럼 컨텍스트에 설정된 DataSource 명이다.
SQL문은 보기 좋게 정렬을 해도 문자열로 덧붙일 필요가 없다. body 에 있는 sql문을 실행한 결과는 emp 라는 변수에 담겨서 이후에 사용이 된다. 이 변수는 ResultSet 과 같은데, JSTL에서 확장한 ResultSet 이고, javax.servlet.jsp.jstl.sql public interface Result 클래스로 내부적으로 정의된다. 지원하는 메소드는 다음과 같다.
javax.servlet.jsp.jstl.sql
public interface Result
public java.util.SortedMap[] getRows()
public Object[][] getRowsByIndex()
public String[] getColumnNames()
public int getRowCount()
public boolean isLimitedByMaxRows()
위와 같은 메소드들이 내부적으로 정의되어있기 때문에 각각의 getter 메소드들을 사용해서 변수 emp를 활용할 수 있다.
<c:forEach/> 의 items 에 있는 ${emp.columnNames}는 getColumnNames() 메소드를 불렀다는 것을 알 수 있다. 이전의 7장에서 ResultSetMetaData 를 활용해서 뽑아낸 정보와 같은 효과를 볼 수 있다.
테이블 내용을 반환하는 ${emp.rowsByIndex} 도 유념할 만하다. 한 행을 row라는 변수에 넣은 뒤에 다음의 <c:forEach/> 에서 이 row변수의 컬럼별로 내용을 출력한다.
column 이라는 변수에 넣은 뒤에 column의 index가 3일 경우 날짜형식을 출력하기 위해서 <fmt:formatDate/> 태그를 사용했고, 그 외의 경우는 바로 출력하게 했다.
다음 <c:forEach/> 에서는 결과를 SortedMap 배열에 넣은 뒤에 한 줄씩 빼서 컬럼이름으로 빼내는 방식이다.
xml 태그를 사용하기 위해서는 XPath 를 먼저 이해할 필요가 있다. xml 소스 트리의 정확한 위치를 지정해주기 위한 경로지정 문법이며 XSLT와 XPointer 를 위해서 만들어진 것이다. xml 엘리먼트들을 노드(node) 로 접근한다. 파일 시스템과 유사하며 다음과 같은 특성이 있다.
l/ 로 시작하면 절대경로처럼 root node 에서 시작된다.
l//로 시작할 경우는 모든 영역에서 해당 엘리먼트를 선택하게 된다.
l표시는 이전 엘리먼트 아래의 모든 자식 엘리먼트를 나타낸다.
l동일한 엘리먼트들이 있을 경우, [] 안에 포함된 숫자는 엘리먼트의 순번이다. 조건식이 올 경우 해당하는 것이 선택된다. last() 일 경우는 맨 마지막 엘리먼트를 표시한다.
l속성은 @ 로 시작된다.
lnormalize-space() 함수는 앞뒤 공백을 제거하는 trim() 역할을 한다.
이 장에 나오는 XPath의 기능은 이 정도로 소개하겠고, XPath에 관한 보다 자세한 내용은 이 장의 마지막에 소개한 인터넷 튜토리얼을 참고하기 바란다.
JSTL에서 XPath를 통해서 내장객체에 쉽게 접근할 수 있다.
표현
매핑
$foo
pageContext.findAttribute("foo")
$param:foo
request.getParameter("foo")
$header:foo
request.getHeader("foo")
$cookie:foo
maps to the cookie's value for name foo
$initParam:foo
application.getInitParameter("foo")
$pageScope:foo
pageContext.getAttribute(
"foo", PageContext.PAGE_SCOPE)
$requestScope:foo
pageContext.getAttribute(
"foo", PageContext.REQUEST_SCOPE)
$sessionScope:foo
pageContext.getAttribute(
"foo", PageContext.SESSION_SCOPE)
$applicationScope:foo
pageContext.getAttribute(
"foo", PageContext.APPLICATION_SCOPE)
예를 들어서 다음 문장은 parameter 로 받은 "name"의 값이 bar엘리먼트의 x속성의 값과 같은 것들을 선택하게 된다.
scopeName 은 {page|request|session|application} 중에 하나
var 속성에 지정된 결과와 result 속성에 지정된 결과의 차이점은 var 속성은 scope 지정해서 다른 곳에서도 사용할 수 있고, result 는 현재 페이지에서만 사용할 수 있다는 것이다.
또한 타입도 차이가 나는데, var는 org.w3c.dom.Document 로, result는 javax.xml.transform.Result 객체로 저장이 된다.
xml의 파라메터를 지정하는 <x:param/> 태그의 형식은 다음과 같다.
Syntax
Syntax 1: value 속성에 파라메터 값이 지정된 경우
<x:param name="name" value="value"/>
Syntax 2: body 내용에 파라메터 값이 지정된 경우
<x:param name="name">
parameter value
</x:param>
여기서 잠깐
<x:transform/> 태그를 사용할 때 jdk1.4 내에 있는 xalan과 jstl이 충돌을 일으킨다.
이런 경우, 톰캣 실행을 중지하고, jstl의 WEB-INF/lib디렉토리에 있는 xalan.jar 파일과 xercesImpl.jar파일 두 개를 <CATALINA_HOME>/common/endorsed디렉토리에 복사한다. 이전 버전의 xercesImpl.jar 파일을 덮어씌운다. 만일 이 과정이 생략되면, 다음과 같은 예외를 만나게 된다.
org.apache.xml.utils.WrappedRuntimeException:
The output format must have a '{http://xml.apache.org/xslt}content-handler' property!