구글링을 통해서 GPS의 좌표를 통한 거리 구하는 공식을 어렵게 찾아 적용했다.

내용은 아래와 같다.


if(oldCoordinate != null) {

// TODO 거리 누적...

double theta = oldCoordinate.getLongitude() - Double.parseDouble(lp.getLongitude());

double dist = Math.sin(Utility.deg2rad(oldCoordinate.getLatitude())) * Math.sin(Utility.deg2rad(Double.parseDouble(lp.getLatitude()))) +

Math.cos(Utility.deg2rad(oldCoordinate.getLatitude())) * Math.cos(Utility.deg2rad(Double.parseDouble(lp.getLatitude()))) * Math.cos(Utility.deg2rad(theta));

if(dist < -1 || dist > 1) {

logger.d("dist : %s", dist);

}

dist = Math.acos(dist);

dist = Utility.rad2deg(dist);

dist = dist * 60 * 1.1515; // statute miles. 단위는 기본 마일.

dist = dist * 1.609344;

if(dist != Double.NaN) {

totalDist += dist*1000;

}

} else {

oldCoordinate = new Coordinate();

}

if(!"".equals(lp.getAltitude())) oldCoordinate.setAltitude(Double.parseDouble(lp.getAltitude()));

if(!"".equals(lp.getLatitude())) oldCoordinate.setLatitude(Double.parseDouble(lp.getLatitude()));

if(!"".equals(lp.getLongitude())) oldCoordinate.setLongitude(Double.parseDouble(lp.getLongitude()));


하지만 dist 값이 -1보다 작거나 1보다 큰 값이 반환되는 경우 Math.acos에 잘못된 파라미터로 들어가게되고, acos는 NaN을 반환하게 된다.

팀 동료와 고민하면서 구글링하게 된 결과 나온 것은 아래와 같다.


float[] distance = new float[3];

if(oldCoordinate != null) {

Location.distanceBetween(oldCoordinate.getLatitude(), oldCoordinate.longitude,

Double.parseDouble(lp.getLatitude()), Double.parseDouble(lp.getLongitude()), distance);

totalDist += distance[0];

} else {

oldCoordinate = new Coordinate();

}

if(!"".equals(lp.getAltitude())) oldCoordinate.setAltitude(Double.parseDouble(lp.getAltitude()));

if(!"".equals(lp.getLatitude())) oldCoordinate.setLatitude(Double.parseDouble(lp.getLatitude()));

if(!"".equals(lp.getLongitude())) oldCoordinate.setLongitude(Double.parseDouble(lp.getLongitude()));


그냥 Location 안에 static으로 선언 된 distanceBetween 메소드를 이용하면 된다.

알기까지 무지막지한 삽질...

알고나면 허무한 ㅠㅠ


WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

eclipse 또는 adt를 통해 android를 개발하다 보면 에뮬레이터가 늦게 떠서 개발에 어려운 경우가 있다.

이럴 경우 다음과 같은 방법으로 에뮬레이터의 속도를 빠르게 할 수 있다.


1. Window > Android SDK Manager를 연다.

2. SDK Manager에서 다음의 package를 설치 한다.



자신의 버전에 맞는 SDK에서 Intel x86 칩셋 이미지를 다운 받는다. 이미 설치 되어 있을 경우에는 설치할 필요가 없다.

가장 하단의 Extras에 있는 Intel x86 Emulator Accelerator (HAXM)도 다운 받는다.

자신의 컴퓨터에 따라 칩셋 이름이 달라질 수 있다.(아마도)


3. Window > Android Virtual Device Manager을 연다.

4. 이미 만들어진 AVD가 있다면 Edit를 누르고, 없다면 New를 눌러 AVD를 설정한다.



위에서 보는 것처럼 CPU/ABI를 Intel Atom(x86)으로 설정한다. 물론 자신의 CPU 칩셋에 따라 옵션이 다를 수 있다. 기본은 ARM으로 설정되어 있다.


이렇게 할 경우 에뮬레이터의 실행 속도가 빨라지게 된다.

다만 내 경우에는 위와 같이 할 경우 아래와 같은 에러 메시지가 떨어졌다.


[2013-05-16 18:08:07 - Emulator] HAX is not working and emulator runs in emulation mode


이와 관련해서 구글링 하니 다음과 같은 내용을 찾을 수 있었다.


http://blog.83rpm.com/archives/1045


HAX를 사용하기 위해 intel에서 프로그램을 다운받아 설치해야 한다는 것이다.

하지만 그래도 에러 발생 ^^; 똑같은 에러 메시지를 검색하니 다음과 같은 내용이 보여진다.


http://answer.beautifultablet.com/android-sdk/hax-is-not-working-and-emulator-runs-in-emulation-mode/


다른 내용은 자세히 보지 않아 모르겠고 "If your computer does not support Intel Virtualization Technology then it will come out this error." 라는 부분을 보면, Intel 가상화 기술을 지원하지 않는다 라는 메시지가 보인다.

즉 지원하지 않는 CPU? 인 것으로 보인다.

이럴 경우에는 그냥 ARM으로 하라는 친절한(?) 설명까지 ^^;;

결국 HAX를 이용한 에뮬레이터 가속은 현재 PC에서는 힘들어 보여서, 기본 셋팅으로 진행할 수 밖에 없었다.





WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 아.. HAX적용하면 정말정말 빠른데요 ㅜ
secret
Site-mesh의 decorators.xml 파일에서 excludes를 정의해서 사용하려 했지만
여전희 decorator 파일이 적용 되는 상황이 발생했다.
여기 저기 구글링 해 봤지만 내 검색 능력의 한계인가 잘 찾질 못했다.
결국 사내 아는 분에게 문의해서 원인을 알게 되었다.


sitemesh.xml decorators.xml


위의 decorators.xml에서 uninstall 부분만 decorator를 적용하지 않으려 했지만 처음에는 적용 되지 않았고,
이에 sitemesh.xml의 아래 부분이 추가 되면서 문제가 해결 되었다.

<property name="decorators" value="/WEB-INF/decorators.xml" />
<excludes file="${decorators}" />

삽질은 정말 해도 해도 끝이 없는거 같다.

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
실 서비스 도중에 대량의 작업을 동기식으로 작업하려면 부담이 되는 경우가 많다.
이럴 경우에는 비동기식으로 작업을 하게 되는데, 보통 Queue에 작업해야 할 목록을 집어 넣어두고, 나중에 Cosumer가 해당 작업을 하도록 만드는 것이 일반적이다.
이런 작업을 하는데 유용한 도구가 ActiveMQ이며 이번 주소록 프로젝트를 하면서 ActiveMQ를 사용하게 되어 정리차원에서 블로그에 올려본다.

설치하기 이전에 우선 환경을 셋팅해야 한다.

JDK는 1.5.x 버젼 이상 설치되어 있어야 하며, " JAVA_HOME" 환경 변수는 JDK가 설치된 경로로 잡혀 있어야 한다. Maven은 1.0.2 이상 설치되어 있어야 한다.(Maven은 소스 설치 또는 개발자 버젼 설치를 위해 필요한 듯...)
또한 관련 JAR 파일들은 미리 classpath에 있어야 한다.
따라서 Maven을 사용한다면 dependencies 아래 아래와 같이 추가하면 된다.



<!-- For Active MQ -->
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-core</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-web</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.xbean</groupId>
    <artifactId>xbean-spring</artifactId>
    <version>3.3</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>2.5.6.SEC01</version>
</dependency>

 환경 셋팅을 다했다면 ActiveMQ를 다운로드 해서 설치해야 한다. 다운로드는 http://activemq.apache.org/download.html 에서 다운 받을 수 있으며, 현재 기준으로 안정 버젼은 5.3.0이다.

설치는 다운로드 받은 파일을 적당한 곳에 풀어주면 된다.

설치 후 ActiveMQ를 실행 시킨다.

cd [activemq_install_dir]
bin\activemq

위와 같이 하던가 아니면 설치 디렉토리의 activemq.bat 파일을 더블클릭 해도 된다.
Unix(Linux) 환경에서는 아래와 같이 하면 된다.

cd [activemq_install_dir]
bin/activemq

OR

bin/activemq > /tmp/smlog  2>&1 &;
Note: /tmp/smlog may be changed to another file name.

ActiveMQ의 자세한 Start 방법은 http://activemq.apache.org/version-5-getting-started.html#Version5GettingStarted-StartingActiveMQ 를 참고하면 된다.

ActiveMQ를 실행 후 제대로 돌고 있는지 확인하기 위해서는 61616 포트를 확인해 보면 된다.

Windows

netstat -an|find "61616"
  TCP    0.0.0.0:61616          0.0.0.0:0              LISTENING

Unix(Linux)

netstat -an|grep 61616
tcp        0      0 :::61616                    :::*                        LISTEN 

위와 같이 61616 포트를 Listen 하고 있다면 제대로 ActiveMQ가 실행 되고 있는 것이다.
또한 위와 같이 ActiveMQ가 실행되고 있는 것이 확인되면 아래의 url로 접근해서 Queue의 상태를 확인해 볼 수 있다.

http://localhost:8161/admin

물론 해당 서버가 로컬이 아니라면 위의 localhost 도메인을 적절히 수정해야 할 것이다.

기본적으로 ActiveMQ를 위와 같은 방식으로 띄운다면 ActiveMQ가 설치된 디렉토리의 conf 디렉토리 밑에 있는 activemq.xml 파일을 가지고 뜬다. 하지만 원하는 디렉토리 밑에 있는 activemq.xml을 참조하도록 하기 위해서는 아래와 같이 띄우면 된다.

activemq설치디렉토리/bin/activemq xbean:file:원하는소스가 있는 디렉토리

activemq.xml에서는 기본적으로 아래와 같은 셋팅을 하면 된다.


policyEntry 부분에 queue 이름만 정확히 넣어 주고 여기서 셋팅한 이름을 applicationContext.xml 파일에서 사용하면 된다.
jetty 부분은 admin으로 모니터링 할 내용을 적어 둔 것이다.
덧. activemq.base는 상단에서 단순히 properties 파일 선언하고 해당 부분에서 ActiveMQ가 설치된 디렉토리를 지정한 것이다.

* applicationContext.xml 파일의 설정


위와 같이 설정이 모두 마쳐 졌다면 프로그램을 만들 차례이다.
우선 가장 기본이 되는 리스너는 다음과 같은 형식으로 만들어 주면 된다.


위 클래스는 주소를 수정할 경우 registerQueue 메소드를 호출해서 Queue에 한개의 아이디를 쌓고, Listener에서 Queue에 데이터가 들어오면 pullFromQueueAndModifyAddress 메소드를 호출해서 수정한 아이디의 주소를 이용하여 친구들 주소들 모두를 수정해 주는 것이다.

우선 applicationContext.xml에서 선언한 jmsTemplate 관련 setter를 선언해 주고 기본이 되는 2가지 메소드를 만들어 주면 된다.
registerQueue 메소드는 Queue에 등록하는 메소드이며 메소드 등록 시 jmsTemplate를 이용하면 된다. 해당 메소드는 별도의 외부 클래스에서 호출해 주는 것이므로 알아서 테스트 해 보면 될 것이다.
Queue에 데이터가 등록되면 Listener가 알아서 감지하고 pullFromQueueAndModifyAddress 메소드가 실행 된다.(applicationContext.xml의 listener list에 선언한 부분)
안의 비즈니스 로직은 알아서 만들면 되는 것이고 이렇게 되면 http://localhost:8161/admin/queues.jsp 에서 해당 내용이 제대로 적용 되었는지 확인해 볼 수 있다.

보통 Queue에 Data가 쌓이게 되면 바로 바로 Consumer가 작동하게 되므로 서버를 Debug 모드로 돌려 놓고 BreakPoint를 찍어 놓았다면 registerQueue를 실행하는 즉시 디버거가 작동하게 될 것이다.

덧. 백업 용도로 정신 없이 작성하다 보니 내가 써 놓고도 내가 무슨 말이지 모르겠다 ^^ 적당히 알아서 들으시면 될 것 같다. 삽질은 프로그램의 기본 ^^
덧2. ActiveMQ의 설정 및 시작 방법 등 자세한 내용은 http://users.handysoft.co.kr/~wsko/articles/activemq-guide/index.html에 더 잘 나온 것 같다.




WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
매일 프로그램 삽질은 하고 있지만 이번 건은 유난히 더 삽질이 길었다.
3일 동안 아무런 일도 못하고 이 일에만 매달렸다.
다른 일을 하긴 해야 하지만, 개발자 자존심에 해결하지 못하고 넘어가기에도 뭐 하고...
결국에는 해결하게 되었지만 아무것도 아닌 원인 때문에 해결하고도 짜증이 난다.

증상은 다음과 같다.




소스는 간단하다.
그냥 특정 쿠키를 생성한 다음 response에 실어 보내는 것이다.

그런데 로컬과 서버의 결과물이 달랐다.

* 로컬의 결과물
cookieKey1=bla...bla...;
cookieKey2=bla...bla...;

* 서버의 결과물
cookieKey1="bla...bla...";
cookieKey2="bla...bla...";

둘 간의 차이점은 value 앞 뒤로 쌍따옴표(")가 있느냐 없느냐이다.
큰 차이가 아닐지 모르지만 인증 관련된 작업을 다루는데 있어서 해당 값으로 인해 로그인이 되기도 하고 안되기도 하는 부분이라서 매우 골치 아픈 부분이었다.

소스를 이리도 뜯어보고 저리도 뜯어 봤지만 해결은 되지 않았고,
Googling을 아무리 해 봐도 답은 나오지 않았다.(내 능력 부족 -.-;;)

결국 회사 내 다른 분의 도움을 얻어 로컬과 서버의 tomcat 버젼의 차이때문에 생기는 것임을 알게 되었고 서버의 tomcat 버젼을 5.5.27로 downgrade해서 문제를 해결하게 되었다

관련된 내용은 http://tomcat.apache.org/tomcat-5.5-doc/changelog.html 의 46587 에서 확인해 볼 수 있다.


WRITTEN BY
체리필터
프로그램 그리고 인생...

트랙백이 하나이고 , 댓글이 없습니다.
secret
프로젝트를 진행하면서 BTS로 _, %로 검색시 검색이 제대로 안된다는 내용이 등록되었다.
해당 내용을 해결하기 위해 구글링 하던 도중 아래와 같은 내용을 찾게 되었다.

http://okjsp.pe.kr/seq/2372

'_', '%'와 같은 문자들이 검색을 하기 위한 wild 문자로 사용되지 않고 리터럴 문자로 사용되기 위해서는 뒤에 escape '\' 와 같이 사용해 주어야 한다는 것이다.
즉 다음과 같이 사용하면 되는 것이다.

LIKE '%검색어\_\%% escape '\'

사용하고 있는 DB는 CUBRID 였으며, 위와 같이 할 경우 잘 동작 되었다.

위 내용을 수정하면서 SQL Injection 위험이 있던 '%$keyword$%' 부분도 '%#keyworkd#%'로 바꾸게 되었다.
하지만 이 과정 중에 ibatis가 자체적으로 #keyword#를 'keyword'로 바꾸기 때문에 결과에서는 '%'keyword'%'가 되므로 수정해 주어야 했다.
MySQL이라면 CONCAT과 같은 함수를 써서 다음과 같이 하면 된다.

LIKE CONCAT('%', #keyword# '%')

하지만 CUBRID에는 CONCAT 함수가 없다. 관련해서 또다시 검색해 보니 Java에서 String을 이어 붙이기 하는 것과 같은 방법을 사용하면 되었다.
즉 CUBRID에서는 다음과 같이 하면 된다.

LIKE '%' + #keyword# + '%'

오늘도 삽질을 통해 또 하나의 깨달음을 얻게 되었다.

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. ㅋㅋㅋ, 지금 저한테 필요한 내용 ㅋㅋㅋ
secret
일반 프로퍼티 또는 List 타입의 프로퍼티를 출력하는 방법은 이미 살펴 보았다.
프로퍼티가 아닌 객체 자체를 출력하는 경우는 다음과 같이 할 수 있다.

struts.xml

<action name="printObject" class="example.chapter2.PrintObjectAction">
    <result>/chapter2/printObject.jsp</result>
</action>

example.model.Product.java

package example.model;

public class Product {
    private String name;
    private String modelNo;
   
    public Product() {}
    public Product(String name, String modelNo) {
        this.name = name;
        this.modelNo = modelNo;
    }
   
    public String getName() {
        return name;
    }
   
    public void setName(String name) {
        this.name = name;
    }
   
    public String getModelNo() {
        return modelNo;
    }
   
    public void setModelNo(String modelNo) {
        this.modelNo = modelNo;
    }
}

example.chapter2.PrintObjectAction.java

package example.chapter2;

import example.model.Product;

public class PrintObjectAction {
    private Product product;
   
    public String execute() throws Exception {
        product = new Product("MP3 플레이어", "MP3-070701");
        return "success";
    }

    public Product getProduct() {
        return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }
}

/chapter2/printObject.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<s:label>제품 이름 : </s:label>
<s:property value="product.name"/>
<br>
<s:label>제품 모델번호 : </s:label>
<s:property value="product.modelNo"/>

</body>
</html>

Product라는 모델에 빈 프로퍼티를 만들고, PrintObjectAction.java 파일의 execute 메소드에서 해당 프로퍼티에 값을 셋팅한다.
셋팅 된 값을 printObject.jsp에서 <s:property> 태그에서 product.name, product.modelNo로 불러와 사용한다.
해당 값은 OGNL 형식으로 getter, setter 형식의 빈 메소드를 사용하여 값을 불러 온다.
label 태그는 html 형식의 label 태그를 생성해 주는 역할을 한다.

객체를 List형태로 보여주는 경우라면 아래와 같이 사용할 수 있다.

<action name="printObjectList" class="example.chapter2.PrintObjectListAction">
   <result>/chapter2/printObjectList.jsp</result>
</action>

example.chapter2.PrintObjectListAction.java

package example.chapter2;

import java.util.ArrayList;
import java.util.List;

import example.model.Product;

public class PrintObjectListAction {
    private List<Product> listProduct;
   
    public String execute() throws Exception {
        listProduct = new ArrayList<Product>();
        listProduct.add(new Product("MP3 플레이어", "MP3-070701"));
        listProduct.add(new Product("노트북 PC", "NB-070613"));
        listProduct.add(new Product("PDA", "PDA-070507"));
        listProduct.add(new Product("휴대폰", "MOB-070615"));
       
        return "success";
    }

    public List<Product> getListProduct() {
        return listProduct;
    }

    public void setListProduct(List<Product> listProduct) {
        this.listProduct = listProduct;
    }
   
}

/chapter2/printObjectList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<p>제품목록 : </p>
<table border="1" width="400">
<tr align="center" style="color:white;background-color:black;">
    <td>제품이름</td>
    <td>제품 모델번호</td>
</tr>

<s:iterator value="listProduct" status="status">
<tr bgcolor="<s:if test="#status.odd">lightgrey</s:if>">
    <td><s:property value="name"/></td>
    <td><s:property value="modelNo"/></td>
</tr>
</s:iterator>


</table>
</body>
</html>

action 파일의 execute 메소드에서 List 타입의 listProduct 프로퍼티에 값을 설정한다.
설정된 값을 printObjectList.jsp에서 불러와서 사용한다.
앞에서 product.name, product.modelNo 와 같이 사용했지만, iterator 안에서는 name, modelNo 와 같은 식으로 사용한다.
status는 Iteration 상태 값을 가리키는 값이다.
if 태그를 통해서 해당 값이 홀수인지 체크(#status.odd)한다.
status에서 지원하는 다른 속성은 다음과 같다.

first : 처음 행
last : 마지막 행
index : 행의 인덱스

추가로 날짜 출력과 관련된 예제 하나를 더 살펴보면 아래와 같다.

struts.xml

<action name="printDate" class="example.chapter2.PrintDateAction">
   <result>/chapter2/printDate.jsp</result>
</action>

PrintDateAction.java

package example.chapter2;

import java.util.Date;

public class PrintDateAction {
    private Date currDate;
   
    public String execute() {
        currDate = new Date();
       
        return "success";
    }

    public Date getCurrDate() {
        return currDate;
    }

    public void setCurrDate(Date currDate) {
        this.currDate = currDate;
    }
}

printDate.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<ul>
    <li><s:property value="currDate"/></li>
    <li><s:date name="currDate"/></li>
    <li><s:date name="currDate" format="yyyy-MM-dd HH:mm:ss"/></li>
</ul>
</body>
</html>

sdate 태그에서 format 을 설정하지 않으면 "2009. 5. 11 오후 3:06:36"와 같이 출력되며, format을 주게 되면 format 형식으로 출력된다.





'Java > Struts2' 카테고리의 다른 글

스트럿츠를 사용하여 객체 출력하기  (0) 2009.05.11
스트럿츠를 사용하여 List 출력하기  (1) 2009.05.06
스트럿츠2 설정하기  (2) 2009.04.28

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
이번에는 스트럿츠를 사용하여 List를 출력해 보자. 일단 Action 파일과 result 파일 정의를 struts.xml에 정의하면 다음과 같다.

<action name="printStringList" class="example.chapter2.PrintStringListAction">
   <result>/chapter2/printStringList.jsp</result>
</action>

위에서 정의한 대로 PrintStringListAction.java 파일을 example.chapter2 패키지에 만든다.

package example.chapter2;

import java.util.ArrayList;
import java.util.List;

public class PrintStringListAction {
    private List<String> listString;
   
    public String execute() throws Exception {
        listString = new ArrayList<String>();
        listString.add("MP3 플레이어");
        listString.add("노트북 PC");
        listString.add("PDA");
        listString.add("휴대폰");
       
        return "success";
    }
   
    public List<String> getListString() { return listString; }
    public void setListString(List<String> listString) {
        this.listString = listString;
    }
}


listString이란 List 타입 변수에 값들을 담고, 나중에 result page(printStringList.jsp)에서 사용할 getListString 메소드도 만들어 준다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:iterator value="listString">
    <li><s:property/></li>
</s:iterator>
</body>
</html>

struts에서 사용하는 taglib를 선언 후 iterator를 통해 listString에 들어간 원소들을 하나씩 보여주고 있다.
위 소스의 결과는 아래와 같다.






'Java > Struts2' 카테고리의 다른 글

스트럿츠를 사용하여 객체 출력하기  (0) 2009.05.11
스트럿츠를 사용하여 List 출력하기  (1) 2009.05.06
스트럿츠2 설정하기  (2) 2009.04.28

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 여기까지 따라해봤는데..
    printString, printStringList 예제 둘다 실행하면 HTML 결과물에 아무것도 나타나지 않습니다..
    이럴때 생각할 수 있는 문제들로 무엇이 있을까요..? 답변 부탁드립니다.
secret
스트럿츠를 사용하기 위해 스트럿츠 환경을 구축할 필요가 있다.
우선은 이클립스에서 다음과 같이 'Dynamic Web Project'를 하나 생성한다.


그런 다음 위에서 볼 수 있는 것처럼 몇가지 내용을 추가해야 한다.
우선은 필요한 라이브러리를 추가해 보자.
http://struts.apache.org/에서 해당 버전을 다운받는다. 현재 시점에서 가장 최신 버젼은 2.1.6이다.(http://struts.apache.org/download.cgi#struts216)
다운 받은 파일의 압축을 풀면 lib 디렉토리에 jar 파일들이 있는데, 그 중에서 필요한 jar 파일들을 추가한다.
추가해야 할 파일 목록은 다음과 같다.

antlr-2.7.2.jar
commons-beanutils-1.7.0.jar
commons-chain-1.2.jar
commons-fileupload-1.2.1.jar
commons-logging-1.0.4.jar
commons-logging-api-1.1.jar
commons-validator-1.3.1.jar
freemarker-2.3.13.jar
jstl.jar
ognl-2.6.11.jar
oro-2.0.8.jar
standard.jar
struts-core-1.3.10.jar
struts2-core-2.1.6.jar
xwork-2.1.2.jar

jstl.jar 파일과 standard.jar 파일은 JSTL을 사용하기 위해 이미 앞에서 추가한 파일이며, 그외 다른 파일들도 추가한다.
에이콘에서 나온 '스트럿츠2 프로그래밍' 책을 기초로 셋팅을 따라 한 것인데 해당 책에는 특히 commons-fileupload-1.2.1.jar 라이브러리에 대한 업급이 없다.
commons-fileupload-1.2.1.jar 파일 없이 server를 구동 시킬 경우 에러가 발생 했었다.

라이브러리 추가 후에는 설정 파일을 수정해야 한다.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>struts2</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
 
  <filter>
      <filter-name>struts</filter-name>
      <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>struts</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>
 
</web-app>

struts.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <package name="default" extends="struts-default" namespace="">
    </package>
</struts>

struts.properties

struts.i18n.reload=true
struts.devMode=false
struts.configuration.xml.reload=true
struts.continuations.package=org.apache.struts2.showcase
struts.custon.i18n.resources=globalMessages
#struts.action.extension=jspa
struts.url.http.port=8080
#struts.freemarker.manager.classname=customFreemarkerManager
struts.serve.static=true
struts.serve.static.browserCache=false
struts.multipart.maxSize=2097252

web.xml은 WebContent/WEB-INF/ 아래에 만들면 되며, struts.xml, struts.properties 파일은 src 밑에 둔다.(이클립스에서 자동으로 classes 밑으로 배포해 준다.)
위와 같이 설정 한 후 이클립스에서 서버를 추가해 준다.

Tomcat 6.0으로 선택

Tomcat 6.0으로 선택



추가 된 서버를 더블 클릭하면 아래와 같은 창이 뜬다.

Server name을 struts2로 바꾼다.(이름은 아무렇게나 해도 된다.)

Server name을 struts2로 바꾼다.(이름은 아무렇게나 해도 된다.)


아래 Modules 탭으로 이동 후 아래 그림과 같이 나오면 다음 내용을 진행한다.

Add External Web Module... 클릭

Add External Web Module... 클릭



Browser를 눌러 Document Root를 지정해 준다

Browser를 눌러 Document Root를 지정해 준다



Document Root는 프로젝트 생성할 때 자동으로 생성된 Directory 중 WebContent 디렉토리를 지정해 준다.
이와 같이 지정 후 저장한 다음 서버 에디팅 창을 닫는다.
그런 다음 생성된 struts2 서버를 start 한다.
이상 없이 start 가 된다면 다음과 같이 간단한 struts예제를 만들어 시험해 본다.

struts.xml

<struts>
    <package name="default" extends="struts-default" namespace="">
        <action name="printString" class="example.chapter2.PrintStringAction">
            <result>/chapter2/printString.jsp</result>
        </action>
    </package>
</struts>

package example.chapter2;

public class PrintStringAction {
    private String greetings;
   
    public String execute() throws Exception {
        greetings = "반갑다! 스트럿츠2.";
        return "success";
    }
   
    public String getGreetings() { return greetings; }
    public void setGreetings(String greetings) {
        this.greetings = greetings;
    }
}


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <s:property value="greetings" />
</body>
</html>

struts.xml에서 정의한 것처럼, example.chapter2 패키지 않에 PrintStringAction.java 파일을 만들고, jsp 파일은 /WebContent/chapter2/printString.jsp로 만든다.
jsp에서는 "<%@ taglib prefix="s" uri="/struts-tags" %>"와 같이 strus에서 사용할 tag lib를 선언한 다음, PrintStringAction에서 정의한 greetings 변수를 "<s:property value="greetings" />"와 같이 출력해 준다.
<s:property/>에서 사용한 value부분은 Action 파일에서 정의한 getter를 사용해서 값을 가져 온다.(OGNL 사용)
만일 getter가 없다면 오류가 나지는 않으며, 단지 화면에 내용이 출력되지 않는다.

'Java > Struts2' 카테고리의 다른 글

스트럿츠를 사용하여 객체 출력하기  (0) 2009.05.11
스트럿츠를 사용하여 List 출력하기  (1) 2009.05.06
스트럿츠2 설정하기  (2) 2009.04.28

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글  2개가 달렸습니다.
  1. 좋은 정보 감사합니다.. 퍼갈께요 ^_^
  2. 감사합니다 !!! 퍼가겠습니다~
secret
필터를 통해 서블릿이 실행되기 전에 특정 액션을 하게 만들 수 있다고 했다.
그런데 서블릿이 실행되고 나서 특정 액션을 하게 만들려면 어떻게 해야 할까?
간단하게 생각해 보면 필터의 doFilter()메소드 안에 있는 chain.doFilter(request, response) 를 마치고 나서 작업하면 될 것 같다.
하지만, 서블릿에 넘겨주는 response 객체를 서블릿이 사용하게 되면, 필터를 거치지 않고 바로 클라이언트로 response 하게 된다.
따라서 doFilter를 통해 response를 넘길 때 새로운 응답 객체(HttpServletResponse를 구현한 객체)를 만들어 넘기는 방법을 써야 한다.

하지만, HttpServletResponse는 간단한 클래스가 아니므로, 썬에서 구현해 둔 다음 4가지를 이용하면 된다.

ServletRequestWrapper
HttpServletRequestWrapper
ServletResponseWrapper
HttpServletResponseWrapper

위 4가지 클래스를 상속한 후 필요한 메소드만 재정의 해서 사용하면 된다.

response를 할 때 내용을 압축해서 보내주는 예제를 통해서 Response Filter를 어떻게 사용하는지 확인해 볼 수 있다.

package com.example.web;

import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CompressionFilter implements Filter {
    private ServletContext ctx;
    private FilterConfig cfg;
   
    @Override
    public void init(FilterConfig cfg) throws ServletException {
        // TODO Auto-generated method stub
        this.cfg = cfg;
        ctx = cfg.getServletContext();
        ctx.log(cfg.getFilterName() + " initialized.");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain fc) throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
       
        String valid_encodings = request.getHeader("Accept-Encoding");
        if(valid_encodings.indexOf("gzip") > -1) {
            CompressionResponseWrapper wrappedResp
                        = new CompressionResponseWrapper(response);
            wrappedResp.setHeader("Content-Encoding", "gzip");
            ctx.log("before chain...");
            fc.doFilter(request, wrappedResp);
           
            GZIPOutputStream gzos = wrappedResp.getGZIPOutputStream();
            gzos.finish();
           
            ctx.log(cfg.getFilterName() + ": finished the request.");
        } else {
            ctx.log(cfg.getFilterName() + ": no encoding performed.");
        }
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        cfg = null;
        ctx = null;
    }
}


package com.example.web;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class CompressionResponseWrapper extends HttpServletResponseWrapper {
    private GZIPServletOutputStream servletGzipOS = null;
    private PrintWriter pw = null;

    public CompressionResponseWrapper(HttpServletResponse response) {
        super(response);
    }
   
    public void setContentLength(int len) {}
   
    public GZIPOutputStream getGZIPOutputStream() {
        return this.servletGzipOS.internalGzipOS;
    }
   
    private Object streamUsed = null;
   
    public ServletOutputStream getOutputStream() throws IOException {
        if((streamUsed != null) && (streamUsed != pw)) {
            throw new IllegalStateException();
        }
       
        if(servletGzipOS == null) {
            servletGzipOS = new GZIPServletOutputStream(getResponse().getOutputStream());
            streamUsed = servletGzipOS;
        }
        return servletGzipOS;
    }
   
    public PrintWriter getWriter() throws IOException {
        if((streamUsed != null) && (streamUsed != servletGzipOS)) {
            throw new IllegalStateException();
        }
       
        if(pw == null) {
            servletGzipOS = new GZIPServletOutputStream(getResponse().getOutputStream());
            OutputStreamWriter osw = new OutputStreamWriter(servletGzipOS, getResponse().getCharacterEncoding());
           
            pw = new PrintWriter(osw);
            streamUsed = pw;
        }
        return pw;
    }
}


package com.example.web;

import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletOutputStream;

public class GZIPServletOutputStream extends ServletOutputStream {
    GZIPOutputStream internalGzipOS;
   
    GZIPServletOutputStream(ServletOutputStream sos) throws IOException {
        // TODO Auto-generated constructor stub
        this.internalGzipOS = new GZIPOutputStream(sos);
    }
   
    @Override
    public void write(int param) throws IOException {
        // TODO Auto-generated method stub
        internalGzipOS.write(param);
    }

}

압축과 관련된 부분은 다루고자 하는 범위를 넘어서는 듯 하며, Response를 어떻게 변형하여 넘겨주는지를 확인해 보면 된다.
CompressionResponseWrapper wrappedResp = new CompressionResponseWrapper(response); 와 같이 HttpServletResponseWrapper를 확장한 클래스를 통해 만들어진 랩퍼 클래스로부터 변경된 response 객체를 리턴받아 사용한다.
response 객체는 super(response);와 같이 상위 클래스에서 알아서 처리하도록 하며, 나머지 압축 관련 부분만 처리해 준다.


'Java > Servlet & JSP' 카테고리의 다른 글

필터 - RESPONSE  (0) 2009.04.28
필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
모든 서블릿 앞단에서 공통적으로 처리해야 할 내용이 있을 경우 필터를 사용해서 해당 내용을 처리할 수 있다.
필터는 자신만의 고유 API가 있으며, 컨테이너가 이 API를 알고 호출해 준다.
해당 API는 init(), destroy(), doFilter()이다.
그리고 호출해야 할 필터는 DD(web.xml)에서 설정하게 된다.

하나의 예제로 필터를 만들어 보면 다음과 같이 할 수 있다.(Head & First 예제)

BeerRequestFilter.java

package com.example.web;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class BeerRequestFilter implements Filter {
    private FilterConfig fc;
   
    public void init(FilterConfig config) throws ServletException {
        this.fc = config;
    }
   
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpReq = (HttpServletRequest) req;
       
        String name = httpReq.getRemoteUser();
       
        if(name != null) {
            fc.getServletContext().log("User " + name + "is updating");
        }
       
        chain.doFilter(req, resp);
    }
   
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
       
    }
}

모든 필터는 Filter 인터페이스를 구현해야 한다.
또한 doFilter()의 인자로 HttpServletRequest, HttpServletResponse가 아니라 ServletRequest, ServletResponse 가 온다는 점도 주의해야 한다.
그리고 필터는 순차적으로 진행될 수 있는데 chain.doFilter를 통해 다음 필터의 doFilter를 호출할 수 있다.

web.xml

  <servlet>
      <servlet-name>ListenerTester</servlet-name>
      <servlet-class>com.example.ListenerTester</servlet-class>
      <security-role-ref>
          <role-name>Manager</role-name>
          <role-link>Admin</role-link>
      </security-role-ref>
  </servlet>
  <servlet-mapping>
      <servlet-name>ListenerTester</servlet-name>
      <url-pattern>/ListenTest.do</url-pattern>
  </servlet-mapping>

  <filter>
      <filter-name>BeerRequest</filter-name>
      <filter-class>com.example.web.BeerRequestFilter</filter-class>
      <init-param>
          <param-name>LogFileName</param-name>
          <param-value>UserLog.txt</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>BeerRequest</filter-name>
      <url-pattern>*.do</url-pattern>
  </filter-mapping>

위와 같이 web.xml을 설정하게 되면 기존에 설정한 /ListenTest.do url을 호출하게 되면 BeerRequestFilter.java의 doFilter 메소드가 자동으로 호출되게 된다.
물론 호출 시에는 init() 메소드가 먼저 실행 되게 되고, 마지막에는 destroy() 메소드가 실행 되게 된다.

url-pattern 대신에 servlet-name이 대신 와도 된다.
우선 순위는 url-pattern이 먼저이고, 해당 패턴에 맞는 필터가 모두 실행되고 나서 servlet-name에 맞는 내용이 실행된다.

필터가 여러개일 경우에는 DD(web.xml)에 설정한 순서대로 실행 되며, 실행 과정은 stack에 관련 필터들이 쌓였다가 제거 되는 모습을 상상해 보면 된다.

필터를 클라이언트가 Request 했을 경우 뿐 아니라, 다른 경우에도 실행할 수 있다.
그럴 경우에는 다음과 같이 DD(web.xml)에 적어주면 된다.

  <filter>
      <filter-name>BeerRequest</filter-name>
      <filter-class>com.example.web.BeerRequestFilter</filter-class>
      <init-param>
          <param-name>LogFileName</param-name>
          <param-value>UserLog.txt</param-value>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>BeerRequest</filter-name>
      <url-pattern>*.do</url-pattern>
      <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

기본 값은 REQUEST이며, 그 대신에 INCLUDE, FORWARD, ERROR 를 사용할 수 있다.
선언하는 내용 그대로 include 시, forward시, error 시에 작동한다.

'Java > Servlet & JSP' 카테고리의 다른 글

필터 - RESPONSE  (0) 2009.04.28
필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글 하나 달렸습니다.
  1. 궁금한게있는데요 여기서 맨위에BeerRequestFilter.java파일에서
    if(name != null) {
    fc.getServletContext().log("User " + name + "is updating");
    }
    있는데 이걸 어떻게 확인하나요??
secret
보안을 위해서는 인증(Authentication), 인가(Authorization), 데이터 무결성(Data Integrity), 기밀성(Confidentiality)이 보장 되어야 한다.
인증이란 쉽게 말해 로그인을 통해 누구인지 서버(컨테이너)가 알 수 있도록 체크하는 단계이다.
인가는 인증된 사용자의 권한 레벨을 확인할 수 있도록 체크하는 단계이다.
데이터 무결성이란 클라이언트가 서버에 보낸 정보가 변경되지 않도록 하는 것이며, 기밀성이란 중간에 해당 데이터를 엿보지 못하도록 하는 것이다.

인증 및 인가 작업은 php에서도 많이 하는 작업이므로 그 의미에 대해서 별 다른 설명은 필요해 보이지 않는다.
자세한 설정 방법 및 사용 법은 아래 예제들을 통해 알 수 있다.

우선은 서버 자체에서 지원하는 인증 방식을 알아보면 다음과 같다.

tomcat-user.xml

<tomcat-users>
    <role rolename="Admin"/>
    <role rolename="Member"/>
    <role rolename="Guest"/>
    <user name="Annie" password="admin" roles="Admin, Member, Guest" />
    <user name="Diane" password="coder" roles="Member, Guest" />
    <user name="Ted" password="newbie" roles="Guest" />
</tomcat-users>

서버 설치 디렉토리 또는 그 하위 디렉토리 중 conf 디렉토리에 있는 tomcat-user.xml에 유저 정보를 위와 같이 기록한다.

web.xml

  <login-config>
      <auth-method>BASIC</auth-method>
  </login-config>
 
  <security-role>
      <role-name>Admin</role-name>
  </security-role>
  <security-role>
      <role-name>Member</role-name>
  </security-role>
  <security-role>
      <role-name>Guest</role-name>
  </security-role>
  
  <security-constraint>
      <web-resource-collection>
          <web-resource-name>UpdateRecipes</web-resource-name>
          <url-pattern>/Beer/AddRecipe/*</url-pattern>
          <url-pattern>/Beer/ReviewRecipe/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
          <role-name>Admin</role-name>
          <role-name>Member</role-name>
      </auth-constraint>
  </security-constraint>

인증방식(auth-method)은 BASIC으로 하고 컨테이너에 지정한 내용(tomcat-user.xml)과 맵핑하는 정보를 기술(security-role)한다.
security-constraint에서는 특정 디렉토리에 인증을 걸 수 있게 url-pattern을 지정한다. 또한 인증을 허가할 http method도 지정한다.
지정을 안한 메소드들은 인증 없이 접근할 수 있다.
auth-constraint에는 접근할 수 있는 유저의 이름을 지정한다.
위와 같이 한 상태로 서버를 재시작 한 다음 http://localhost:8080/Beer/AddRecipe/ 에 접근하면 아래와 같은 로그인 창이 뜨게 된다.



* <auth-constraint> 규칙

<auth-constraint>은 옵션이다.
<auth-constraint> 항목이 있다는 것은 컨테이너에게 관련 URL에 대해 인증을 실시하라고 명령하는 것이다.'
<auth-constraint>가 없다면 해당 URL에 대해 인증없이 접근할 수 있다.
  - <auth-constraint/>와 같은 형식으로 되어 있다면 모든 사용자가 접근할 수 없다는 것이다.

* <role-name> 규칙

<role-name>은 옵션이다.
<role-name> 항목은 접근 허용목록을 컨테이너에게 알려주는 것이다.
<role-name>이 하나도 없다면, 어떤 사용자도 접근할 수 없다.
<role-name>*</role-name>와 같이 되어 있다면, 모든 사용자가 접근 가능한 것이다.
<role-name>은 대소문자를 구분한다.

동일 자원(url)에 대해서 <security-constraint> 항목이 두개 이상이라면, 더 큰 범위에 설정 된 값을 따른다.

서블릿에서 특정 레벨을 가진 역할명을 임의로 하드 코딩해 뒀다면, 해당 레벨명과 DD(web.xml)에서 정의한 레벨명을 서로 맵핑할 필요가 있다.
즉 다음과 같은 경우가 될 것이다.

if(request.isUserInRole("Manager")) {
    ...
} else {
    ...
}

DD에는 Manager란 롤 이름이 없다.
따라서 위와 같은 경우에는 DD에 다음과 같이 설정해 줄 수 있다.

  <servlet>
      <security-role-ref>
          <role-name>Manager</role-name>
          <role-link>Admin</role-link>
      </security-role-ref>
  </servlet>

  <security-role>
      <role-name>Admin</role-name>
  </security-role>

위와 같이 하게 되면 Sevlet에서 사용하는 Manager란 role은 Admin으로 처리 되게 된다.
물론 실제 Manager란 role이 있다 하더라도 위와 같이 설정하게 되면 Manager는 Admin의 role 역할을 수행하게 된다.

* 인증의 종류

인증의 종류에는 총 4가지가 있다.

1. BASIC

BASIC 방법은 위 예제에서 사용해 보았다.
로그인 정보를 base64 encoding 한 상태로 보내기 때문에, 중간에 '이즈드롭퍼(Eavesdropper)'가 가로챌 경우 쉽게 노출 될 수 있다.

2. DIGEST

암호화 매커니즘을 이용하여 전송하기에 좀더 안전하지만, 많이 사용되지 않는 암호화 기법이라서 J2EE 컨테이너가 반드시 지원 안할 수도 있다.

3. CLIENT-CERT

클라이언트가 인증서를 가지고 공인키 인증방식을 사용하여 로그인 하는 방식이다.
클라이언트가 인증서를 가지고 있어야지만 로그인이 되므로, 비즈니스 환경에서만 사용된다.

4. FORM

일반적인 웹폼(html)을 이용하여 로그인 정보를 서버에 전송하는 방식이다.
암호화 되지 않은 로그인 정보를 그대로 전송하므로, '이즈드롭퍼(Eavesdropper)'가 가로챌 경우 쉽게 노출 될 수 있다.

위 4가지 방식을 구현하기 위해서는 위의 예제 가운데 있었던 <login-config> 부분을 아래와 같이 수정하면 된다.

  <login-config>
      <auth-method>BASIC</auth-method>
  </login-config>

  <login-config>
      <auth-method>DIGEST</auth-method>
  </login-config>

  <login-config>
      <auth-method>CLIENT-CERT</auth-method>
  </login-config>

  <login-config>
      <auth-method>FORM</auth-method>
      <form-login-config>
          <form-login-page>/loginPage.html</form-login-page>
          <form-error-page>/loginError.html</form-error-page>
      </form-login-config>
  </login-config>

우리가 일반적으로 보는 FORM 방식의 로그인 인증 방식은 base64 인코딩도 안된 상태로 로그인 정보가 전송 되기 때문에, 보안에 가장 취약하다.
하지만 https를 통해서 로그인 정보를 전송하게 된다면 안전하게 정보를 전달할 수 있다.
그렇게 하기 위해서 DD에 다음과 같이 설정할 수 있다.

  <security-constraint>
      <web-resource-collection>
          <web-resource-name>UpdateRecipes</web-resource-name>
          <url-pattern>/Beer/AddRecipe/*</url-pattern>
          <url-pattern>/Beer/ReviewRecipe/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
          <role-name>Admin</role-name>
          <role-name>Member</role-name>
      </auth-constraint>
      <user-data-constraint>
          <transport-guarantee>CONFIDENTIAL</transport-guarantee>
      </user-data-constraint>
  </security-constraint>

transport-guarantee에 들어갈 수 있는 값으로는 다음의 3가지가 있다.

NONE, INTEGRAL, CONFIDENTIAL

NONE : 디폴트 값으로 데이터 보호를 하지 않겠다는 의미이다.
INTEGRAL : 전송 중 데이터가 변경되지 않음을 보장한다는 의미이다. - 데이터 무결성(Data Integrity)
CONFIDENTIAL : 전송 중 데이터를 누구도 훔쳐보지 않았음을 보장한다는 의미이다. - 기밀성(Confidentiality)

그런데 데이터 무결성과 기밀성은 클라이언트나 서버 단에서 보장할 수 있는 것이 아니다.
따라서 위에서 설정한 INTEGRAL, CONFIDENTIAL 과 같은 내용은 다음과 같은 방법으로 보장한다는 의미이다.

전송 보장이 설정된 경우, 클라이언트가 해당 자원에 보안이 되지 않은 상태로 요청을 하게 되면, 컨테이너는 301 코드를 내려 보내게 된다.
301 코드는 클라이언트에게 새로운 주소로 새로운 요청을 하라는 말인데, 여기서는 주소가 아니라 전송 방식을 http에서 https로 수정해서 보내도록 요청하는 것이다.
https로 다시 요청이 들어오면 해당 자원에 인증이 필요함을 확인 후 401 코드를 내려 보낸다.
클라이언트는 401 코드를 확인 후 유저 정보를 안전하게 https로 전송하게 되는 것이다.


'Java > Servlet & JSP' 카테고리의 다른 글

필터 - RESPONSE  (0) 2009.04.28
필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글  4개가 달렸습니다.
  1. 이해가 팍팍 되는군요 ㅎㅎ 잘 봤습니다 !! 퍼갈께욥~
  2. 좋은 자료 감사합니다. 출처 남길께요.^^
  3. 정말 좋은자료에요.....눈물...
secret
어떤 파일이 어느 곳에 위치하는지는 중요하다.
php를 개발 할 때 프로그래머가 임의로 정하는 장소와는 달리 Java(JSP)는 어느정도 rule을 가지고 특정 위치에 특정 파일을 배포해야지만, 전체 웹 애플리케이션이 돌아가기 때문이다.
따라서 중요한 몇개의 파일 타입별 배포 위치에 대해 정리해 보면 아래와 같다.

  • 정적인 컨텐츠(html, 이미지) 및 jsp
    • 루트 디렉토리 및 그 하위 디렉토리
    • WEB-INF 밑에 배포 할 경우에는 클라이언트가 직접 접근할 수 없다. 대신 웹 애플리케이션에서 접근을 하게 만들 경우를 위해서 배포할 순 있다.
  • 태그파일(.tag) - http://www.4te.co.kr/567 참고
    • WEB-INF/tags 디렉토리에 배포
  • DD 파일
    • 이름은 web.xml이어야 하며 WEB-INF 바로 밑에 두어야 한다.
  • jar 파일
    • WEB-INF/lib 밑에 두어야 한다.
    • jar파일 내 구조
      • jar 최상위 디렉토리 바로 밑에서부터 시작해야 한다.(패키지 시작부터)
      • jar로 배포할 경우 최상위 디렉토리에 META-INF 디렉토리가 있어야 하며, tld 파일은 META-INF 디렉토리 아래 배포 해야한다.
  • class 파일
    • WEB-INF/classes 밑에 패키지 구조로 배포 되어야 한다.

* WAR 파일로 배포하기

jar 파일로 애플리케이션 전체를 압축한 후 확장자만 war로 바꿔서 배포하는 것을 말한다.
압축하는 디렉토리는 웹 애플리케이션의 내용이 들어있는 디렉토리를 압축해서 배포하면 된다.
압축을 하는 위치는 디렉토리 위에서 압축하는 것이 아니라, 디렉토리 내부에서 압축해야 한다.
또한 압축 파일명은 웹애플리케이션의 이름이 된다(톰캣의 경우)

ex) /webapps/BeerApp 를 압축하기 위해서는 /webapps에서 BeerApp를 압축하는 것이 아니라 BeerApp 아래 들어가서 압축

WAR 파일의 경우 META-INF/MANIFEST.MF 파일에 라이브러리 의존성을 작성해 두면, 배포 시 컨테이너가 애플리케이션을 실행하기 위해 필요한 라이브러리가 있는지 사전 체크할 수 있다.

* 서블릿 맵핑

서블릿 맵핑은 물리적인 디렉토리가 아닌 가상의 디렉토리(URL)에서 실행할 내용을 찾기 위해 사용하는 것이다. 관련 내용은 http://www.4te.co.kr/538를 참고하면 된다.

참고)서블릿 맵핑은 와일드 카드와 같이 패턴 형식으로 맵핑할 수도 있다.

* DD에 환영 파일 설정하기

파일명을 안치고 디렉토리명까지 치면 자동을 찾게 할 파일명을 설정할 수 있다. web.xml에 다음과 같이 적어주면 된다.

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>

위 리스트 중에서 index.html과 index.jsp가 같이 있게 된다면 먼저 나온 index.html을 보여주게 된다.
apache의 httpd.con파일에 있는 다음과 같은 설정과 같은 역할을 해 주는 것이다.

<IfModule mod_dir.c>
    DirectoryIndex index.html index.php
</IfModule>

* DD에 오류 페이지 설정하기

오류 페이지를 web.xml에 설정하는 방법은 http://www.4te.co.kr/565 를 참고하면 된다.
프로그램에서 오류코드를 일부러 방생하고자 할 경우에는 다음과 같이 하면 된다.

response.sendError(HttpServletResponse.SC_FORBIDDEN);

위 내용은 403 에러를 내뱉는 경우를 프로그램적으로 처리한 경우이다. 실제 403 에러를 내 뱉는 것과 프로그램을 통해 내뱉는 것 사이의 차이를 클라이언트는 알 수 없다.

* DD에서 강제로 서블릿 초기화 하기

서블릿을 배포하게 되면 가장 처음 요청하는 클라이언트는 해당 서블릿이 로딩 될 때까지 기다려야 한다. 그리 무겁지 않은 서블릿일 경우에는 자동 로딩을 적용 하지 않아도 되지만, 무거운 서블릿일 경우에는 클라이언트에 의해 로딩되지 않고, 배포 할 때 로딩 되도록 만들어야 한다.
따라서 그럴 경우에는 아래와 같이 web.xml에 정의해 주면 된다.

  <servlet>
      <servlet-name>Chapter1 Servlet</servlet-name>
      <servlet-class>Ch1Servlet</servlet-class>
      <load-on-startup>1</load-on-startup>
  </servlet>

해당 숫자는 초기화 하는 순서를 말한다. 동일한 숫자가 여러개 있다면 web.xml에 정의한 순서대로 로딩한다.

* mime mapping 하기

DD(web.xml)에서 mime type을 확장자와 맵핑해 둘 수 있다.

  <mime-mapping>
      <extension>mpg</extension>
      <mime-type>video/mpeg</mime-type>
  </mime-mapping>




'Java > Servlet & JSP' 카테고리의 다른 글

필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26
사용자 정의 태그 개발 (1)  (0) 2009.03.23

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
커스텀 태그 안에 커스텀 태그가 위치해 있을 경우 서로간에 필요한 속성이라던가 내용 등을 읽어올 필요가 있다. 이럴 경우에는 다음과 같은 방법으로 접근할 수 있다.

myCustomTag3.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.1</tlib-version>

<uri>Nested</uri>
<tag>
    <description>NestedLevel Check</description>
    <name>NestedLevel</name>
    <tag-class>com.example.tag.NestedLevelTag</tag-class>
    <body-content>JSP</body-content>
</tag>
</taglib>

NestedLevelTag.java

package com.example.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;

public class NestedLevelTag extends TagSupport {
    private int nestLevel = 0;
   
    public int doStartTag() throws JspException {
        nestLevel = 0;
        Tag parent = getParent();
       
        while(parent != null) {
            parent = parent.getParent();
            nestLevel++;
        }
       
        try {
            pageContext.getOut().println("<br>Tag nested level : " + nestLevel);
        } catch (IOException e) {
            // TODO: handle exception
            throw new JspException("IOException - " + e.toString());
        }
       
        return EVAL_BODY_INCLUDE;
    }
}

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="Nested" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<mine:NestedLevel>
    <mine:NestedLevel>
        <mine:NestedLevel/>
    </mine:NestedLevel>
</mine:NestedLevel>
</body>
</html>

결과물

Insert title hereTag nested level : 0
Tag nested level : 1
Tag nested level : 2

getParent 메소드를 반복해서 호출해서 depth가 몇단계인지 알아내는 예제이다.

SimpleTag 인터넾이스와 Tag인터페이스는 JspTag 인터페이스를 구현한 것이다. SimpleTag의 getParent 메소드는 JspTag를 리턴하는데 반해 Tag(클래식 커스텀 태그)의 getParent 메소드는 Tag를 리턴한다.
이 점은 중요한데 왜냐하면 이 차이로 인해 Tag가 SimpleTag의 부모 태그가 될 수 있지만, SimpleTag가 Tag의 부모 태그가 될 수 없기 때문이다.
아래 내용은 Tag가 SimpleTag의 부모 태그로서 사용 되어질 경우 부모 태그의 attribute를 알아오는 예제이다.

classicParent.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.1</tlib-version>

<uri>ClassicParentTag</uri>
<tag>
    <description>classic parent</description>
    <name>ClassicParent</name>
    <tag-class>com.example.tag.MyClassicParent</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <name>name</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
<tag>
    <description>Simple Inner Tag</description>
    <name>SimpleInner</name>
    <tag-class>com.example.tag.SimpleInner</tag-class>
    <body-content>empty</body-content>
</tag>
</taglib>

SimpleInner.java

package com.example.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class SimpleInner extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        MyClassicParent parent = (MyClassicParent) getParent();
        getJspContext().getOut().print("Parent attribute is : " + parent.getName());
    }
}

MyClassicParent.java

package com.example.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

public class MyClassicParent extends TagSupport {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
   
    public int doStartTag() throws JspException {
        return EVAL_BODY_INCLUDE;
    }
}

classicParentTag.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="ClassicParentTag" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<mine:ClassicParent name="aaa">
    <mine:SimpleInner/>
</mine:ClassicParent>
</body>
</html>

결과물

Insert title hereParent attribute is : aaa

tld 파일에서는 ClassicParent 부모 태그와 SimpleInner 자식 태그를 정의 한다. ClassicParent 에는 name 이란 attribute가 존재 한다.
ClassicParent와 SimpleInner태그를 구현한 각 Java 파일은 위 소스와 같이 구현한다.
SimpleInner.java 파일에서 눈여겨 볼 것은 getParent 메소드를 통해 리턴 받은 JspTag형 객체를 ClassicParent로 형변환 해서 사용하는 것이다.
ClassicParent.java 파일에는 setter와 getter가 있는데, 자동으로 setter는 tld 에서 정의한 name이란 attribute에 값을 할당해 주며, getter는 SimpleInner.java에서 사용한다.
물론 ClassicParent.java의 doStartTag()에서 EVAL_BODY_INCLUDE를 리턴해 주어야지만 자식 태그인 SimpleInner가 실행 되게 된다.

중요한 것은 자식 태그에서 부모 태그의 정보를 읽을 수는 있어도 부모에서 자식 태그의 정보를 읽을 수는 없다.
따라서 부모에서 자식 태그를 알기 위해서는 반대로 자식에수 부모 쪽에 값을 셋팅 하는 방법을 사용해야 한다.
다음 예제를 통해서 부모가 자식의 속성 값을 알아내는 방법을 살펴보자.

menuTag.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.1</tlib-version>

<uri>MenuTag</uri>
<tag>
    <description>parent menu tag</description>
    <name>Menu</name>
    <tag-class>com.example.tag.Menu</tag-class>
    <body-content>JSP</body-content>
</tag>
<tag>
    <description>child menu tag</description>
    <name>MenuItem</name>
    <tag-class>com.example.tag.MenuItem</tag-class>
    <body-content>JSP</body-content>
    <attribute>
        <name>itemValue</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
</taglib>

Menu.java

package com.example.tag;

import java.util.ArrayList;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

public class Menu extends TagSupport {
    private ArrayList<String> items;
   
    public void addMenuItem(String item) {
        items.add(item);
    }
   
    public int doStartTag() throws JspException {
        items = new ArrayList<String>();
       
        return EVAL_BODY_INCLUDE;
    }
   
    public int doEndTag() throws JspException {
        try {
            pageContext.getOut().println("Menu items are : " + items);
        } catch (Exception e) {
            // TODO: handle exception
            throw new JspException("Exception : " + e.toString());
        }
        return EVAL_PAGE;
    }
}

MenuItem.java

package com.example.tag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

public class MenuItem extends TagSupport {
    private String itemValue;

    public void setItemValue(String itemValue) {
        this.itemValue = itemValue;
    }
   
    public int doStartTag() throws JspException {
        return EVAL_BODY_INCLUDE;
    }
   
    public int doEndTag() throws JspException {
        Menu parent = (Menu) getParent();
        parent.addMenuItem(itemValue);
        return EVAL_PAGE;
    }
}

menuItem.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="MenuTag" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<mine:Menu>
    <mine:MenuItem itemValue="Dogs"></mine:MenuItem>
    <mine:MenuItem itemValue="Cats"></mine:MenuItem>
    <mine:MenuItem itemValue="Horses"></mine:MenuItem>
</mine:Menu>
</body>
</html>

결과물

Insert title hereMenu items are : [Dogs, Cats, Horses]

menuTag.tld 파일에선 Menu태그와 MenuItem 태그를 정의한다. MenuItem 태그가 child 태그이며, itemValue라는 attribute를 가지고 있다.
Menu 태그를 정의한 Menu.java 파일에서는 ArrayList 변수 addMenuItem 메소드를 하나 만들고 값을 받아 하나씩 추가할 수 있도록 한다. addMenuItem 메소드는 child 태그인 MenuItem 클래스에서 사용할 메소드이다.
MenuItem 태그를 정의한 MenuItem.java에서는 doEndTag 부분에서 parent 객체를 하나 생성한 후 Menu 클래스에 있는 addMenuItem 메소드를 호출해 자신이 가지고 있는 itemValue라는 속성을 넘겨준다.
마지막으로 Menu 클래스의 doEndTag 메소드에서는 child 태그로부터 넘겨 받게 된 값을 화면에 보여준다.



'Java > Servlet & JSP' 카테고리의 다른 글

웹 애플리케이션 보안  (4) 2009.04.10
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26
사용자 정의 태그 개발 (1)  (0) 2009.03.23
커스텀 태그 사용하기  (0) 2009.03.17

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
JSP2.0 이전 버젼에서의 커스텀 태그는 심플 방식이 아닌 클래식 방식을 사용한다. simple 방식에서는 doTag() 메소드 하나로 모든 작업을 하고, exception 처리도 JspException, IOException를 throw 해서 catch 블럭이 없지만, 클래식 방식에는 doStartTag(), doEndTag() 메소드를 사용하며, JspException만을 throw 해서 catch 블럭에서 IOException을 잡을 필요가 있다.
간단하게 아래 예제를 통해 알아보자

tld 파일

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.9</tlib-version>

<uri>KathyClassicTags</uri>
<tag>
    <description>ludicrous use of a Classic tag</description>
    <name>classicOne</name>
    <tag-class>com.example.tag.Classic1</tag-class>
    <body-content>empty</body-content>
</tag>
</taglib>

com.example.tag.Classic1.java 파일

package com.example.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

public class Classic1 extends TagSupport {
    JspWriter out;
   
    public int doStartTag() throws JspException {
        out = pageContext.getOut();
        try {
            out.println("in doStartTag()");
        } catch (IOException e) {
            // TODO: handle exception
            throw new JspException("IOException - " + e.toString());
        }
        return SKIP_BODY;
    }
   
    public int doEndTag() throws JspException {
        try {
            out.println("in doEndTag");
        } catch (IOException e) {
            // TODO: handle exception
            throw new JspException("IOException - " + e.toString());
        }
        return EVAL_PAGE;
    }
}

closeTag1.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="KathyClassicTags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Classic Tag One : <br>
<mine:classicOne/>
</body>
</html>

Classic1 클래스를 보면 SimpleTagSupport가 아닌 TagSupport를 확장한다. 또한 getJspContext 대신에 pageContext를 사용한다.

doStartTag() 메소드에서 SKIP_BODY를 리턴 하는 것은 컨테이너가 다음에 어떤 일을 수행하도록 알려주는 것이다. 이 뜻은 body가 있더라도 실행하지 말고 곧바로 doEndTag()로 가라는 뜻이다.
화면에 print 해보면 해당 값이 0이라는 것을 알 수 있다.

또한 doEndTag() 메소드에서는 EVAL_PAGE를 리턴 하는데, 이 뜻은 작업이 끝났으니 페이지 뒷 부분을 실행하라는 의미이다.
심플 태그 핸들러의 SkipPageException과 비슷한 의미이다.

태그에 몸체가 있는 경우 몸체 안의 내용은 다음처럼 사용할 수 있다.

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.9</tlib-version>

<uri>KathyClassicTags</uri>
<tag>
    <description>ludicrous use of a Classic tag</description>
    <name>classicOne</name>
    <tag-class>com.example.tag.Classic1</tag-class>
    <body-content>tagdependent</body-content>
</tag>
</taglib>

com.example.tag.Classic1.java 파일

package com.example.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

public class Classic1 extends TagSupport {
    JspWriter out;
   
    public int doStartTag() throws JspException {
        out = pageContext.getOut();
        try {
            out.println("in doStartTag()");
            out.println("SKIP_BODY : " + SKIP_BODY);
            out.println("EVAL_BODY_INCLUDE : " + EVAL_BODY_INCLUDE);
        } catch (IOException e) {
            // TODO: handle exception
            throw new JspException("IOException - " + e.toString());
        }
        //return SKIP_BODY;
        return EVAL_BODY_INCLUDE;
    }
   
    public int doEndTag() throws JspException {
        try {
            out.println("in doEndTag");
            out.println("EVAL_PAGE : " + EVAL_PAGE);
        } catch (IOException e) {
            // TODO: handle exception
            throw new JspException("IOException - " + e.toString());
        }
        return EVAL_PAGE;
    }
}


closeTag1.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="KathyClassicTags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Classic Tag One : <br>
<mine:classicOne>
This is the body
</mine:classicOne>
</body>
</html>

Insert title here결과물

Insert title hereClassic Tag One :
in doStartTag() SKIP_BODY : 0 EVAL_BODY_INCLUDE : 1 This is the body in doEndTag EVAL_PAGE : 6

EVAL_BODY_INCLUDE는 몸체를 실행 시키라는 내용이며, 화면에 print 해 보면 1이라는 것을 알 수 있다. EVAL_PAGE의 값은 6이다.

심플 태그에서는 doTag 안에서 iterator를 실행 하면서 getJspBody().invoke(null)을 실행하면 루핑을 돌리면서 반복적인 작업을 할 수 있었다.
클래식 커스텀 태그에서는 doStartTag와 doEndTag로는 iterator 작업을 할 수 없으므로, doAfterTag를 이용할 수 밖에 없다. doAfterTag는 doStartTag에서 EVAL_BODY_INCLUDE를 리턴 하는 경우에만 실행 된다.
관련 소스는 아래를 참고하면 된다.

myCustomTag2.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.9</tlib-version>

<uri>KathyClassicTags2</uri>
<tag>
    <description>ludicrous use of a Classic tag</description>
    <name>iterateMovies</name>
    <tag-class>com.example.tag.Classic2</tag-class>
    <body-content>scriptless</body-content>
</tag>
</taglib>

Classic2.java

package com.example.tag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

public class Classic2 extends TagSupport {
    String[] movies = new String[] {"Spiderman", "Saved!", "Amelie"};
    int movieCounter;
   
    public int doStartTag() throws JspException {
        movieCounter = 0;

        pageContext.setAttribute("movie", movies[movieCounter]);
        movieCounter++;
       
        return EVAL_BODY_INCLUDE;
    }
   
    public int doAfterBody() throws JspException {
        if(movieCounter < movies.length) {
            pageContext.setAttribute("movie", movies[movieCounter]);
            movieCounter++;
            return EVAL_BODY_AGAIN;
        } else {
            return SKIP_BODY;
        }
    }
   
    public int doEndTag() throws JspException {
        return EVAL_PAGE;
    }
}

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="KathyClassicTags2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<table border="1">
<mine:iterateMovies>
    <tr><td>${movie}</td></tr>
</mine:iterateMovies>
</table>
</body>
</html>

doStartTag()에서는 EVAL_BODY_INCLUDE를 리턴하며 따라서 doAfterBody()를 호출하게 된다. doAfterBody 안에서는 movies 배열을 이용해 iterator 하면서 movie 객체에 값을 담는다. movieCounter가 movies의 길이만큼 루프를 돌게 되면 SKIP_BODY를 리턴 하면서 doEndTag를 호출하게 된다.
유의 할 점은 doAfterTag는 body가 한번 호출된 후부터 호출 되므로, doStartTag에서 "pageContext.setAttribute("movie", movies[movieCounter]);"를 한번 호출 한다.
doStartTag에서 호출하지 않을 경우 빈 값으로 <tr> 부분이 한번 iterator를 하기 때문에 빈 row가 하나 들어가게 된다.

movieCounter를 doStartTag 안에서 0으로 초기화 하는 것은 컨테이너가 풀(pool)로 관리하기 때문이다. 선언하면서 바로 초기화를 하게 되면, 재사용 될 경우에는 0으로 시작하지 않고 doAfterTag 의 iterator가 끝날 때 가지고 있는 값을 사용하게 될 것이기 때문이다.









'Java > Servlet & JSP' 카테고리의 다른 글

웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26
사용자 정의 태그 개발 (1)  (0) 2009.03.23
커스텀 태그 사용하기  (0) 2009.03.17
JSTL 사용하기 (2)  (0) 2009.03.16

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
<jsp:include> 표준 액션 또는 <c:import> JSTl과 같은 기능을 하는 태그 파일을 만들어 보자.

<img src="http://wstatic.naver.com/w9/lg_naver_v3.gif"><br>

위와 같이 코딩 한 후 WEB-INF 아래 tags란 디렉토리를 만들어 Header.tag 파일로 저장한다.
그리고 난 후 우리가 url에 호출할 파일을 만들어 보자.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="myTags" tagdir="/WEB-INF/tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<myTags:Header></myTags:Header>
Welcome to our site.
</body>
</html>

위와 같이 만든 다음 DocumentRoot 아래 customTag란 디렉토리 안에 customTag.jsp란 파일로 만들었다.
브라우져에 http://localhost:8080/customTag/customTag.jsp를 호출하면 아래와 같은 모습이 보여진다.


위에서 <myTags:Header></myTags:Header> 라고 사용한 부분을 보면 파일명이 바로 Tag 명이 된 것을 알 수 있다.
또한 <jsp:include> 표준액션에서 param을 사용한 것처럼 사용할 수 있다.

customTag.jsp

<jsp:include page="Header.jsp">
    <jsp:param name="subTitle" value="We take the sting out of SOAP"/>
</jsp:include>

Header.jsp

<img src="http://wstatic.naver.com/w9/lg_naver_v3.gif"><br>
<em><strong>${param.subTitle}</strong></em>

표준 액션을 사용하면 위와 같이 할 수 있다. 태그를 사용하면 아래와 같이 사용하면 된다.

customTag.jsp

<myTags:Header subTitle="We take the sting out of SOAP" />
Welcome to our site.

Header.tag

<%@ attribute name="subTitle" required="true" rtexprvalue="true" %>
<img src="http://wstatic.naver.com/w9/lg_naver_v3.gif"><br>
<em><strong>${subTitle}</strong></em>

attribute로 넘어온 값은 param 이라는 이름 없이 ${subTitle} 과 같이 바로 사용하면 된다. 또한 사용하기 위해서는 상단에 "<%@ attribute name="subTitle" required="true" rtexprvalue="true" %>" 와 같이 attribute 정의를 해 놔야 한다.
tld에 정의한 바와 같이 required는 필수 여부, rtexprvalue는 표현식 사용 여부를 나타내는 것이다.

속성으로 넘겨야 할 내용이 길다면 다음과 같이 사용할 수 있다.

customTag.jsp

<myTags:Header fontColor="#660099" subTitle="We take the sting out of SOAP">
Welcome to our site.
Welcome to our site.
Welcome to our site.
Welcome to our site.
Welcome to our site.
Welcome to our site.
Welcome to our site.
Welcome to our site.
Welcome to our site.
</myTags:Header>

Header.tag

<%@ attribute name="fontColor" required="true" %>
<%@ tag body-content="tagdependent" %>

<em><strong><font color="${fontColor}"><jsp:doBody></jsp:doBody></font></strong></em>

body-content에 들어가는 내용은 tld 정의에서 배운 내용 그대로이다.

컨테이너가 태그 파일을 찾는 위치는 4군데이다.

1. WEB-INF/tags 바로 밑
2. WEB-INF/tags의 하위 디렉토리
3. WEB-INF/lib에 JAR 파일로 배포되었다면, JAR파일 META-INF/tags 밑에
4. WEB-INF/lib에 JAR 파일로 배포되었다면, JAR파일 META-INF/tags의 하위 디렉토리

태그 파일이 JAR 파일로 배포 되었다면, 반드시 TLD 파일이 있어야 한다.

태그 파일만으로 부족할 경우에는 자바 클래스를 이용한 사용자 정의 태그를 만들 수 있다. http://www.4te.co.kr/566에서 이미 만들어 본 내용이지만 다시 한번 정리하면 아래와 같다.

package com.example;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class simpleTagTest1 extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        getJspContext().getOut().print("This is the lamest use of a custom tag");
    }
}

/WEB-INF/simpleTag.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>1.2</tlib-version>
<uri>simpleTags</uri>
<tag>
    <description>worst use of a custom tag</description>
    <name>simple1</name>
    <tag-class>com.example.simpleTagTest1</tag-class>
    <body-content>empty</body-content>
</tag>

</taglib>

/customTag/customTag2.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="myTags" uri="simpleTags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<myTags:simple1/>
</body>
</html>

설명은 http://www.4te.co.kr/566 에서 설명한 내용을 참고하면 된다.
몸체가 있는 태그일 경우에는 아래와 같이 하면 된다.

package com.example;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class simpleTagTest1 extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        //getJspContext().getOut().print("This is the lamest use of a custom tag");
        getJspBody().invoke(null);    //태그 몸체를 읽은 후 응답에 출력하라는 의미, 인자가 null이므로 Response로 출력하라는 의미
    }
}

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>1.2</tlib-version>
<uri>simpleTags</uri>
<tag>
    <description>worst use of a custom tag</description>
    <name>simple1</name>
    <tag-class>com.example.simpleTagTest1</tag-class>
    <body-content>scriptless</body-content>
</tag>

</taglib>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="myTags" uri="simpleTags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<myTags:simple1>
    This is the body
</myTags:simple1>
</body>
</html>

태그 몸체에 표현식이 있을 경우에는 다음과 같이 할 수 있다.

<myTags:simple1>
    This is the body ${message}
</myTags:simple1>

public class simpleTagTest1 extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        //getJspContext().getOut().print("This is the lamest use of a custom tag");
        getJspContext().setAttribute("message", "Wear sunscreen");
        getJspBody().invoke(null);
    }
}

동적으로 테이블 행을 출력하는 태그를 만들어 보면 아래와 같이 하면 된다.

public class simpleTagTest1 extends SimpleTagSupport {
    String[] movies = {"Monsoon Wedding", "Saved!", "Fahrenheit 9/11"};
    public void doTag() throws JspException, IOException {
        //getJspContext().getOut().print("This is the lamest use of a custom tag");
        //getJspContext().setAttribute("message", "Wear sunscreen");
       
        for(int i=0 ; i<movies.length ; i++) {
            getJspContext().setAttribute("movie", movies[i]);
            getJspBody().invoke(null);
        }
    }
}

<table>
<myTags:simple1>
    <tr><td>${movie}</td></tr>
</myTags:simple1>
</table>

태그 속성이 반드시 있어야 한다고 TLD에서 설정했다면, 태그핸들러 클래스에서는 Bean 스타일의 Setter가 있어야 한다.

<%@ taglib prefix="myTags" uri="simpleTags5" %>
<myTags:simple1 movieList="${movieCollection}">
    <tr>
        <td>${movie.name}</td>
        <td>${movie.gener}</td>
    </tr>
</myTags:simple1>

package com.example;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class simpleTagTest5 extends SimpleTagSupport {
    private List<String> movieList;

    public void setMovieList(List<String> movieList) {
        this.movieList = movieList;
    }
   
    public void doTag() throws JspException, IOException {
        Iterator<String> i = movieList.iterator();
       
        while(i.hasNext()) {
            Movie movie = (Movie) i.next();
            getJspContext().setAttribute("movie", movie);
            getJspBody().invoke(null);
        }
    }
}

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>1.2</tlib-version>
<uri>simpleTags5</uri>
<tag>
    <description>worst use of a custom tag</description>
    <name>simple1</name>
    <tag-class>com.example.simpleTagTest5</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
        <name>movieList</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>

</taglib>


어떤 태그를 호출 했을 경우, 오류가 떨어지더라도 지금까지 출력 된 내용은 화면에 나와야 한다면 SkipPageException을 사용할 수 있다.

getJspContext().getOut().print("Message from within doTag()<br>");
getJspContext().getOut().print("About to throw a SkipPageException");

if(thingsDontWork) {
    throw new SkipPageException();
}

<myTags:simple/>
Back in the page after invoking the tag.

위 클래스에서 thingsDontWork가 참이라서 SkipPageException이 던져지게 되면, 그 이전에 출력하려 했던 "Message from within doTag()<br>About to throw a SkipPageException은 나오지만, "Back in the page after invoking the tag."은 출력되지 않는다.

다음과 같이 하게 됬을 경우 나오게 되는 내용에 주의해야 한다.

pageA.jsp

aaaa<br>
<jsp:include page="pageB.jsp"></jsp:include>
after aaaa<br>

pageB.jsp

<%@ taglib prefix="myTags" uri="simpleTags" %>
bbbb<br>
<myTags:simple1></myTags:simple1>
after bbbb<br>

doTag() 메소드

    public void doTag() throws JspException, IOException {
        getJspContext().getOut().print("in doTag()<br>");
        throw new SkipPageException();
    }

pageB 안에 있는 myTags.simple1 태그는 doTag() 메소드를 호출 할 것이며 doTag 메소드에서는 SkipPageException을 throw 할 것이다.
이럴 경우에는 pageB.jsp에서만 처리가 중지 되며, pageA.jsp에서는 그대로 나머지 내용이 출력된다.
즉 다음과 같이 내용이 나오게 될 것이다.

aaaa
bbbb
in doTag()
after aaaa

즉, after bbbb 라는 내용은 나오지 않게 된다.


'Java > Servlet & JSP' 카테고리의 다른 글

부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26
사용자 정의 태그 개발 (1)  (0) 2009.03.23
커스텀 태그 사용하기  (0) 2009.03.17
JSTL 사용하기 (2)  (0) 2009.03.16
JSTL 사용하기 (1)  (5) 2009.03.12

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
사용자 정의 태그를 만들어서 사용할 수 있다.
사용 방법은 EL에서 함수를 만드는 방법과 유사하나 조금 더 복잡한 부면이 있다.
커스텀 태그를 만들기 위해서는 tld 파일과 java class 파일이 필요하다.
예제로 유저에게 랜덤하게 조언을 해 주는 커스텀 태그 소스를 보면 이해하는데 도움이 된다.(소스는 역시 Head & First Servelet & JSP에서 사용된 것이다.)

우선 tld 파일은 EL과 마찬가지로 WEB-INF 밑에 작성한다. 이름은 myCustomTag.tld로 한다.

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">

<tlib-version>0.9</tlib-version>

<short-name>RandomTags</short-name>
<function>
    <name>rollIt</name>
    <function-class>com.example.DiceRoller</function-class>
    <function-signature>int rollDice()</function-signature>
</function>

<uri>randomThings</uri>
<tag>
    <description>random advice</description>
    <name>advice</name>
    <tag-class>com.example.AdvisorTagHandler</tag-class>
    <body-content>empty</body-content>
    <attribute>
        <name>user</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
</taglib>

태그 이름은 <name> 태그의 내용을 사용하며 해당 태그가 호출될 경우 사용하는 java class 파일은 <tag-class> 태그에 있는 클래스를 사용한다.
<body-content>가 empty인 것은 몸체가 없이 사용한다는 뜻이며, 속성으로 사용되는 것으로 user라는 것이 있다는 의미이다.
rtexprvalue가 true인 것은 EL과 같은 표현식(스크립팅, 표준액션 포함) 값이 들어갈 수 있다는 뜻이다. 만일 rtexprvalue가 false라면 EL 표현식은 사용 불가이다.
위 내용을 jsp 페이지에서 커스텀 태그로 사용한다면 아래와 같이 사용할 수 있다.

<%@ taglib prefix="mine" uri="randomThings" %>

<c:set var="userName" value="오봉근"></c:set>
<mine:advice user="${userName}" />

위와 같이 쓰게 된다면 AdvisorTagHandler 클래스에서는 user라는 변수를 사용하여 어떤 액션을 하게 될 것이다.
AdvisorTagHandler 클래스의 소스는 아래와 같다.

com.example.DiceRoller.java 파일
package com.example;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class AdvisorTagHandler extends SimpleTagSupport {
    private String user;
   
    public void doTag() throws JspException, IOException {
        getJspContext().getOut().write("Hello " + user + " <br>");
        getJspContext().getOut().write("Your advice is : " + getAdvice());
    }
   
    public void setUser(String user) {
        this.user = user;
    }
   
    String getAdvice() {
        String[] adviceStrings = {"That color's not working for you", "You should call in sick", "You might want to rethink that haircut."};
        int random = (int) (Math.random() * adviceStrings.length);
       
        return adviceStrings[random];
    }
}


위 클래스에서 주의 해야 할 점은 doTag 메소드이다.
JSP가 태그를 실행하게 되면 컨테이너는 자동으로 doTag 메소드를 실행하게 된다. EL에서 메소드 명을 지정할 수 있었던 것과 다르다.
또 주의해서 볼 것은 setUser 메소드이다. 이름에서 볼 수 있듯이 자바 빈 프로퍼티 명명 규칙을 따서 사용하게 된다. 즉 커스텀 태그에서 사용하는 attribute의 이름을 빈 프로퍼티로 사용하게 된다.

태그 몸체에 들어갈 수 있는 내용들은 아래와 같다.

<body-content>empty</body-content>
=> 몸체를 가질 수 없다.

<body-content>scriptless</body-content>
=> 스크립팅(스크립틀릿, 스크립팅 표현식, 선언문)은 올 수 없다. 하지만 템플릿 텍스트, EL, 커스텀 태그, 표준 액션은 가능하다.

<body-content>tagdependent</body-content>
=> 태그 몸체를 그냥 텍스트로 취급한다.

<body-content>JSP</body-content>
=> JSP 안에 들어갈 수 있는 것이라면 무엇이든지 가능하다.

<body-content>empty</body-content>라고 되어 있어도 jsp 표준액션을 사용하면 body에 내용을 기술 할 수 있다.

<mine:advice>
    <jsp:attribute name="user">${userName}</jsp:attribute>
</mine:advice>

<jsp:attribute> 태그는 속성을 기술하는 또 하나의 방식이므로, body content 개수로 치지 않으며, 따라서 시작 태그와 마침 태그에 들어갈 수 있다.

taglib에 들어가는 uri는 실제 uri(위치정보)가 아니다. 다른 태그 라이브러리와 구별하기 위한 유일한 값을 지정하기 위해 사용하는 이름일 뿐이다.
uri와 실제 TLD 파일은 컨테이너가 알아서 맵핑한다. 하지만 jsp 2.0 이전 버젼에서는 web.xml에 맵핑 정보를 적어 줘야 했었다.

  <jsp-config>
      <taglib>
          <taglib-uri>randomThings</taglib-uri>
          <taglib-location>/WEB-INF/</taglib-location>
      </taglib>
  </jsp-config>

jsp 2.0 버전에서는 사용하지 않아도 컨테이너가 알아서 uri 이름에 대한 맵을 만든다는 의미이지, 위 내용을 web.xml에 기술 안해야지만 된다는 의미는 아니다. web.xml에 기술을 하게 된다면 해당 내용을 사용하게 되며, 없을 경우 자동으로 searching을 하게 된다.
컨테이너가 자동으로 TLD 파일을 찾는 위치는 다음과 같다.

1. WEB-INF 안
2. WEB-INF 아래 하위 디렉토리
3. WEB-INF/lib 밑에 jar 파일로 배포 했다면 jar 안 META-INF 디렉토리
4. WEB-INF/lib 밑에 jar 파일로 배포 했다면 jar 안 META-INF 아래 하위 디렉토리

jsp에서 여러개의 태그라이브러리를 사용한다면, 각각의 TLD에 대한 uri를 작성해야 한다. uri는 유일해야 하며, 같은 값을 중복해서 사용할 수 없다.
또한 예약어를 첨자로 사용할 수 없다.

jsp:, jspx:, java:, javax:, servlet:, sun:, sunw:




'Java > Servlet & JSP' 카테고리의 다른 글

클래식 커스텀 태그  (0) 2009.03.26
사용자 정의 태그 개발 (1)  (0) 2009.03.23
커스텀 태그 사용하기  (0) 2009.03.17
JSTL 사용하기 (2)  (0) 2009.03.16
JSTL 사용하기 (1)  (5) 2009.03.12
템플릿 형태로 JSP 사용하기  (0) 2009.02.16

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
클라이언트의 브라우져에서 쿠키 사용을 제한 하고 있을 경우 jsessionid를 추가하여 url로 세션 정보를 공유하는 방법은 이미 서블릿에서 살펴 봤다.
jstl에서도 이와 같은 방법을 사용할 수 있다.

서블릿
response.encodeURL("/BeerTest.do");

jstl
<c:url value="/inputCommets.jsp"></c:url>

위와 같이 하게 되면 url 뒤에 jsessionid를 덧 붙여서 사용하게 된다.
하지만 urlencoding을 자동으로 하지는 않게 된다.
urlencoding을 하게 하려면 아래와 같은 방법을 사용해야 한다.

<c:set var="last" value="Hidden Cursor"></c:set>
<c:set var="first" value="Crouching Pixels"></c:set>
<c:url value="/inputCommets.jsp">
    <c:param name="firstName" value="${first}"></c:param>
    <c:param name="lastName" value="${last}"></c:param>
</c:url>

위와 같이 하게 되면 last, first에 설정 된 값에 있는 공백과 같은 값이 인코딩 되어 사용 된다.

* 오류 페이지 만들기

오류 메시지를 그대로 보여주지 않고 디자인 된 페이지를 보여주려면, 예외 사항이 발생하는 페이지에 다음과 같이 코딩해 주면 된다.

<%@ page errorPage="errorPage.jsp" %>
<% int x = 10/0; %> -> 예외 발생

에러 시 보여줄 페이지

<%@ page isErrorPage="true" %>
<body>
Error Page!!!
</body>

하지만, 모든 페이지에서 <%@ page errorPage="errorPage.jsp" %> 내용을 삽입하게 된다면 여간 귀찮은 일이 아닐 것이다. 따라서 configuration에서 error page를 지정하는 방법을 써서 수고를 덜 수 있다.

web.xml

  <error-page>
      <exception-type>java.lang.Throwable</exception-type>
      <location>/errorPage.jsp</location> => 일반적인 에러
  </error-page>
  <error-page>
      <exception-type>java.lang.ArithmeticException</exception-type>
      <location>/arithmeticError.jsp</location> => 특수한 경우의 에러
  </error-page>
  <error-page>
      <error-code>404</error-code>
      <location>/notFoundError.jsp</location> => 에러 코드에 따른 경우
  </error-page>

물론 web.xml에서 error page임을 명시해도 errorPage.jsp 파일에서는 "<%@ page isErrorPage="true" %>" 내용을 꼭 넣어야 한다.
errorPage.jsp에서는 exception 객체를 사용할 수 있다.
사용 방법은 다음과 같이 쓴다.

<%@ page isErrorPage="true" %>
You caused a ${pageContext.exception} on the Server.

Insert title hereError Page!!! You caused a java.lang.ArithmeticException: / by zero on the Server.

위의 예와 같은 경우에는 위와 같이 브라우져에 출력되게 된다.

오류가 났을 경우 오류 페이지로 가지 않고 catch로 잡을 수도 있다.
방법은 다음과 같다.

<c:catch>
    Inside the catch
    <% int x = 10/0; %>
</c:catch>

오류가 난 원인을 알기 위해서는 exception 객체를 사용해야 하는데, isErrorPage로 설정되지 않은 jsp에서는 exception 객체를 사용할 수 없다. 따라서 다음과 같이 사용해야 한다.

<c:catch var="myException">
    Inside the catch
    <% int x = 10/0; %>
</c:catch>

<c:if test="${myException != null}">
    There was an exception : ${myException.message}<br>
</c:if>

Insert title hereThere was an exception : / by zero

jstl에서 사용되는 5개의 라이브러리 중에서 자주 사용되는 태그는 다음과 같다.

* Core 라이브러리

<c:out>, <c:set>, <c:remove>, <c:catch>

<c:if>, <c:choose>, <c:when>, <c:otherwise>

<c:import>, <c:url>, <c:redirect>, <c:param>

<c:forEach>, <c:forTokens>

* 포맷팅 라이브러리

<fmt:message>, <fmt:setLocale>, <fmt:bundle>, <fmt:setBundle>, <fmt:param>, <fmt:requestEncoding>

<fmt:timeZone>, <fmt:setTimeZone>, <fmt:formatNumber>, <fmt:parseNumber>, <fmt:parseDate>

* SQL 라이브러리

<sql:query>, <sql:update>, <sql:setDataSource>, <sql:param>, <sql:dateParam>

* XML 라이브러리

<x:parse>, <x:out>, <x:set>

<x:if>, <x:choose>, <x:when>, <x:otherwise>, <x:forEach>

<x:transform>, <x:param>





'Java > Servlet & JSP' 카테고리의 다른 글

사용자 정의 태그 개발 (1)  (0) 2009.03.23
커스텀 태그 사용하기  (0) 2009.03.17
JSTL 사용하기 (2)  (0) 2009.03.16
JSTL 사용하기 (1)  (5) 2009.03.12
템플릿 형태로 JSP 사용하기  (0) 2009.02.16
EL 함수 사용하기  (0) 2009.02.13

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
jsp에서 스크립팅을 사용하지 않고 루프를 돌리거나 조건문을 실행하는 방법으로 사용하는 것이 JSTL이다.
JSTL을 사용하기 위해서는 다음과 같은 방법으로 설정을 해 줘야 한다.

apache-tomcat-6.0.18\webapps\examples\WEB-INF\lib 에 있는 jstl.jar 파일과 standard.jar 파일을 WebContent/WEB-INF/lib 밑으로 copy 한다.

위와 같이 하게 되면 jstl을 사용할 수 있다.
jstl을 사용할 수 있게 됬으므로 forEach 문을 돌려보자.
사용 방법은 php에서 사용하는 foreach 문과 별로 다를 바가 없다.

web.xml

  <servlet>
      <servlet-name>jstlTest</servlet-name>
      <servlet-class>com.example.jstlTest</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>jstlTest</servlet-name>
      <url-pattern>/Jstl</url-pattern>
  </servlet-mapping>


서블릿 코드

package com.example;

import java.awt.List;
import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class jstlTest extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        String[] movieList = {"Amelie", "Return of the King", "Mean Girls"};
        request.setAttribute("movieList", movieList);
       
        String[] movies1 = {"Matrix Revolutions", "Kill Bill", "Boondock Saints"};
        String[] movies2 = {"Amelie", "Return of the King", "Mean Girls"};
       
        ArrayList movieList2 = new ArrayList();
        movieList2.add(movies1);
        movieList2.add(movies2);
        request.setAttribute("movies", movieList2);
       
        RequestDispatcher view = request.getRequestDispatcher("jstlTest.jsp");
        view.forward(request, response);
    }
}

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<table>
    <c:forEach var="movie" items="${movieList}" varStatus="movieLoopCount">
        <tr><td>${movieLoopCount.count} : ${movie}</td></tr>
    </c:forEach>
    <c:forEach var="listElement" items="${movies}" varStatus="listCount">
        <c:forEach var="movie" items="${listElement}" varStatus="movieCount">
            <tr><td>${listCount.count}, ${movieCount.count}, ${movie}</td></tr>
        </c:forEach>
    </c:forEach>
</table>
</body>
</html>

서블릿에서 배열이나 ArrayList에 담아서 request 객체에 담아 jsp로 forwarding을 넘긴다.
jsp에서는 "<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>"라고 taglib 지시자를 선언하고 아래 부분에 forEach문을 통해서 해당 내용을 출력한다.
forEach문에서 items를 통해서 서블릿에서 넘겨준 request의 속성을 사용할 수 있으며 배열이나 ArrayList의 각 속성은 var라고 선언한 이름에 담겨 사용되어 진다.
forEach문을 통해 루푸가 도는 동안에는 for문에서 사용되어지는 i와 같은 속성을 varStatus를 통해서 사용할 수 있다.

말로 주저리 주저리 설명하는 것보다 위 소스 코드를 직접 실행해 보는 것이 이해하는데 훨씬 도움이 될 듯 하다.
위 소스를 실행(http://localhost:8080/Jstl)해 보면 아래와 같이 출력된다.

1 : Amelie
2 : Return of the King
3 : Mean Girls
1, 1, Matrix Revolutions
1, 2, Kill Bill
1, 3, Boondock Saints
2, 1, Amelie
2, 2, Return of the King
2, 3, Mean Girls

jstl을 사용한 if문은 다음과 같이 사용할 수 있다.

서블릿 코드
String userType = "member";
request.setAttribute("userType", userType);

jsp 코드
<c:if test="${userType eq 'member'}">
    <jsp:include page="inputComments.jsp"></jsp:include>
</c:if>

test 문을 사용해서 조건부의 내용을 검사한다.
else 문은 따로 없으며 여러 조건 들 중을 검사하거나, else와 같은 경우를 처리하려면 choose, when, ohterwise를 사용하면 된다.

서블릿
String userPref = "safety";
request.setAttribute("userPref", userPref);

jsp 코드
<c:choose>
    <c:when test="${userPref == 'performance'}">
        Performance...
    </c:when>
   
    <c:when test="${userPref == 'safety'}">
        Safety...
    </c:when>
   
    <c:when test="${userPref == 'maintenance'}">
        Maintenance...
    </c:when>
   
    <c:otherwise>
        Otherwise...
    </c:otherwise>
</c:choose>

변수를 셋팅하기 위해서는 set을 사용한다. 사용 방법은 다음과 같다.

<c:set var="userLevel" scope="session" value="Cowboy"></c:set>
userLevel : ${userLevel}<br>
<c:set var="Fido" value="${person.dog}"></c:set>
<c:set var="userLevel" scope="session">
    Sheriff, Bartender, Cowgirl
</c:set>
userLevel : ${userLevel}<br>

<c:set target="${PetMap}" property="dogName" value="Clover"></c:set>
<c:set target="${person}" property="name">
    ${foo.name}
</c:set>

var로 셋팅하는 경우는 변수이며, target인 경우에는 빈이나 맵을 셋팅하게 된다. property는 빈인 경우 프로퍼티가, 맵인 경우에는 키가 된다.
value는 몸체가 없는 경우에는 value property를 사용해서 정의하면 되며, 몸체가 있는 경우에는 그냥 나열하면 된다. value는 꼭 문자열일 필요가 없으며, 객체를 사용할 수 있다.
value가 null일 경우에는 var나 target은 파괴된다.
target에는 실제 객체를 표현하는 표현식이 들어가야 한다. 빈이나 맵의 id 이름을 나타내는 문자열을 기입하면 안된다.

scope는 옵션이며, scope를 표기하지 않을 경우에는 page -> request -> session -> application(context) 순서대로 찾아 나간다. 4군데 모두 없다면, 새롭게 생성하게 된다.

셋팅된 변수를 제거하기 위해서 value를 null로 셋팅해도 되지만, remove 태그를 사용해서 사용하는 것이 정석이다.

<c:set var="userStatus" scope="request" value="Brilliant"></c:set>
userStatus : ${userStatus} <br>
<c:remove var="userStatus" scope="request"/>
userStatus : ${userStatus} <br>

결과물

Insert title hereuserStatus : Brilliant
userStatus :

다른 컨텐츠를 포함하는 jstl 태그는 다음과 같다.

<c:import url="http://www.naver.com/"></c:import>

현재까지 알아본 다른 컨텐츠를 포함하는 방법으로는 include 지시자와 표준액션, 그리고 jstl 태그를 사용하는 방법까지 총 3가지였다.

<%@ include file="Header.html" %>    //include 지시자
<jsp:include page="Header.jsp"></jsp:include>    //include 표준액션

include 지시자는 정적인 페이지를, include 표준 액션은 동적인 페이지를, 그리고 jstl import 태그는 동적인 페이지와 외부 서버에 있는 url 형식의 페이지까지 포함할 수 있다.
include 지시자는 정적인 페이지를 include 하므로 file이란 속성을 사용하며, 표준액션은 동적인 페이지를 include 하므로 page란 속성을 사용한다.
jstl import 태그는 외부 서버에 있는 페이지까지 include할 수 있으므로 url 이란 속성을 사용한다.

<jsp:include> 태그에서 파라미터 값을 넘길 경우 param 태그를 사용하였는데, 그와 같은 기능을 하는 jstl 태그도 있다.

<jsp:include page="Header.jsp">
    <jsp:param name="subTitle" value="We take the sting out of SOAP"/>
</jsp:include>

<c:import url="Header.jsp">
    <c:param name="subTitle" value="We take the sting out of SOAP"></c:param>
</c:import>

Header.jsp

<body>
subTitle : ${param.subTitle}
</body>

Insert title heresubTitle : We take the sting out of SOAP




'Java > Servlet & JSP' 카테고리의 다른 글

커스텀 태그 사용하기  (0) 2009.03.17
JSTL 사용하기 (2)  (0) 2009.03.16
JSTL 사용하기 (1)  (5) 2009.03.12
템플릿 형태로 JSP 사용하기  (0) 2009.02.16
EL 함수 사용하기  (0) 2009.02.13
EL 사용하기  (3) 2009.02.12

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글  5개가 달렸습니다.
  1. 좋은 자료 감사합니다. ^^
  2. 저도 좋은 자료에 감사드립니다 ^^
  3. 잘보고갑니다.^^ 퍼갈게요.
  4. 비밀댓글입니다
secret
템플릿 형태로 모듈화 해서 jsp를 사용하기 위해서는 include를 사용하면 된다.
include를 사용하는 형식은 "include 지시자"와 "include 표준 액션"을 사용할 수 있다.

include 지시자를 사용하여 코딩하는 예제는 다음과 같다.

<%@ include file="Header.jsp" %>

include 표준 액션은 다음과 같이 사용한다.

<jsp:include page="Header.jsp"></jsp:include>

지시자를 사용한 include일 경우에는 변환시에, include 표준액션은 실행시에 Header.jsp파일을 실행한다.
만일 Header.jsp 파일에서 동적인 내용을 포함할 페이지로부터 받아야 한다면 다음과 같이 사용할 수 있다.

<jsp:include page="Header.jsp">
    <jsp:param name="subTitle" value="We take the sting out of SOAP."/>
</jsp:include>

Header.jsp

<strong>${param.subTitle}</strong>

변수를 미리 선언해 두고 Header.jsp 파일에서 사용하는 방법이 아닌 param 태그를 사용하여 변수를 넘길 수 있다.

* forward 표준액션

다른 jsp 페이지로 요청을 넘길 수도 있다. 이 때 사용하는 액션이 foward 액션이다.
사용 방법은 아래와 같다.

<jsp:forward page="HandleIt.jsp"></jsp:forward>

forward를 싱행하게 되면 forward 이전에 나온 내용은 버퍼에서 삭제해 준다. 다만 forward 이전에 <% out.flush(); %>를 실행하게 되면 이미 버퍼가 출력된 상태이므로 forward 액션은 실행되지 않는다.


'Java > Servlet & JSP' 카테고리의 다른 글

JSTL 사용하기 (2)  (0) 2009.03.16
JSTL 사용하기 (1)  (5) 2009.03.12
템플릿 형태로 JSP 사용하기  (0) 2009.02.16
EL 함수 사용하기  (0) 2009.02.13
EL 사용하기  (3) 2009.02.12
표준 액션을 사용한 JSP  (0) 2009.02.11

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret