'Java/Servlet & JSP'에 해당하는 글 26건

필터를 통해 서블릿이 실행되기 전에 특정 액션을 하게 만들 수 있다고 했다.
그런데 서블릿이 실행되고 나서 특정 액션을 하게 만들려면 어떻게 해야 할까?
간단하게 생각해 보면 필터의 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
EL 표현에서도 함수(메소드)를 사용할 수 있다.
EL에서 메소드를 사용하기 위해서는 다음과 같은 순서를 따라 작성해야 한다.

1. 정적인 공용 메소드를 제공하는 클래스를 만든다.
해당 메소드는 EL(JSP)에서 사용할 메소드이므로 반드시 정적인 공용 메소드여야 하며, 리턴 타입이 있어야 한다. 그래야지 JSP에서 브라우져에 내용을 그릴 수 있다.

2. 태그 라이브러리 서술자 파일을 만든다.
  EL 함수를 사용하기 위해서는 TLD(Tag Library Descriptor)란 파일로 자바 클래스, 메소드와 EL에서 호출할 메소드 명을 맵핑해야 한다.

3. JSP에 taglib 지시자를 코딩한다.

4. 함수를 호출하는 EL을 작성한다.

위 형식대로 EL을 작성해 보면 아래 처럼 작성할 수 있다.

* DiceRoller.java

package com.example;

public class DiceRoller {
    public static int rollDice() {
        return (int) ((Math.random() * 6) + 1);
    }
}

* myFunction.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>DiceFunctions</uri>
    <function>
        <name>rollIt</name>
        <function-class>com.example.DiceRoller</function-class>
        <function-signature>
            int rollDice()
        </function-signature>
    </function>
</taglib>

* TestBean.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="mine" uri="DiceFunctions" %>
<!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:rollIt()}
</body>
</html>

java파일은 com.example 패키지에 만들고, tld 파일은 WEB-INF 디렉토리 밑에 만든다. jsp 파일은 WebContent(DocumentRoot) 아래에 바로 만든다.
조금 복잡한 감이 있다.
jsp 파일에서는 네임스페이스 개념의 prefix를 taglib에 정해주고 호출할 uri를 적어준다. 호출할 uri명은 tld 파일에 작성한 uri 값이다.
tld 파일에서는 jsp에서 호출할 함수 이름(rollIt)과 실재 class명, 그리고 실재 함수명을 적어주고 실재 클래스, 함수와 jsp에서 호출할 함수명을 서로 맵핑 시킨다.

개인적으로 이해가 되지는 않지만, 자바는 겹겹히 숨겨 두는 방식을 좋아하는 듯 하다 -.-;;

덧1. function-signature에 들어가는 실제 메소드에 인자를 줄 수 있다.

int rollDice(java.util.Map)

덧2. 컨테이너는 WEB-INF 밑에 있는 tld 파일을 찾게 끔 되어 있으므로, tld 파일을 작성 후 WEB-INF 디렉토리 밑에 두면 된다.

덧3. 몇 가지 EL 연산자들

다음과 같은 간단한 연산을 EL 단에서도 할 수 있다.

+, -, *, /, %, &&, ||, !, ==, !=, <, >, <=, >=










신고

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

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
JSP 초기화 하기  (0) 2009.02.10

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

받은 트랙백이 없고 , 댓글이 없습니다.
secret
프로퍼티의 프로퍼티를 스크립틀릿이나 표준액션으로만 표현하기가 어렵거나 아예 불가능한 경우가 있다.
즉 다음과 같은 서블릿 코드를 스크립틀릿이나 표준액션으로 표현하려고 하면 다음과 같이 표현할 수 밖에 없을 것이다.

    public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
        foo.Person p = new foo.Person();
        p.setName("Evan");
       
        foo.Dog dog = new foo.Dog();
        dog.setName("spike");
        p.setDog(dog);
       
        request.setAttribute("person", p);
       
        RequestDispatcher view = request.getRequestDispatcher("result.jsp");
        view.forward(request, response);
    }

<%= ((foo.Person) request.getAttribute("person")).getDog().getName() %>

<jsp:useBean id="person" class="foo.Person" scope="request" />
<jsp:getProperty name="person" property="dog"/>

스크립틀릿으로 쓰면 결과물은 제대로 나오지만 많이 지져분한 모습이다.
표준액션을 사용하게 되면 dog라는 프로퍼티가 있는 것이 아니라 dog라는 객체안에 있는 name이라는 프로퍼티를 출력해야 하므로 결과가 잘못 나오게 된다.

이런 경우를 위해서 사용하는 것이 EL(Expression Language) 이다.
위 내용을 EL로 표현하게 되면 ${person.dog.name} 이라고 간단하게 표현할 수 있는 것이다.
EL에서 도트연산자(.) 앞에 있는 것(person)은 반드시 맵이거나 빈이여야 한다. 도트 연산자 우측에 있는 것은 맵의 키이거나 빈 프로퍼티여야 한다.
${person.dog.name}을 php로 표현하자면 $person[$dog['name']] 정도로 표현할 수 있을지도 모르겠다. ^^
오른쪽에 오는 값은 일반적인 자바 명명규칙을 따라야 한다.

도트 연산자 대신에 [] 연산자를 사용할 수도 있다.
즉 ${person.name}은 ${pserson["name"]}처럼 쓸 수 있다.

도트 연산자의 왼쪽에는 맵, 빈만 올 수 있었지만, [] 연산자의 경우에는 왼쪽에 맵, 빈 외에도 배열, 리스트가 올 수 있다.
또한 [] 안에 따옴표로 감싸져 있으므로 [] 안의 내용은 맵 키, 빈 프로퍼티, 리스트나 배열의 인덱스가 될 수 있다.
다음은 []의 예제이다.

서블릿 코드

String[] favoriteMusic = {"Zero 7", "Tahiti 80", "BT", "Frou Frou"};
request.setAttribute("musicList", favoriteMusic);

JSP 코드

${musicList}
${musicList[0]}
${musicList["1"]}

${musicList}은 배열을 toString()으로 변환해서 보여주기 때문에 제대로 된 값이 출력되진 않는다. ${musicList[0]}는 "Zero 7"이 ${musicList["1"]}는 "Tahiti 80"이 나오게 된다.

php에서 $array[key] 라고 하면 key라는 상수가 있나 없나를 확인 후 없을 경우 해당 key에 맵핑 된 value를 찾아 보여준다.(따라서 한번의 확인 과정을 더 거치므로 속도가 떨어진다.)
이와 마찬가지로 EL에서도 다음과 같이 작성해도 무방하다.

서블릿 코드

java.util.Map musicMap = new java.util.HashMap();
musicMap.put("Ambient", "Zero 7");
musicMap.put("Surf", "Tahiti 80");
musicMap.put("DJ", "BT");
musicMap.put("Indie", "Frou Frou");

request.setAttribute("musicMap", musicMap);
request.setAttribute("Genre", "Ambient");

JSP 코드

${musicMap[Genre]}
${musicMap["Genre"]}

위 코드에서 따옴표가 없는 Genre는 request.setAttribute를 통해서 Ambient라는 String이 할당되어 있다. 따라서 ${musicMap[Genre]}는 ${musicMap["Ambient"]}와 같은 내용이며 결과 값은 "Zero 7"이 나오게 되는 것이다.
하지만 따옴표가 있는 "Genre"는 말 그대로 String Genre이므로 변환이 되지 않으며 musicMap에서 해당 키가 있나 찾게 되는 것이다.
musicMap에는 Genre라는 키가 없으므로 결과 값이 null이 나오게 된다.

위와 같은 형식으로 아래와 같은 표현도 가능하다.

서블릿 코드

java.util.Map musicMap = new java.util.HashMap();
musicMap.put("Ambient", "Zero 7");
musicMap.put("Surf", "Tahiti 80");
musicMap.put("DJ", "BT");
musicMap.put("Indie", "Frou Frou");

String[] musicType = {"Ambient", "Surf", "DJ", "Indie"};
request.setAttribute("MusicType", musicType);

JSP 코드

${musicMap[MusicType[0]]}    //${musicMap["Ambient"]}와 같은 뜻.

MusicType[0]의 값이 "Ambient"이기 때문에 ${musicMap["Ambient"]}와 같은 뜻이다.
주의 할 점은 도트연산자의 우측에 오는 내용은 자바 명명규칙을 따르기 때문에 ${musicList["1"]}은 가능해도 ${musicList.1}과 같이 사용할 수는 없다.

* EL 내장객체

EL에서 사용하는 내장객체가 있다. EL에서 사용하는 내장 객체는 pageContext만 제외하고는 모드 맵이다. EL에서 사용하는 내장 객체는 다음과 같다.

pageScope, requestScope, sessionScope, applicationScope, param, paramValues, header, headerValues, cookie, initParam, pageContext

요청 파라미터와 같은 경우에는 다음과 같이 사용할 수 있다.

<form action="TestBean.jsp">
    <input type="text" name="name">
    <input type="text" name="empID">
   
    <input type="checkbox" name="food">
    <input type="checkbox" name="food">
</form>

${param.name}
${param.empID}
${param.food}
${paramValues.food[0]}
${paramValues.food[1]}
${paramValues.name[0]}

이름이 같은 경우에 paramValues를 사용할 수 있으며 이름이 한개인 경우에 paramValues를 사용하게 되면 0번째 인덱스가 그 값이 된다.

request parameter를 param 또는 paramValues를 이용해서 사용할 수 있는데 그보다 더 많은 내용은 어떻게 알 수 있을까?
기타 정보는 다음과 같은 방법으로 알 수 있다.

host 정보

${header["host"]}
${header.host}

http 요청 메소드

${pageContext.request.method}

스크립팅에서는 <%= request.getMethod() %>와 같이 사용하였지만, EL에서 ${request.method} 또는 ${requestScope.method}라고 하면 오류가 난다.
request란 내장객체가 EL에는 존재하지 않고, 또한 requestScope는 단순 request 생존 범위에 묶여 있는 속성에 대한 단순 맵이기 때문이다.

* 생존 범위

생존범위를 사용하는 이유는 이름 충돌을 피하기 위해서이다. 또한 한가지 이유로 생존범위를 쓰는데 다음과 같은 경우를 위해서이다.

서블릿

request.setAttribute("foo.person", p);

JSP

${foo.person.name}    //틀린 표현
${requestScope["foo.person"].name}    //맞는 표현

${foo.person.name}와 같이 표현하게 되면 foo란 속성 밑에 person이란 프로퍼티가 있다는 식으로 컨테이너가 생각할 우려가 있다.("foo.person"이란 String임에도 불구하고)
따라서 ${requestScope["foo.person"].name}와 같은 식으로 표현하게 되면 문자열에 도트가 들어가 있어도 문제 없이 사용할 수 있다.

* 쿠키

쿠키를 스크립팅을 사용해서 보여주기 위해서는 매우 복잡(?)한 코드가 필요하다.

스크립팅

<%
Cookie[] cookies = request.getCookies();

for(int i=0 ; i<cookies.length ; i++) {
    if((cookies[i].getName().equals("userName"))) {
        out.println(cookies[i].getValue());
    }
}
%>

EL

${cookie.userName.value}

* 컨텍스트 초기화 파라미터

web.xml

  <context-param>
      <param-name>breed</param-name>
      <param-value>Great Dane</param-value>
  </context-param>

스크립팅

<%= application.getInitParameter("breed") %>

EL

${initParam.breed}

context-param이지만 EL에서는 contextParam이라고 하지 않고 initParam이라고 한다.



신고

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

템플릿 형태로 JSP 사용하기  (0) 2009.02.16
EL 함수 사용하기  (0) 2009.02.13
EL 사용하기  (3) 2009.02.12
표준 액션을 사용한 JSP  (0) 2009.02.11
JSP 초기화 하기  (0) 2009.02.10
스크립틀릿에서 변수 선언하기  (0) 2009.02.04

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

받은 트랙백이 없고 , 댓글  3개가 달렸습니다.
  1. 정말 잘 정리하셨네요. ^^
    잘 보고 갑니다~!
  2. 비밀댓글입니다
secret
"자바 빈을 이용해서 특정 객체의 값을 가져와 보여주는 소스는 아래와 같은 형식으로 jsp에서 사용할 수 있다.

<% foo.Person p = (foo.Person) request.getAttribute("person"); %>
Person is : <%= p.getName() %>

jsp내에서 스크립틀릿을 없애기 위해 나온 것이 표준 액션이므로 스크립틀릿을 없애는 방법으로 표현하자면 아래와 같이 할 수 있다.

<jsp:useBean id="person" class="foo.Person" scope="request"></jsp:useBean>
Person created by servlet : <jsp:getProperty name="person" property="name"/>

위에서 id로 지정한 것을 아래 getProperty에서 name으로 가져다 쓸 수 있다.
property의 name은 자바빈의 getter, setter에서 사용하는 값이다. 즉 name은 getName()과 같은 기능을 하게 되는 것이다.
class는 패키지 명까지 다 써야한다.

getProperty가 있다면 당연히 setProperty도 있다.
setProperty는 다음과 같이 사용할 수 있다.

<jsp:setProperty name="person" property="name" value="Fred"/>

setProperty를 useBean 안에 사용하게 되면, 새로운 빈이 만들어지는 경우에만 설정하고, 이미 있는 경우에는 설정하지 않게 된다.
즉 다음과 같이 코딩하게 되면 빈이 없을 경우에만 값을 설정하게 된다.

<jsp:useBean id="person" class="foo.Person" scope="request">
    <jsp:setProperty name="person" property="name" value="Fred"/>
</jsp:useBean>

위와 같이 코딩하게 되면 컨테이너가 자동으로 생성해 주는 _jspService() 메소드는 다음과 같은 형식을 가지게 되는 것이다.

foo.Person person = null;
person = (foo.Person) _jspx_page_context.getAttribute("person", PageContext.PAGE_SCOPE);

if(person == null) {
    person = new foo.Person();
    _jspx_page_context.setAttribute("person", person, PageContext.PAGE_SCOPE);
   
    org.apache.jasper.runtime.JspRuntimeLibrary.JspRuntimeLibrary.introspecthelper(
            _jspx_page_context.findAttribute("person"), "name", "Fred", null, null, false);
}

없다면 해당 객체를 만들어서 할당하고, 값을 추가 한다.
아래 "org.apache.jasper.runtime.JspRuntimeLibrary..." 부분은 컨테이너가 자동으로 생성하는 코드이므로 어떤 식으로 처리되는지 자세히 알 필요는 없다.

* 다형성 사용하기

빈 참조에 대해서도 다형성 기법을 사용할 수 있다.
가령 foo.Person이 추상 객체이고 Employee객체가 이를 상속 받았다면 아래와 같은 형식으로 쓸 수 있다.

<jsp:useBean id="person" type="foo.Person" class="foo.Employee" scope="page"></jsp:useBean>

type에는 클래스, 추상클래스, 인스턴스 등이 들어갈 수 있으며, class에는 type에 명기한 클래스를 상속한 것이거나 구현한 클래스가 들어가야 한다.

* HTML에서 JSP로 form 전송을 바로 하기

html에서 jsp로 바로 form을 전송하게 될 경우가 있다.

<form action="TestBean.jsp">
    name : <input type="text" name="userName">
    ID# : <input type="text" name="userID">
</form>

위와 같이 하게 되면 jsp에서는 어떻게 처리해야 할까?
request.getParameter를 사용하여 setProperty의 value 속성에 넣을 수도 있겠지만 param 속성을 이용하면 더 쉽게 빈에 값을 설정할 수 있다.

<jsp:useBean id="person" type="foo.Person" class="foo.Employee">
    <jsp:setProperty name="person" property="name" param="userName"/>
</jsp:useBean>

html에서 userName이라고 설정을 하였다면 param에 userName이라고 넣어주게 되면 name이란 빈 프로퍼티에 값이 설정되게 된다.
param이란 속성을 이용하는 것보다 더 간단한 방법이 있다.
html에서 form element의 이름이 빈 프로퍼티의 이름과 같게 되면 자동으로 들어가게 된다.
즉 다음과 같으면 param이란 속성을 안 적어 줘도 된다.

<form action="TestBean.jsp">
    name : <input type="text" name="name">
    ID# : <input type="text" name="userID">
</form>

<jsp:useBean id="person" type="foo.Person" class="foo.Employee">
    <jsp:setProperty name="person" property="name"/>
</jsp:useBean>

만일 빈의 모든 프로퍼티와 html form 객체의 모든 이름이 같다면 더욱더 간단하게 아래와 같이 할 수 있다.

<jsp:useBean id="person" type="foo.Person" class="foo.Employee">
    <jsp:setProperty name="person" property="*" />
</jsp:useBean>






신고

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

EL 함수 사용하기  (0) 2009.02.13
EL 사용하기  (3) 2009.02.12
표준 액션을 사용한 JSP  (0) 2009.02.11
JSP 초기화 하기  (0) 2009.02.10
스크립틀릿에서 변수 선언하기  (0) 2009.02.04
간단한 Jsp 시작하기  (0) 2009.02.04

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

받은 트랙백이 없고 , 댓글이 없습니다.
secret
ServletConfig를 web.xml에 정의해 두고 나서 서블릿에서 가져다 쓰는 방법에 대해 http://www.4te.co.kr/540 에서 다루었었다.
JSP에서도 마찬가지로 초기화를 통해서 web.xml에 있는 ServletConfig를 변수처럼 사용할 수 있다.
방법은 서블릿 생명 주기에서 가장 처음에 한번만 호출하는 메소드인 jspInit() 메소드를 오버라이드 해서 사용하면 되는 것이다.
사용 방법은 아래와 같다.

  <servlet>
      <servlet-name>MyTestInit</servlet-name>
      <jsp-file>/TestInit.jsp</jsp-file>
      <init-param>
          <param-name>email</param-name>
          <param-value>ikickedbutt@wickedlysmart.com</param-value>
      </init-param>
  </servlet>
 
  <servlet-mapping>
      <servlet-name>MyTestInit</servlet-name>
      <url-pattern>/TestInit.jsp</url-pattern>
  </servlet-mapping>

위와 같이 설정을 하면 된다.
다만 servlet-class가 아니라 jsp-file 이라는 태그를 사용하게 되며, 패키지 형식이 아니라 디렉토리 형식으로 표시한다.
TestInit.jsp 파일은 아래와 같이 작성한다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%!
public void jspInit() {
    ServletConfig sConfig = getServletConfig();
    String emailAddr = sConfig.getInitParameter("email");
    System.out.println("emailAddr : " + emailAddr);
    ServletContext ctx = getServletContext();
    ctx.setAttribute("mail", emailAddr);
}
%>
<!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>
<%= pageContext.getAttribute("mail", PageContext.APPLICATION_SCOPE) %>
</body>
</html>

ServletConfig에서 email 값을 읽어온 후 ServletContext에 setting 하고 있다.
그 후 pageContext를 이용하여 ServletContext를 읽어와 화면에 뿌려주고 있다.

참고로 page생존범위를 셋팅하고 읽어오는 방법은 아래와 같다.

<% Float one = new Float(42.5); %>
<% pageContext.setAttribute("foo", one); %>
<%= pageContext.getAttributesScope("foo") %>

<% Float two = new Float(22.4); %>
<% pageContext.setAttribute("foo", two, PageContext.SESSION_SCOPE); %>
<%= pageContext.getAttribute("foo", PageContext.SESSION_SCOPE) %>

setAttribute와 getAttribute 메소드는 인자를 String 하나만 받는 경우와, String과 int 두개를 받는 것이 있다.
인자가 하나인 경우에는 pagecontext 객체의 속성에 저장된 것을 읽지만, 두개인 것은 지정한 범위에 있는 속성을 읽게 된다.

TestInit.jsp에서 사용한 "<%= pageContext.getAttribute("mail", PageContext.APPLICATION_SCOPE) %>"는 다음과 같이 사용할 수도 있다.

<%= application.getAttribute("mail") %>

scope의 범위를 PageContext.APPLICATION_SCOPE로 주는 대신에 application으로 접근해서 동일한 효과를 얻게 되는 것이다.
생존 범위가 어디인지 모르는 경우에 속성을 찾기 위해서는 다음과 같이 사용할 수 있다.

<%= pageContext.findAttribute("foo") %>

위와 같이 하게 되면 제일 처음으로 page 생존 범위에서 찾는다. page 생존 범위에 없다면 가장 작은 범위에서부터 시작해서 가장 넓은 범위로 속성을 찾게 된다.
즉 request => session => application 범위로 찾게 된다.
아래와 같이 하게 되면 결과 값은 42.5가 나오게 된다.

<% Float one = new Float(42.5); %>
<% pageContext.setAttribute("foo", one); %>
<%= pageContext.getAttributesScope("foo") %>

<% Float two = new Float(22.4); %>
<% pageContext.setAttribute("foo", two, PageContext.SESSION_SCOPE); %>
<%= pageContext.getAttribute("foo", PageContext.SESSION_SCOPE) %>

<%= pageContext.findAttribute("foo") %> => 42.5가 나옴


신고

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

EL 사용하기  (3) 2009.02.12
표준 액션을 사용한 JSP  (0) 2009.02.11
JSP 초기화 하기  (0) 2009.02.10
스크립틀릿에서 변수 선언하기  (0) 2009.02.04
간단한 Jsp 시작하기  (0) 2009.02.04
리스너 예제  (2) 2009.02.03

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

받은 트랙백이 없고 , 댓글이 없습니다.
secret
다음과 같이 jsp를 작성해서 방문자의 숫자를 세어 보는 코드를 만들 생각을 할 수 있다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!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>
<% int count = 0; %>
The page count is  now:
<%= ++count %>
</body>
</html>

이렇게 하게 되면 화면에 "The page count is  now:1" 이라는 메시지가 찍힐 것이다.
하짐나 브라우져를 리프레쉬 하게 되면 count가 2가 되는 것이 아니라 여전히 1이 된다.
왜냐면 위에서 선언한 count란 변수는 지역 변수로 작용하기 때문이다.

즉 jsp는 서블릿으로 변하면서 그 모든 코드가 _jspService 메소드 안에 작성 되는데 count란 변수 역시 _jspService 메소드 안에서 정의 된다는 뜻이다.
jsp 코드가 서블릿으로 변환 되게 된다면 아래와 같은 코드를 가지게 될 것이다.

    public void _jspService(HttpServletRequest request, HttpServletResponse response)
                    throws IOException, ServletException {
        PrintWriter out = response.getWriter();
        response.setContentType("text/html");
        int count = 0;
        out.println(++count);
    }

변환된 서블릿이 실행 될 때마다 _jspService 메소드는 실행 될 것이며 그때마다 count 변수는 0으로 셋팅 될 것이다.
따라서 누적된 방문자 수를 가지기 위해서는 매번 실행 되지 않는 곳에 count 변수를 위치하게 하면 될 것이다.
즉 _jspService 메소드 위로 count 변수를 빼면 된다.
이럴 경우에는 jsp 선언문을 사용해서 count가 지역 변수가 아닌 인스턴스 변수가 되도록 만들면 된다.

<%! int count = 1; %>

위에서 볼 수 있듯이 앞에 "!"(느낌표) 하나를 붙여주면 된다.
이렇게 되면 서블릿에서는 다음과 같은 방법으로 코딩한 것과 같은 효과를 주게 된다.

    int count = 0;
    public void _jspService(HttpServletRequest request, HttpServletResponse response)
                    throws IOException, ServletException {
        PrintWriter out = response.getWriter();
        response.setContentType("text/html");
        out.println(++count);
    }

"!"는 변수 뿐 아니라 메소드에도 사용할 수 있다.



신고

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

표준 액션을 사용한 JSP  (0) 2009.02.11
JSP 초기화 하기  (0) 2009.02.10
스크립틀릿에서 변수 선언하기  (0) 2009.02.04
간단한 Jsp 시작하기  (0) 2009.02.04
리스너 예제  (2) 2009.02.03
HttpSessionBindingListener와 HttpSessionAttributeListener  (0) 2009.02.02

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

받은 트랙백이 없고 , 댓글이 없습니다.
secret
Jsp를 작성하게 되면 jsp는 컨테이너에 의해서 서블릿으로 변형되게 된다.
간단하게 얼마나 많은 요청을 받았는지를 출력하는 jsp를 작성해 보면 다음과 같다.

Counter.java

package com.example;

public class Counter {
    private static int count;
    public static synchronized int getCount() {
        count++;
        return count;
    }
}

BasicCounter.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.example.*" %>
<!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>
<%
    out.println(Counter.getCount());
%>
</body>
</html>

Counter 클래스에서 static int 변수를 두어서 요청한 횟수를 저장할 변수를 만든다.
BasicCounter.jsp에서는 page 지시자를 사용하여 com.example.*을 import 한 후 Counter 객체를 사용해서 카운트를 표시한다.(page 지시자 끝에는 세미콜론이 없다.)

위 코드와 같이 표시하는 경우를 스크립틀릿 코드라 하며 표현식 코드로 변환해서 사용해도 무방하다.
표현식 코드는 다음과 같이 사용한다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.example.*" %>
<!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>
<%= Counter.getCount() %>
</body>
</html>

표현식 코드 역시 지시자와 마찬가지로 세미콜론을 사용하지 않는다.

신고

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

JSP 초기화 하기  (0) 2009.02.10
스크립틀릿에서 변수 선언하기  (0) 2009.02.04
간단한 Jsp 시작하기  (0) 2009.02.04
리스너 예제  (2) 2009.02.03
HttpSessionBindingListener와 HttpSessionAttributeListener  (0) 2009.02.02
Cookie 사용하기  (0) 2009.02.02

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

받은 트랙백이 없고 , 댓글이 없습니다.
secret
1. 세션 카운터

package com.example;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class BeerSessionCounter implements HttpSessionListener {
    static private int activeSessions;

    public static int getActiveSessions() {
        return activeSessions;
    }

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
        activeSessions++;
        System.out.println("activeSessions : " + activeSessions);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
        activeSessions--;
        System.out.println("activeSessions : " + activeSessions);
    }
}

  <listener>
      <listener-class>
          com.example.BeerSessionCounter
      </listener-class>
  </listener>

위와 같이 하게 되면 웹 애플리케이션에 있는 활성화된 세션의 개수를 확인할 수 있다.

2. 속성 리스터

package com.example;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class BeerAttributeAdded implements HttpSessionAttributeListener {

    @Override
    public void attributeAdded(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
        String name = arg0.getName();
        Object value = arg0.getValue();
       
        System.out.println("Attribute added : " + name + ": " + value);
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
        String name = arg0.getName();
        Object value = arg0.getValue();
       
        System.out.println("Attribute removed : " + name + ": " + value);
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
        String name = arg0.getName();
        Object value = arg0.getValue();
       
        System.out.println("Attribute replaced : " + name + ": " + value);
    }

}

  <listener>
      <listener-class>
          com.example.BeerAttributeAdded
      </listener-class>
  </listener>

위와 같이 하게 되면 속성이 세션에 추가, 제거, 대체 되는 경우를 리슨 할 수 있다.
System.out.printl으로 출력하게 되면 톰캣의 경우 catalina.out 파일에 로그로 쌓이게 된다.

3. 속성 클래스

package com.example;

import java.io.Serializable;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;

public class Dog implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable {
    private String breed;
   
    public Dog(String breed) {
        this.breed = breed;
    }
   
    public String getBreed() {
        return breed;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void sessionWillPassivate(HttpSessionEvent arg0) {
        // TODO Auto-generated method stub
       
    }
}

  <listener>
      <listener-class>
          com.example.Dog
      </listener-class>
  </listener>

valueBound, valueUnbound는 앞에서도 설명했듯이 자신이 세션에 들어가고 나오는 것을 리슨 하는 것이다.
반면에 sessionDidActivate, sessionWillPassivate는 새로운 가상머신으로 옮겨 가더라도 세션이 살아 있게 만드는 경우에 리슨 하는 것이다.
sessionWillPassivate는 새로운 가상머신으로 옮겨 가기 전에 직렬화 되지 않은 필드들을 읽어 들이는 부분이고, sessionDidActivate는 새로운 가상머신으로 옮겨 간 후 필드들을 원래대로 돌려 놓는(sessionWillPassivate와 반대) 부분이다.

따라서 HttpSessionBindingListener 부분은 DD(web.xml)에 설정할 필요가 없으며, HttpSessionActivationListener는 web.xml에 설정해야 한다.



신고

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

스크립틀릿에서 변수 선언하기  (0) 2009.02.04
간단한 Jsp 시작하기  (0) 2009.02.04
리스너 예제  (2) 2009.02.03
HttpSessionBindingListener와 HttpSessionAttributeListener  (0) 2009.02.02
Cookie 사용하기  (0) 2009.02.02
Session 관리  (0) 2009.01.30

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

받은 트랙백이 없고 , 댓글  2개가 달렸습니다.
secret
HttpSessionAttributeListener는 세션에 어떤 속성이 추가, 제거, 수정되는 이벤트가 발생하는지 알고 싶을 때 사용한다. 반면에 HttpSessionBindingListener는 자신이 세션에 속성으로 추가되었는지, 제거 되었는지 알기 위하여 사용한다.

package com.example;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class Dog implements HttpSessionBindingListener {
    private String breed;
   
    public Dog(String breed) {
        this.breed = breed;
    }
   
    public String getBreed() {
        return breed;
    }

    @Override
    public void valueBound(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
       
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent arg0) {
        // TODO Auto-generated method stub
       
    }
}

valueBound는 세션에 추가 되었을 때 실행되는 메소드이며, valueUnbound는 세션에서 제거 되었을 때 실행되는 메소드이다.
Dog 객체가 세션에 추가 되거나 제거 될 때를 위와 같이 구성하게 되면 알 수 있게 된다.

신고

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

간단한 Jsp 시작하기  (0) 2009.02.04
리스너 예제  (2) 2009.02.03
HttpSessionBindingListener와 HttpSessionAttributeListener  (0) 2009.02.02
Cookie 사용하기  (0) 2009.02.02
Session 관리  (0) 2009.01.30
스레드 안전한 ServletContext 만들기  (1) 2009.01.29

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

받은 트랙백이 없고 , 댓글이 없습니다.
secret
웹 프로그래밍을 하다 보면 자연스럽게 나오게 되는 부분이 쿠키(Cookie)이다.
Java에서도 쿠키를 사용할 수 있는데, 다음과 같은 방식으로 사용하면 된다.

1. Cookie 객체 생성
Cookie cookie = new Cookie("username", name);

2. Cookie 생명 주기 설정
cookie.setMaxAge(30*60);  //30분...

3. Response에 Cookie 추카
response.addCookie(cookie);

4. Request에서 Cookie 일어오기
Cookie[] cookies = request.getCookies();
for(int i = 0 ; i<cookies.length ; i++) {
  Cookie cookie = cookies[i];
  if(cookie.getName().equals("username")) {
    String userName = cookie.getValue();
    out.println("Hello " + userName);
    break;
  }
}

예제

1. 쿠키를 생성하고 설정하는 서블릿

package com.example;

import java.io.IOException;

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

public class CookieTest extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                    throws IOException, ServletException {
        response.setContentType("text/html");
        String name = request.getParameter("username");
       
        Cookie cookie = new Cookie("username", name);
        cookie.setMaxAge(30*60);
        response.addCookie(cookie);
       
        RequestDispatcher view = request.getRequestDispatcher("cookieresult.jsp");
        view.forward(request, response);
    }
}

2. 간단한 사용자 정의 쿠키 예제

package com.example.web;

import java.io.IOException;
import java.io.PrintWriter;

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

public class CheckCookie extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                    throws IOException, ServletException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
       
        Cookie[] cookies = request.getCookies();
       
        for(int i=0 ; i<cookies.length ; i++) {
            Cookie cookie = cookies[i];
            if(cookie.getName().equals("username")) {
                String userName = cookie.getValue();
                out.println("Hello " + userName);
                break;
            }
        }
    }
}




신고

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

리스너 예제  (2) 2009.02.03
HttpSessionBindingListener와 HttpSessionAttributeListener  (0) 2009.02.02
Cookie 사용하기  (0) 2009.02.02
Session 관리  (0) 2009.01.30
스레드 안전한 ServletContext 만들기  (1) 2009.01.29
ServletContext 이용하기  (2) 2009.01.23

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

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