Multiple Parameter

 | JSP
2012. 5. 8. 18:17

MyBatis 3.0의 예제를 보면 파라미터를 하나 던지는 예제만 줄줄 있습니다.
그 타입이 primitive 이던, POJO 형태든지 말이죠.

 

여러 개의 파라미터를 던지려면 @Param 어노테이션을 사용하고, map으로 xml 파일에 정의해 주면 됩니다.

 

1. 먼저 Mapper Interface에 메소드를 정의합니다.

public Map selectMachineAgentMap(@Param("machineId") int machineId, @Param("agentId") int agentId);


2. mapper.xml 파일에 paramterType을 map으로 잡습니다.

<select id="selectMachineAgentMap" parameterType="map" resultType="map">
 SELECT MACHINE_ID, AGENT_ID
 FROM M_MACHINE_AGENT_MAP_TBL
 WHERE MACHINE_ID = #{machineId} AND AGENT_ID = #{agentId}
  </select>



위 와 같이 하면 MyBatis는 맵 객체를 생성하고 거기에 @Param에 해당하는 이름으로 전달받은 인자를 저장합니다. 이후 프로세서에서는 #{}의 이름에 해당하는 key를 이용하여 데이터를 추출하고 컬럼의 메타데이터에 해당하는 타입으로 캐스팅하여 쿼리를 날리게 됩니다.

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

PAGING(sql)  (0) 2012.05.08
동적쿼리문  (0) 2012.05.08
log4j구현환경제공.  (0) 2012.05.08
MyBatis (odbc프레임웍)  (0) 2012.05.08
MVC패턴  (0) 2012.05.08
Posted by 사라링

log4j구현환경제공.

 | JSP
2012. 5. 8. 18:17

log4j.jpg

그림과 과 같은 구조에 주의 하자.

res 라는 clasess  폴더 안의 패키지 kr.or.ddit.mybatis 안에 마이바티스팩토리와  config.xml  이 구조화 되어있다.

log4j.properties 라는 프로 퍼티스 파일을 res 즉 클래스시스 루트단에 생성 한후.

  1. 전역 로깅 설정
    log4j.rootLogger=DEBUG, stdout, file
    #log4j.rootLogger=ERROR, file
    # MyBatis 로깅 설정...
    #log4j.logger.org.apache.ibatis=DEBUG
    log4j.logger.java.sql.Connection=DEBUG
    #log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    # Console 출력...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    file 이름의 appender 설정
    log4j.appender.file=org.apache.log4j.DailyRollingFileAppender

    log4j.appender.file.Threshold=INFO
    log4j.appender.file.File=C:/MyTest.log
    log4j.appender.file.DatePattern='.'yyyy-MM-dd'.log'
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%-5p %d{ISO8601} %l] %m%n

 

의 내용을 적어 놓으면 . 자동으로 설정 된다. log4j 의 경우 마이바티스 에서 자동으로 리포팅 하여 설정 되는듯 하다.

내부의 자세한 내용은 JSP 단의 log4j  에서 확인 하자.

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

동적쿼리문  (0) 2012.05.08
Multiple Parameter  (0) 2012.05.08
MyBatis (odbc프레임웍)  (0) 2012.05.08
MVC패턴  (0) 2012.05.08
Login&Logout 기본패턴  (0) 2012.05.08
Posted by 사라링

MyBatis (odbc프레임웍)

 | JSP
2012. 5. 8. 18:16

마이바티스

http://code.google.com/p/mybatis/downloads/detail?name=mybatis-3.1.1-bundle.zip&can=3&q=%22mybatis-3%22+-migrations

위의 링크로 들어가 jar 파일을 다운받은후 추가 하자.

신세계 마이바티스를 공부해 보자. (아이바 티스를 안보신분은 먼저 그것부터 보길 바란다 ) http://sararing.springnote.com/pages/10976942

MyBatis-3-User-Guide_ko.pdf  <== 마이바 티스 한글 유저 가이드 .

 

먼저 마이바티스란. odbc 프레임웍으로 웹페이지 에서 필요한 디비를 최대한 능동적으로 (빠른 접속시간및 코드의 효율 최상화)를 위해 사용 하게 된다.

먼저 우리가 기본적으로 생성하게 되는  MemberDaoOracleMpl.java 파일을 보자

findByPk 메서드를  중심으로 설명해 보겠다.

기존의_findByPk.jpg

rs 생성후 파라미터 연결 또 보드객체 생성 받고 rs psmt conn 닫고 ./ 여기서 이러한 코드들이 거의 의미 없이 무제한 적으로 사용 하게 되는것에 의문을 가질수 있다.

실제로 쿼리 문장은 매우 규칙적이고 심플 하기 때문에 이러한 의미 없는 코딩을 줄이고 풀링및 접속 환경 까지 제어 해주는것이 마이바티스 이다.

위와 같은 코드식을

변경후의_finByPK.jpg

이렇게 만들었다.  DAO 의 인터페이스인 IMDAO를 implement 했다.  이렇든 간단하게 형식에 맞추어 구현이 가능한 것이 마이바 티스 이다.

 

사용에 먼저 가장 먼저 할일은

res.jpg res 소스 폴더를 만든다 이후에 kr.or.ddit.mybatis 패키지를 생성후 패키지의 루트에  config.xml 파일을 생성 하자.

마이바티스 기본 설정 이다.   jar 파일은 이미 추가 했다는 전제 이다. 

config.jpg

config.xml 의 소스 내용 이다. 중요한 부분으로는 typeAliases  를 볼수 있다 . 위 소스를 보면 MemberBean 및 BoardBean 과 같은 bean 파일을 type 으로 지정 하여

애칭을 주며. 차후에 factory 등에서 쉽게   kr.or.ddit.member.vo.MemberBean  를  member 라고 적어 구현 할수 있다. 

여기서 중간 내용을 적용 하여 dataSource pooled 에서 드라이브및 url username password 등을 적어 구현 한다.

밑단에 보면 mappers 를 확인해 보자. 이하 매핑 이라고 하면 mappers 라고 알고 있자

mapper 의 리소스에는 mapping 에 각기 member.xml board.xml 이 있다 이이곳에서 실질적인 데이터베이스에 필요한 sql 문을 구현 하게 된다.

memberxmlfinbypk.jpg

위 사진은 member.xml 구현부 이다. 여기서 namespace 를 DAO 에 잡으며 반드시 select 문의 id 값은 DAO 에서 구현한 메서드의 이름과 같아야 자동 매칭이 가능하다.

구문 에서 parameterType="String" resultType="member"  메서드 실행시 파라미터 의 종류로 String 과 돌려주는 값이 member 임을 알수 있다.

      여기서 member는 위에서 알리아스(애칭)을 적용 한값을 사용 함을 알수 있다.

받은 값에 대하여 마치 파라미터를 받는것과 마찬 가지로 #{mem_id}  등의 형식으로 적어  member 객체로 받아     MemberDaoMyBatis.java 에서 구현 한다.

DAOmybatis.jpg

위의 사진에서 MemberDaoMyBatis.java  의 구현 사진이다. 실질적으로 MemberServiceIMpl.java 에서는 이 명령을 구현 하게 된다 . session factory 구현에 메퍼를 사용함을

주의 깊게 보자. openSession파라미터  true commit()을 의미 하니 셀렉트 문에는 구지 안넣어도 되나 db 값이 변화 하는 경우 반드시 추가 해야만 한다. 

board구현.png

위의 사진에서 보면 실질적으로 jsp 단에서 파일을 생성 구현 하는 부분은 두 부분이다. 먼저 직접적인 생성및 사용하게 되는 BoardServiceIMple 이 있다.이곳 에서 일차 적으로 jsp 에서 사용 하게 되며

service 에서는 두가지 선택이 가능 하다. BoardDaoOracle 과 BoardDaoMybatis 이다. 기본적인 factory 단을 이용 하여 구현 하는게 훨신 간단하게 소스구현을 할수 있다는것을 알수 있다.

추가로 member의 UML 도 확인하자.

member구현.png

 

 

소스

 

 

config.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <typeAlias alias="member" type="kr.or.ddit.member.vo.MemberBean"/>
        <typeAlias alias="board" type="kr.or.ddit.board.vo.BoardBean"/>
   
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:xe"/>
                <property name="username" value="hun"/>
                <property name="password" value="java"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="kr/or/ddit/mapping/member.xml"/>
        <mapper resource="kr/or/ddit/mapping/board.xml"/>
    </mappers>
</configuration>

MyBatisFactory.java

package kr.or.ddit.mybatis;

import java.io.IOException;
import java.io.Reader;

import javax.management.RuntimeErrorException;

import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.io.Resources;

public class MyBatisFactory {

    private static SqlSessionFactory sqlMapper;
    static{
       
        try {
            String resource = "kr/or/ddit/mybatis/config.xml";
            Reader reader = Resources.getResourceAsReader(resource);
            sqlMapper = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(),e);
        }

    }
    public static SqlSessionFactory getSqlMapper() {
        return sqlMapper;
    }
   
}

member.xml (member 의 매핑 파일)|| 실제 sql문 구현

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.board.dao.IBoardDao">
    <cache eviction="LRU" flushInterval="600000" size="512" readOnly="true" />

    <select id="findAllByPage" parameterType="map" resultType="board">

        select *
        from (select rownum rn,a.*
        from (select *
        from board
       
        order by bo_no desc
        )a
        where <![CDATA[ rownum <= #{endRow}]]>
        )
        where <![CDATA[rn >= #{startRow}]]>


    </select>
    <select id="findByPk" parameterType="int" resultType="board">
        select * from BOARD where bo_no=#{bo_no}
    </select>
    <select id="getBoardCount" resultType="int" parameterType="map">
        select count(*) from board
    </select>
    <insert id="insertBoard" parameterType="board" >
       
        INSERT INTO board (bo_no,bo_title,bo_writer,bo_passwd,bo_email,bo_ip,
        bo_content,bo_hit,bo_date)
         VALUES(board_seq.nextval,#{bo_title}, #{bo_writer}, #{bo_passwd}, #{bo_email}, #{bo_ip},
        #{bo_content}, 0, sysdate)

    </insert>
    <update id="hitCount" parameterType="board">
        update board set
        BO_HIT =#{bo_hit}
        where BO_NO=#{bo_no}
    </update>
    <update id="updateBoard" parameterType="board">
        update board set  
        BO_TITLE =#{bo_title}, 
        BO_WRITER =#{bo_writer},
        BO_EMAIL =#{bo_email} ,
        BO_IP =#{bo_ip} ,
        BO_CONTENT =#{bo_content}
        where BO_NO=#{bo_no}
</update>

</mapper>

board.xml (mapper 구현부)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.or.ddit.member.dao.IMemberDao">
    <select id="findByPk" parameterType="String" resultType="member">
        select * from member where mem_id= #{mem_id}
    </select>
   

    <select id="findByAll" resultType="member">
        select * from member
    </select>
    <insert id="insertMember" parameterType="member">
        INSERT INTO MEMBER
        (MEM_ID,MEM_PASS, MEM_NAME,MEM_REGNO1,MEM_REGNO2,MEM_BIR,
         MEM_ZIP, MEM_ADD1, MEM_ADD2, MEM_HOMETEL, MEM_COMTEL, MEM_HP,
        MEM_MAIL, MEM_JOB, MEM_LIKE, MEM_MEMORIAL,MEM_MEMORIALDAY,
          MEM_MILEAGE, MEM_DELETE)
         VALUES
        (#{mem_id},#{mem_pass}, #{mem_name},#{mem_regno1},#{mem_regno2},#{mem_bir},
         #{mem_zip}, #{mem_add1}, #{mem_add2}, #{mem_hometel}, #{mem_comtel}, #{mem_hp},
        #{mem_mail}, #{mem_job}, #{mem_like}, #{mem_memorial},#{mem_memorialday},0,'N' )

    </insert>
    <update id="updateMember" parameterType="member">
   
              update MEMBER set  
              MEM_NAME       =#{mem_name},
              MEM_REGNO1      =#{mem_regno1},
              MEM_REGNO2      =#{mem_regno2},
              MEM_BIR         =#{mem_bir},
              MEM_ZIP          =#{mem_zip},
                           
              MEM_ADD1       =#{mem_add1},
              MEM_ADD2       =#{mem_add2},
              MEM_HOMETEL     =#{mem_hometel},
              MEM_COMTEL      =#{mem_comtel},
              MEM_HP         =#{mem_hp},
                           
              MEM_MAIL         =#{mem_mail},
              MEM_JOB        =#{mem_job},
              MEM_LIKE           =#{mem_like},
              MEM_MEMORIAL       =#{mem_memorial},
              MEM_MEMORIALDAY =#{mem_memorialday},
                                   
              MEM_MILEAGE  =#{mem_mileage},
              MEM_DELETE      =#{mem_delete}
              where MEM_ID     =#{mem_id}
             
             
   
    </update>
    <select id="findByJob" resultType="member">
        select * from member
        <where>
        <if test="array !=null">
            <foreach collection="array" item="item" open=" mem_jop in(" close=")" separator=",">
                #{item}
            </foreach>
        </if>
        </where>
    </select>

</mapper>

 

 

BoardDaoMyBatise.java

package kr.or.ddit.board.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import kr.or.ddit.board.vo.BoardBean;
import kr.or.ddit.mybatis.MyBatisFactory;

public class BoardDaoMyBatis implements IBoardDao {

    private SqlSessionFactory factory = MyBatisFactory.getSqlMapper();
   
    @Override
    public int insertBoard(BoardBean board) {
        SqlSession session = factory.openSession(true);
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.insertBoard(board);
        } finally{
            session.close();
        }
    }

    @Override
    public int updateBoard(BoardBean board) {
        SqlSession session = factory.openSession(true);
       
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.updateBoard(board);
        } finally{
            session.close();
        }
    }

    @Override
    public int deleteBoard(BoardBean board) {
        SqlSession session = factory.openSession();
       
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.deleteBoard(board);
        } finally{
            session.close();
        }
    }

    @Override
    public int getBoardCount(Map<String, Object> paramMap) {
        SqlSession session = factory.openSession();
       
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.getBoardCount(paramMap);
        } finally{
            session.close();
        }
    }


    @Override
    public List<BoardBean> findAllByPage(Map<String, Object> paramMap) {
        SqlSession session = factory.openSession();
       
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.findAllByPage(paramMap);
        } finally{
            session.close();
        }
    }

    @Override
    public BoardBean findByPk(int bo_no) {
        SqlSession session = factory.openSession();
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.findByPk(bo_no);
        } finally{
            session.close();
        }
    }

    @Override
    public int hitCount(BoardBean board) {
        SqlSession session = factory.openSession(true);
        try {
            IBoardDao boardDao = session.getMapper(IBoardDao.class);
            return boardDao.hitCount(board);
        } finally{
            session.close();
        }
    }

}

MemberDaoMyBatis.java

package kr.or.ddit.member.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import kr.or.ddit.member.vo.MemberBean;
import kr.or.ddit.mybatis.MyBatisFactory;

public class MemberDaoMyBatis implements IMemberDao {

    private SqlSessionFactory factory = MyBatisFactory.getSqlMapper();

    @Override
    public int insertMember(MemberBean member) {
       
//         factory.openSession(true);   오토코밋 가능
        SqlSession session = factory.openSession(true);
        try {
            //return session.insert("kr.or.ddit.member.dao.IMemberDao.insertMember",member);
            // 위에엇을 밑에 것으로 변환 해서 사용.
            IMemberDao dao = session.getMapper(IMemberDao.class);
            return dao.insertMember(member);
        } finally {
            session.close();
            // session 을 close 하면서 dao를 null로 돌려 주므로 IMemberDao 를 전역 변수로 사용 하지 않아도 된다..
        }
    }
    @Override
    public int updateMember(MemberBean member) {
       
//         factory.openSession(true);   오토코밋 가능
        SqlSession session = factory.openSession(true);
        try {
            IMemberDao dao = session.getMapper(IMemberDao.class);
            return dao.updateMember(member);
           
            //return session.update("kr.or.ddit.member.dao.IMemberDao.updateMember",member);
        } finally {
            session.close();
        }
    }

    @Override
    public int deleteMember(String mem_id) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public List<MemberBean> findByAll() {
        SqlSession session = factory.openSession();
        try {
            //return session.selectList("kr.or.ddit.member.dao.IMemberDao.findByAll");
            IMemberDao dao = session.getMapper(IMemberDao.class);
            return dao.findByAll();
        } finally {
           
            session.close();
        }
    }
   
    @Override
    public MemberBean findByPk(String mem_id) {
        SqlSession session = factory.openSession(true);
        try {
            IMemberDao dao = session.getMapper(IMemberDao.class);
            return dao.findByPk(mem_id);
        } finally {
            session.close();
        }
    }

}

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

Multiple Parameter  (0) 2012.05.08
log4j구현환경제공.  (0) 2012.05.08
MVC패턴  (0) 2012.05.08
Login&Logout 기본패턴  (0) 2012.05.08
log4j  (0) 2012.05.08
Posted by 사라링

MVC패턴

 | JSP
2012. 5. 8. 18:16


그림 1 MVC Pattern Diagram


<그림 1>에서는 일반적인 MVC 패턴을 다이어그램으로 표현하고 있습니다.

MVC 패턴은 Model, View, Controller 세 개의 컴포넌트로 구성되는데 각 컴포넌트에서 담당하는 책임은 다음과 같습니다.


  • ViewUI 요소를 그려줍니다.
  • ControllerUI의 사용자 액션에 응답하고 데이터 흐름을 제어합니다.
  • ModelMVP 패턴의 Model과 마찬가지로 데이터와 상태를 유지하며 데이터 처리 로직을 포함합니다.


MVC 패턴이 MVP패턴과 다른 점은 Presenter 컴포넌트 대신 Controller 컴포넌트가 존재하는 것이죠. Controller는 사용자의 액션 - 예를 들면 버튼 컨트롤을 클릭하거나 키보드를 통해 텍스트를 입력 받는 행위들 - 에 반응하여 적절한 행동을 하는 코드를 포함합니다. 즉, 사용자의 액션에 반응하여 Model의 데이터를 변경하는 역할을 수행합니다.


MVC 패턴이 적용된 대표적인 사례는 MFC(CDocument <-> CView), ASP.NET(ASPX <-> Code Behind), WinForm.NET(Form & Control Design.cs<-> Form & Control .cs <-> Entity or Business Logic .cs) , JSP등이 있겠습니다. 특히 JSP에서는 (자바진영) View와 Controller가 합쳐진 Model 1과 View와 Controller를 명확히 구분하고 View와 Model 사이에 상호작용을 전담하는 역할로서 Controller를 정의합니다.


그림 2 JSP Model 1 architecture


그림 3 JSP Model 2 architecture


Model 1에서는 JSP에서 사용자 Request(Controller)와 Response(View)를 담당하고 Data source의 접근 처리는 JavaBean(Model)에서 담당하게 됩니다.

Model 2에서는 사용자 Request를 Servlet(Controller)에서 담당하고 Response는 JSP(View), Data source의 접근 처리는 JavaBean(Model)에서 담당하게 되죠.


이와 비슷한 모델로 최근 ASP.NET 3.5에서 발표될 MVC Framework를 들 수 있겠습니다. Request URL을 Controller가 분석해서 Model에 데이터를 쿼리(조회, 갱신)하고 적합한 View를 선택해서 Rendering하는 방식입니다.


문제는 View와 모델을 완전히 분리할 수 없다는 것이죠.

JSP의 Model2를 보셔도 JSP와 JavaBean과의 상호작용은 피할 수 없습니다. 역시 ASP.NET의 MVC Framework에서도 Controller에서 Model에 쿼리를 수행한 다음 반환된는 Entity(or Entity List)를 View에 바인딩할 때 Entity 자체를 넘겨주게 되므로 View에서 Model을 의존하게 되는 상황이 발생하게 되죠.


또한 MVC 패턴에서 Model에 연결된 View가 여럿일 경우 Model과 View간(또는 Model을 대신해서 Controller) Observer패턴을 적용하여 Model(or Controller)에서 View로 Notify하는 (Active Model) 방식이 사용되거나 반대로 View에서 Model을 polling(Passive Model)하는 방식이 수행됩니다.


그림 4 MVC passive model


그림 5 MVC active model


그림 6 Using observer pattern to decouple the model from view in the active model


이 프로세스를 살펴봐도 역시 View와 Model의 커플링은 제거하기 힘들어 보이는 군요.

(이런 논의도 있습니다. http://kldp.org/node/70219 )


애초에 MVC패턴이 나온 이유가 뭘까요?

바로 View와 Model의 분리입니다. 쉽게 말해 표현계층과 데이터( + 데이터 처리 로직)를 분리하여 데이터 처리 로직이 중복 코딩되는 것을 막고 로직과 엔티티(데이터)를 재 사용하는데 그 목적이 있습니다. 또한 GUI의 단위 테스트 코드를 작성할 수 있을지에 대한 기대감도 포함합니다.


하지만 MVC패턴에서는 View와 Model간의 의존성을 완벽히 분리할 수 없는데다 패턴의 모호함때문에 해석이 제각각 달라지며 여러가지 변형이 생기게 되었습니다.

그런 와중에 MVP패턴이 마틴 파울러에 의해서 발표되었는데 이 패턴에서는 View와 Model의 완벽한 분리가 이뤄지는 특징이 있습니다.

MVP 패턴에 대해서는 "MVP(Model-View-Presenter)패턴을 적용한 GUI Architecture 설계" 포스트를 참조하세요.

Trackback 0 Comment 0

MVP(Model-View-Presenter) 패턴을 적용한 GUI Architecture 설계



MVP(Model-View-Presenter) 패턴을 적용한 GUI Architecture 설계

1. MVP 패턴 소개

 

사용자 삽입 이미지

 

그림 1 MVP Pattern Diagram

MVP 패턴은 MVC(Model-View-Controller) 패턴에 기반을 둔 UI 프리젠테이션 패턴입니다.
MVP 패턴은 Model, View, View Interface, Presenter 네 개의 컴포넌트(구성요소)로 구성됩니다.
각 컴포넌트에서 담당하는 책임은 다음과 같습니다.

  • View
    View Interface의 Display 멤버(Properties, Display Methods)를 구현하여 실제적인 UI 요소를 그려줍니다.
  • View Interface
    Presenter에서 Concrete View를 직접 참조하지 않고 View Interface를 참조함으로써 Concrete View와의 커플링을 감소시키고 View의 실제 UI 요소가 어떻게 구현되는지 몰라도 데이터를 올바르게 표현할 수 있도록 합니다.
  • Presenter
    View와 Model간의 상호작용을 담당합니다. Model의 데이터를 View Interface를 통해 Concrete View에 출력(바인딩)해 주고 사용자의 이벤트를 View에서 구독하여(실제적인 이벤트 핸들러 구현) Model의 데이터를 갱신하는 역할을 수행합니다.
    Presenter를 통해 View와 Model간의 의존관계를 없앨 수 있습니다.
  • Model
    데이터와 상태를 유지하며 데이터 처리 로직을 포함합니다. 일반적으로 비즈니스 엔티티와 비즈니스 로직을 Model 컴포넌트로 간주합니다.

2. MVP 패턴 적용

위에서 소개한 MVP 패턴을 적용하여 샘플 어플리케이션을 만들어 보도록 하겠습니다.
샘플 어플리케이션의 Class Diagram은 다음과 같습니다.

 

사용자 삽입 이미지
그림 2 MVP Pattern이 적용된 샘플 어플리케이션의 Class Diagram

Model 컴포넌트는 Member Class이며 Member Entity와 데이터 처리 로직을 포함하고 있습니다.
Presenter 컴포넌트는 MemberPresenter Class이며 View와 Model을 참조합니다. 그리고 View의 이벤트 핸들러를 구현합니다.
View Interface 컴포넌트는 IMemberView Interface이며 View의 속성과 이벤트를 정의합니다.
Concrete View 컴포넌트는 MemberView Form이며 IMemberView를 구현하며 UI요소를 렌더링합니다.

먼저 Model 컴포넌트인 Member Class를 만들도록 합니다.
Member Class는 실제 데이터 소스에 접근하여 데이터를 검색하거나 갱신하는 로직을 포함합니다. 또한 Member의 Age, Name 속성을 갖는 Entity Class의 역할도 수행합니다.
데이터 소스는 실제 데이터베이스가 아닌 <표 1>과 같이 더미 데이터를 임시로 구현하여 사용합니다.

static class DummyMember
{
    #region 회원 더미 데이터 생성

    public static List<Models.Member> List = new List<Models.Member>();

    static DummyMember()
    {
        List.Add(new Models.Member("홍길동", 20));
        List.Add(new Models.Member("김갑수", 10));
    }

    #endregion

}

표 1 DummyMember Class

public int SaveMember(){
    #region 저장

    if (id == null)
    {
        // 추가
        Data.DummyMember.List.Add(new Member(this.name, this.age));
        id = Data.DummyMember.List.Count - 1;
    }
    else
    {
        // 수정
        Data.DummyMember.List[id.Value].Name = name;
        Data.DummyMember.List[id.Value].Age = age;
    }


    #endregion

    return 0;
}

public bool LoadMember(int id){
    if (id >= Data.DummyMember.List.Count || id < 0)
    {
        this.id = null;
        return false;
    }

    this.id = id;

    name = Data.DummyMember.List[id].Name;
    age = Data.DummyMember.List[id].Age;

    return true;
}

표 2 Member Class의 데이터 처리 메서드


다음으로 IMemberView Interface를 정의합니다.
IMemberView Interface는 Concrete View에서 구현해야할 UI 요소와 표현 메서드를 정의합니다. 일반적으로 UI요소는 Property로 정의하며 Concrete View의 UI 요소에서 이벤트가 발생했을 때 이를 Presenter에서 처리할 수 있도록 이벤트를 정의합니다.

interface IMemberView
{
    int ID { get; set; }
    string Name { get; set; }
    int Age { get; set; }

    event EventHandler LoadMember;
    event EventHandler SaveMember;
}

표 3 IMemberView Interface

세 번째로 UI 요소를 출력하는 MemberView Form을 생성합니다.MemberView Form은 System.Windows.Forms.Form Class를 상속하는 WinForm Class입니다. 닷넷의 WinForm Control이 UI 요소가 됩니다.
또한 IMemberView Interface를 구현하여 Presenter에서 UI 요소에 데이터를 채우거나 이벤트 핸들러를 구현하여 View를 제어할 수 있도록 합니다.

public partial class MemberView : Form, IMemberView{
    MemberPresenter presenter;

    public MemberView()
    {
        InitializeComponent();

        presenter = new MemberPresenter(this);        }

    #region IMemberView 멤버

    int IMemberView.ID
    {
        get { return (int)numericUpDown1.Value; }
        set { numericUpDown1.Value = value; }
    }

    string IMemberView.Name
    {
        get { return textBox1.Text; }
        set { textBox1.Text = value; }
    }

    int IMemberView.Age
    {
        get { return (int)numericUpDown2.Value; }
        set { numericUpDown2.Value = value; }
    }

    event EventHandler IMemberView.SaveMember
    {
        add { button1.Click += value; }
        remove { button1.Click -= value; }
    }

    event EventHandler IMemberView.LoadMember
    {
        add { button2.Click += value; }
        remove { button2.Click -= value; }
    }

    #endregion
}

표 4 MemberView Form

주목할 부분은 SaveMember, LoadMember 이벤트를 구현한 부분입니다. button1.Click과 button2.Click의 실제 이벤트 핸들러는 Presenter에서 구현하는데, 이 곳에서 Model의 데이터를 검색하고 갱신하는 코드를 작성하게 됩니다.

마지막으로 View와 Model의 상호작용을 처리하는 MemberPresenter Class를 구현합니다. MemberPresenter Class는 Concrete View의 UI 요소(WinForm Control)의 실제 이벤트 핸들러를 구현하며 Member Class의 데이터 처리 메소드를 호출해 데이터를 검색하거나 갱신하는 코드를 구현합니다. 또한 IMemberView 인터페이스를 통해 Concrete View인 MemberView Form의 UI 요소(WinForm Controls)에 데이터를 출력하는 코드도 구현하고 있습니다.

class MemberPresenter
{
    IMemberView view;
    Models.Member model;

    public MemberPresenter(IMemberView view)
    {
        this.view = view;
        this.view.SaveMember += new EventHandler(view_SaveMember);
        this.view.LoadMember += new EventHandler(view_LoadMember);

        this.model = new SingleLayerMVP.Models.Member();    }


// 회원정보 불러오기
    void view_LoadMember(object sender, EventArgs e)
    {
        int id = view.ID;

        if (model.LoadMember(id))
        {
            view.Name = model.Name;
            view.Age = model.Age;
        }
        else
        {
            MessageBox.Show("존재하지 않는 회원입니다.");

            view.Name = string.Empty;
            view.Age = 0;
        }
    }


    // 회원정보 저장하기
    void view_SaveMember(object sender, EventArgs e)
    {
        model.Name = view.Name;
        model.Age = view.Age;

        if (model.SaveMember() == 0)
        {
            MessageBox.Show("성공");
        }
        else
        {
            MessageBox.Show("실패");
        }
    }
}

MemberPresenter Class는 IMemberView와 Member의 참조를 모두 갖게 되며 view와 model간의 상호작용을 담당하는 코드를 실제 구현하게 됩니다. 이 로써 View와 Model간의 커플링을 없앰과 동시에 View와의 상호작용을 IMemberView Interface를 통해 처리함으로써 실제 Concrete View인 MemberView Form과의 커플링도 없앨 수 있게 되는 것입니다.

3. Multi Layer Application Architecture 에서 MVP Pattern 적용하기

 

사용자 삽입 이미지


그림 3 MVP Pattern Diagram with C/S Application

일반적으로 비즈니스 레이어와 Presentation Layer가 분리 되어 있는 어플리케이션에서 MVP Pattern의 적용은 <그림 3>과 같은 구조를 적용하면 됩니다. Model의 Business Entity는 직렬화가 가능하도록 [Serializable] 어트리뷰트를 추가하여 개발하고 Client Application에서는 Web Service참조를 Model로 간주하여 Presenter에 구현하면 됩니다.

[Serializable]public class Member
{
    int? id = null;

    public int? Id
    {
        get { return id; }
        set { id = value; }
    }

표 5 직렬화 가능한 Member Entity

class MemberPresenter
{
    IMemberView view;
    WebService.Service1 model;

    public MemberPresenter(IMemberView view)
    {
        this.view = view;
        this.view.SaveMember += new EventHandler(view_SaveMember);
        this.view.LoadMember += new EventHandler(view_LoadMember);

        this.model = new Client.WebService.Service1();    }


    // 회원정보 불러오기
    void view_LoadMember(object sender, EventArgs e)
    {
        int id = view.ID;

        WebService.Member member = model.wpLoadMember(id);

표 6 MemberPresenter Class에서 Web Service 참조를 Model로 참조하기

4. View를 위한 단위 테스트 코드 작성

우선 단위 테스트 코드는 NUnit Framework를 사용하도록 합니다.
NUnit Framework의 다운로드와 설치는 다음 링크를 참조하세요.
http://www.nunit.org

일반적으로 GUI에 대한 테스트는 사용자 액션(이벤트 발생)을 시뮬레이션하기 어렵기 때문에 자동화하기가 어렵습니다. 그래서 마우스와 키보드 동작을 레코드하여 매크로 동작으로 테스트하는 상용화된 테스팅 툴을 사용하게 됩니다.
하 지만 MVP 패턴을 적용하게 되면 IMemberView 인터페이스를 사용해서 View만을 위한 단위 테스트 코드를 작성할 수 있습니다. 이것이 의미하는 것은 GUI 개발을 테스트 지향적(TDD, 테스트 코드 먼저구현->실제 코드 구현)으로 개발할 수 있다는 것이기도 합니다.

단위 테스트 코드를 작성하기 위해 먼저 IMemberView Interface를 구현하는 DummyView Class를 생성합니다.

public class DummyView : IMemberView{
    int id;
    int age;
    string name;

    event EventHandler loadMember;
    event EventHandler saveMember;

    #region IMemberView 멤버

    …

    #endregion

    #region 테스트용 인터페이스

    public void TestLoadMember()
    {
        loadMember(this, null);
    }

    public void TestSaveMember()
    {
        saveMember(this, null);
    }

    #endregion
}

표 7  DummyView Class

DummyView Class에서 IMemberView Interface를 구현한 것과 테스트용 메서드인 TestLoadMember()와 TestSaveMember() 메서드를 추가한 것입니다. 실제 View의 UI 요소를 제어하는 코드가 있는 Class는 MemberPresenter이기 때문에 IMemberView와 MemberPresenter를 모두 테스트하는 코드라고 볼 수 있습니다.

using NUnit.Framework;

namespace SingleLayerMVP.Tests
{
    [TestFixture]
    public class MemberViewTest
    {
        IMemberView view;
        MemberPresenter presenter;

        [SetUp]
        public void SetUp()
        {
            view = new DummyView();
            presenter = new MemberPresenter(view);
        }

        [TearDown]
        public void TearDown()
        {
            // null
        }

        /// <summary>
        /// Test LoadMember()
        /// </summary>
        [Test]
        public void TestLoadMember()
        {
            view.ID = 0;
            (view as DummyView).TestLoadMember();

            Assert.AreEqual(view.Name, "홍길동");
            Assert.AreEqual(view.Age, 20);
        }

        /// <summary>
        /// Test SaveMember()
        /// </summary>
        [Test]
        public void TestSaveMember()
        {
            view.ID = 0;
            view.Name = "홍길삼";
            view.Age = 21;
            (view as DummyView).TestSaveMember();

            Assert.AreEqual(view.Name, "홍길삼");
            Assert.AreEqual(view.Age, 21);           

        }


    }
}

표 8 IMemberView와 MemberPresenter 단위 테스트 코드

NUnit Framework를 사용해서 테스트 코드를 작성하는 방법은 http://www.nunit.org 사이트를 참조하세요.

TestLoadMember() 테스트 메서드와 TestSaveMember() 테스트 메서드를 보면 실제 GUI의 동작을 시뮬레이션하는 코드가 구현되어 있는 것을 볼 수 있습니다.

TestLoadMember() 메서드를 자세히 살펴보죠.
view.ID = 0; 이라는 코드에서 사용자가 ID UI 요소에 0이란 값을 입력한 것을 의미하고,
(view as DummyView).TestLoadMember(); 코드에서는 “불러오기” 버튼을 클릭(또는 유사한 동작)한 것을 의미합니다.

Assert.AreEqual() 구문에 의해서 DummyMember.List[]의 첫 번째 데이터인 {‘홍길동’, 20}이 정상적으로 View에 출력되었는지 확인하게 됩니다.

NUnit에서 컴파일된 어셈블리를 로드하여 테스트를 수행하면 다음과 같이 테스트가 통과한 것을 볼 수 있습니다.
 

사용자 삽입 이미지


그림 4 NUnit 테스트 결과

물론 MemberView 폼을 대상으로 테스트를 수행하지 않은 것은 아쉬운 점입니다. 특히 View의 입력 요소에 값이 입력될 때 유효성 체크하는 코드를 작성할 수 없다는 것도 역시 아쉬운 점입니다. 이 두 항목을 포함하여 본 문서에서 미흡하게 다뤘거나 또는 건너 뛴 부분(MVC 패턴, MVC와 MVP 패턴의 차이점과 장단점…)들은 참조문헌에 있는 링크를 참조하세요.

이상으로 MVP 패턴을 적용한 Gui Archicecture 설계를 마치도록 하겠습니다.

5. 참조문헌

5.1. MSDN Magazine, Model View Presenter :
http://msdn.microsoft.com/msdnmag/issues/06/08/DesignPatterns/

5.2. MVC or MVP Pattern – What’s the difference :

http://blogs.infragistics.com/blogs/tsnyder/archive/2007/10/17/mvc-or-mvp-pattern-whats-the-difference.aspx
5.3. Martin Fowler’s GUI Architectures :
http://www.martinfowler.com/eaaDev/uiArchs.html
5.4. MSDN, Model View Controller
http://msdn2.microsoft.com/en-us/library/ms978748.aspx
5.5. MSDN, Implementing Model-View-Controller in ASP.NET
http://msdn2.microsoft.com/en-us/library/ms998540.aspx
5.6. MSDN, Page Controller
http://msdn2.microsoft.com/en-us/library/ms978764.aspx
5.7. MSDN, Front Controller
http://msdn2.microsoft.com/en-us/library/ms978723.aspx
5.8. KLDP, M-V-C가 서로의 영역을 전혀 침범하지 않고 개발하는 것이 과연 가능한가?
http://kldp.org/node/70219
5.9. Javaworld, JSP의 MVC 모델1과 모델2

http://www.javaworld.com/javaworld/jw-12-1999/jw-12-ssj-jspmvc.html

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

log4j구현환경제공.  (0) 2012.05.08
MyBatis (odbc프레임웍)  (0) 2012.05.08
Login&Logout 기본패턴  (0) 2012.05.08
log4j  (0) 2012.05.08
JSTL- 사용 하기  (0) 2012.05.08
Posted by 사라링

Login&Logout 기본패턴

 | JSP
2012. 5. 8. 18:16

index.jsp

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>방가 방가 ~~/index.jsp</title>
</head>
<body >
<link rel="stylesheet" type="text/css" href="<%=request.getContextPath() %>/css/main.css" />
    <div id="topMenu">
        <jsp:include page="/include/top.jsp" />
    </div>
    <h3>이쁜 선희의 웹페이지</h3>

<img src="<%=request.getContextPath() %>/img/5985.jpg" alt="절대경로 3" width="100px"  /><br />

</body>
</html>

top.jsp(include)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
    <%--
    request.setCharacterEncoding("utf-8");--%>
<%-- // incloude/top.jsp --%>
<ul>
    <li>HOME</li>
    <li>회원관리</li>
    <li>게시판</li>
    <li>사이트맵</li>
<%--     <li>msg=<%=request.getParameter("msg") %></li>
 --%>
    <%
        String id = (String)session.getAttribute("USER_ID");
        if(id==null){   
        %>
        <li><a href="<%=request.getContextPath()%>/07/login.jsp">로그인</a>
        </li>
        <%   
        }else {
        %>
        <li><%=id %>님 방가 방가 ^^
        <a href="<%=request.getContextPath()%>/07/logout.jsp">로그아웃</a>
        </li>
        <%
        }
   
    %>

</ul>

login.jsp

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>로그인 07/ login.jsp</title>
<%
    String id="";
    String checked="checked='checked'";
    // 쿠키 배열을 읽어서 이름중에 "user_id" 가 있다면
    // 그 값을 id에 넣고 ,checked변수에는 "";
    Cookie cookie[] =request.getCookies();
    for(int i =0;i<cookie.length;i++){
        if(cookie[i].getName().equals("user_id")){
            id=cookie[i].getValue();
            checked="";
            break;
        }
    }

%>
</head>
<body>


<h4>로그인</h4>
<form action="logincheck.jsp" method="post">
    <label>아이디
        <input type="text" name="user_id" value="<%=id %>" />
    </label>
    <label>패스워드
        <input type="password" name="user_pw" />
    </label>
    <label>
        <input type="checkbox" <%=checked %> name="id_save" value="Y"/>
        ID 기억
    </label>
    <button type="submit">로그인</button>
</form>


</body>
</html>

loginchecked.jsp

<?xml version="1.0" encoding="UTF-8" ?>
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%!static Map memberMap;
    static {
        memberMap = new HashMap();
        memberMap.put("hwang", "java");
        memberMap.put("malja", "1004");
        memberMap.put("sunhee", "pretty");
        memberMap.put("sararing", "1234");
    }%>
   
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>07 /logincheck.jsp</title>
</head>
<body>
<%
    //사용자가 입력한 파라미터 user_id  user_pass 를 받아서
    // 등록된 것과 비교를 해야함.
    //맞으면 세션에 "USER_ID" 키로 해당 user_id 저장
    // / index.jsp로 다이렉트 하고 
    //  틀리면 자바 스크립트로 메시지 창을 보여 주고 login.jsp로 이동
    String id = request.getParameter("user_id");
    String pw = request.getParameter("user_pw");
    String checked = request.getParameter("id_save");
    if (id != null) {
        if (memberMap.containsKey(id)) {
            String dbPass = (String) memberMap.get(id);
            if (dbPass.equals(pw)) {
                session.setAttribute("USER_ID", id);
                //sendRedirect 는 헤더에 3xx 대 상태 코드로 설정 하고
                // Location 헤더에 다시 들어올 경로를 알려 주는데
                // 클라이언트가 다시 와야하므로 웹경로를 포함 시켜야 한다.
                if(checked!=null){
                    /* "user_id",id, 한달,루트로 저장
                     */
                    Cookie cookie =new Cookie("user_id",id);
                    cookie.setMaxAge(60*60*24*30);
                    cookie.setPath("/ddit");
                    response.addCookie(cookie);
                }else {
                    Cookie cookie =new Cookie("user_id","");
                    cookie.setMaxAge(0);
                    cookie.setPath("/ddit");
                    response.addCookie(cookie);
                }
                response.sendRedirect(request.getContextPath()+"/index.jsp");
                return;
            }else {
                // 패스워드가 틀릴때
                // 자바 스크립트로 경고 메시지 출력후 login.jsp로 점프
                %>
            <script type="text/javascript">
            <!--
                alert("d야 패스워드가 틀려");
                location.href="login.jsp";
            //-->
            </script>
                <%
                return;
            }
           
        }
    }
    // id가 null 이거나 memberMap 에 없는 경우
%>
            <script type="text/javascript">
            <!--
                alert("아이디를 입력해주세요");
                location.href="login.jsp";
            //-->
            </script>
</body>
</html>

 

 

 

 

 

logout.jsp

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>07/logout.jsp</title>
</head>
<body>
<%
    //세션중 임의 키만 제거 은 밑
    //session.removeAttribute("USER_ID");

    // 기존세션 무효화,새로운 세션키 활당
    session.invalidate();
    response.sendRedirect(request.getContextPath()+"/index.jsp");


%>


</body>
</html>

 

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

MyBatis (odbc프레임웍)  (0) 2012.05.08
MVC패턴  (0) 2012.05.08
log4j  (0) 2012.05.08
JSTL- 사용 하기  (0) 2012.05.08
JSTL  (0) 2012.05.08
Posted by 사라링

log4j

 | JSP
2012. 5. 8. 18:16

첨부파일 : 압축해제후 log4j-1.2.14.jar 파일을
JDK설치된 폴더의 lib/ext 디렉토리에 카피후
프로젝트 properties 의 Java Build Path >
Library > Add External Jars ... 에서 추가시킴


 

LOG4J

I. 들어가면서.. 그리고 log4j


log4j는 자바 어플리케이션에서 빠르고 효과적으로 로깅 할 수 있도록 도와주는 오픈 소스 프로젝트입니다.


로깅(logging)은 코드의 가독성을 떨어뜨리는 단점이 있지만 애플리케이션에 문제가 있을 때 개발자가 자세한 상황을 파악할 수 있도록 해 주며 테스팅시 빠질 수 없는 요소입니다.


아마도 여러분들은 여러 어플리케이션이 추가되면서 각 개발자들만의 독특한 로깅방식이 서로 썩이고 얽혀서 화면에 나타나는것을 많이 봤을겁니다 -_-;
즉 로깅방법을 통일할 필요가 있는것이죠. 모든 개발자가 특정 포맷에 맞추어서 로깅 한다면 한결 로깅하기도 편하겠지요


오픈 소스 프로젝트인 Log4j는 개발자들이 매우 손쉽고 다양한 형태로 로깅을 할 수 있도록 도와줍니다. 성능또한 우수해 더이상 System.out.println을 사용할 필요가 없습니다.



II. 다운로드


다운로드 http://logging.apache.org/log4j/docs/download.html

매뉴얼 http://logging.apache.org/log4j/docs/documentation.html

API spec http://logging.apache.org/log4j/docs/api/index.html



III. LOG4J 구조


일단 log4j를 잘 모르지만 그 구조만 살짝 살펴보고 넘어갑시다

log4j는 크게 3가지 요소로 구성되며 그 구조는 다음과 같습니다

① Logger(Category) : 로깅 메세지를 Appender에 전달합니다.

② Appender : 전달된 로깅 메세지를 파일에다 기록할 것인지, 콘솔에 출력할 것인지

                   아니면 DB에 저장할 것인지 매개체 역활을 합니다.

③ Layout : Appender가 어디에 출력할 것인지 결정했다면 어떤 형식으로 출력할 것이지

                출력 layout을 결졍합니다.

쉽죠?



IV. LOG4J 로깅 레벨


log4j는 다양한 로깅레벨을 지원합니다.


① FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.

② ERROR : 일반 에러가 일어 났을 때 사용합니다.

③ WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.

④ INFO : 일반 정보를 나타낼 때 사용합니다.

⑤ DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.


만약 로깅 레벨을 WARN 으로 설정하였다면 그 이상 레벨만 로깅하게 됩니다.

즉 WARN, ERROR, FATAL 의 로깅이 됩니다.



V. 샘플코드 1


jsp에서 사용하는 예제가 없어 만들어 봤습니다.


test.jsp


<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.Logger" %>

<%!
 static Logger logger = Logger.getLogger("test.jsp");
%>

<%
logger.fatal("fatal!!");

logger.fatal("fatal2!!", new NullPointerException("널입니다요"));

logger.error("error!", new NumberFormatException());

logger.error("error!2");

logger.warn("warn");

logger.info("info");

logger.debug("debug");
%>

 

결과 콘솔화면








static Logger logger = Logger.getLogger("test.jsp");

static 메소드 getLogger를 통해 logger 인스턴스를 가져옵니다.
getLogger에는 파라미터로 스트링 혹은 클래스를 사용하는데 jsp에서는 클래스를 파라미터로 주기에는 좀 애매합니다. 그냥 스트링으로 주도록 하지요


logger.fatal("fatal!!");
logger.fatal("fatal2!!", new NullPointerException("널입니다요"));
  
logger에 fatal 레벨의 메세지를 전달합니다. 다음 두가지 메소드를 지원하는군요

fatal(Object message)

fatal(Object message, Throwable t)

각 레벨마다 위처럼 두가지 메소드를 지원합니다.


지원 메쏘드
logger.fatal(Object message) logger.fatal(Object message, Throwable t)
logger.error(Object message) logger.error(Object message, Throwable t)
logger.warn(Object message) logger.warn(Object message, Throwable t)
logger.info(Object message) logger.info(Object message, Throwable t)
logger.debug(Object message) logger.debug(Object message, Throwable t)



VI. 샘플코드 2


서블릿의 경우 다음과 같이 코딩하면 되겠군요

TestServlet.java


import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TestServlet extends HttpServlet {


    static Logger logger = Logger.getLogger(TestServlet.class);


    public void init(ServletConfig config) throws ServletException {
         super.init(config);
    }


    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         try {
              ...
   
              logger.info("Hellow World~");

              ...

          } catch (Exception e) {
              logger.error("Error at TestServlet", e);
          }
     }
}



VII. LOG4J 설정


log4j 설정은 프로그램 내에서 할 수 있지만 설정파일을 사용함으로서 좀더 유연하게 log4j환경을 만들 수 있습니다.


프로그램에서 설정

<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.*,java.io.* "
%>

<%!
 static Logger logger = Logger.getLogger("log4j.jsp");
%>

<%
String layout = "%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n";
String logfilename = "DailyLog.log";
String datePattern = ".yyyy-MM-dd ";

PatternLayout patternlayout = new PatternLayout(layout);
DailyRollingFileAppender appender = new DailyRollingFileAppender(patternlayout, logfilename, datePattern);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
logger.fatal("fatal!!");
%>


property 파일에 설정
log4j.properties를 만들어 /WEB-INF/classes 밑에 놓으세요



log4j.rootLogger=INFO, stdout, rolling

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n

log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

log4j.appender.rolling.File=output.log

log4j.appender.rolling.Append=true

log4j.appender.rolling.MaxFileSize=500KB

log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n

#최상위 카테고리에 INFO로 레벨 설정 및 appender로 stdout, rolling을 정의

log4j.rootLogger=INFO, stdout, rolling

#stdout 어펜더는 콘솔에 뿌리겠다는 정의

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

#stdout 어펜더는 patternlayout을 사용하겠다는 정의

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

#페턴은 다음과 같이 포맷팅 하겠다는 것을 정의

log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


#역시나 rolling 어펜더는 파일로 처리한다라고 정의

log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

#로그 파일 이름은 output.log

log4j.appender.rolling.File=output.log

#true면 톰캣을 내렸다 올려도 파일이 리셋되지 않습니다.

log4j.appender.rolling.Append=true

#파일 최대 사이즈는 500KB로 설정

log4j.appender.rolling.MaxFileSize=500KB

#파일 포맷은 output.log.2005-03-10 으로 관리하겠다고 정의

log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

#역시나 rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의

log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

#rolling 어펜더는 패턴 레이아웃 포맷

log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n



VIII. 설정 포맷


로그파일명 포맷 (DatePattern)
로그파일명 포맷입니다. 날짜, 시간 및 분단위로까지 로그 파일을 분리할 수 있습니다.

 

형식 설명
'.'yyyy-MM 매달 첫번째날에 로그파일을 변경합니다
'.'yyyy-ww 매주의 시작시 로그파일을 변경합니다.
'.'yyyy-MM-dd 매일 자정에 로그파일을 변경합니다.
'.'yyyy-MM-dd-a 자정과 정오에 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH 매 시간의 시작마다 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH-mm 매분마다 로그파일을 변경합니다.



PatternLayout 포맷
로그자체를 어떤 포맷으로 남길지 결정합니다.
layout에는 HTMLLayout, PatternLayout, SimpleLayout, XMLLayout등이 있으며 PatternLayout이 일반적으로 가장 많이 쓰입니다.


형식 설명
%p debug, info, warn, error, fatal 등의 priority 가 출력된다.
%m 로그내용이 출력됩니다
%d 로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.
%c 카테고리를 표시합니다
예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%C 클래스명을 포시합니다.
예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.


예시) (같은 색끼리 보시면 됩니다)

위의 test.jsp를 다음 포맷으로 출력해본다면

[%c] [%C] [%d] [%F] [%l] [%L] [%m] [%M] [%n] [%p] [%r] [%t] [%x] [%X]는 다음과 같다

[test.jsp] [org.apache.jsp.test_jsp] [2005-03-10 12:37:23,561] [test_jsp.java] [org.apache.jsp.test_jsp._jspService(test_jsp.java:64)] [64] [fatal!!] [_jspService] [개행] [FATAL] [765567] [http-8080-Processor25] [] []


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com

=============================================

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

MVC패턴  (0) 2012.05.08
Login&Logout 기본패턴  (0) 2012.05.08
JSTL- 사용 하기  (0) 2012.05.08
JSTL  (0) 2012.05.08
JSP 에러코드 모음  (0) 2012.05.08
Posted by 사라링

JSTL- 사용 하기

 | JSP
2012. 5. 8. 18:15

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

테그 라이브러리를 맨 위 임포트 페이지 쪽에 정의 하자. core는 c 로 fmt 는 fmt(포멧) 으로 사용하는게 정신건강에 이로우며

<fmt:bundle basename="kr.or.ddit.res.board"
            prefix="BOARD.">

이런식의 응용도 가능 하다.

먼저 board.properties 를 만들어야 한다.

BOARD.TITLE=Sunhee mom's Doodle
BOARD.NEW=new
BOARD.LIST.NO=no
BOARD.LIST.TITLE=title
BOARD.LIST.WRITER=writer
BOARD.LIST.HIT=hit
BOARD.LIST.DATE=date
BOARD.LIST.TIP={0} is write, {1} hit ,{2} haha
BOARD.LIST.PREV=Previous
BOARD.LIST.NEXT=Next

이는 소스의 내용이다. 이것을 이용 하여

<thead>
                <tr>
                    <th><fmt:message key="LIST.NO"/></th>
                    <th><fmt:message key="LIST.TITLE"/></th>
                    <th><fmt:message key="LIST.WRITER"/></th>
                    <th><fmt:message key="LIST.HIT"/></th>
                    <th><fmt:message key="LIST.DATE"/></th>
                </tr>

이런식의 key 값을 지정 하여 사용 할수 있다.

 

jstl_변경전(1).jpg

 

이러한 포문을

 

jstl_변경후(1).jpg

변경 되었다 request.setAttribute 에 list 를 "dragon" 이라는 key 값으로 주며

그것을 forEach 문에서 Items 로 사용함을 확인 하자.  <%%> 의 사용을 최대한 줄이자.

 

한가지 더

jstl_변경전_2(1).jpg

 

하단의 페이지 구문 이다. <%%> d이러한 것을 없에자.

request.setAttrivute 로 값을 셋팅 하고 있다.

jstl_변경후2_1(1).jpg

jstl_변경후2_2(1).jpg

이렇듯 바뀌었다. !!

 

참고로 주석은 되도록 jsp 에서는 <%-- --%> 라고 사용 하자.

 

<c:set var="msg">황-지혜,유-지혜,오-선희</c:set>

<c:forTokens items="${msg}" delims="," var="tk">
    ${tk} <br/>
</c:forTokens>

 

 

추가 소스

 

<?xml version="1.0" encoding="UTF-8" ?>
<%@page import="kr.or.ddit.board.vo.BoardBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<% request.setCharacterEncoding("UTF-8"); %>   
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>19/core_3.jsp</title>
</head>
<body>
<c:set var="board" value="<%=new kr.or.ddit.board.vo.BoardBean()%>"></c:set>
<c:set target="${board}" property="bo_title" value="수요일에는 빨간 장미를 .. " />

1.${board.bo_title}<br>
<c:remove var="board" />추가.jpg
2.${board.bo_title}<br>
</hr>
<c:set var="msg">황-지혜,유-지혜,오-선희</c:set>

<c:forTokens items="${msg}" delims="," var="tk">
    ${tk} <br/>
</c:forTokens>
<hr/>

<!-- 아래 데이터를 데이터베이스에서 가져왔다고 가정 하자.  -->
<c:set var="hee">
    <b>오선희</b>그녀는 누구인가.. 근데.. ? ,
    <font color="red">유지혜</font>,유씨엄마
</c:set>
${hee}
<hr/>
<c:out value="${hee}" default="기본값" escapeXml="true"/>
<hr/>

-- excapeXml="false" 옵션시 
<c:catch var="ex">
    <%
        String a =request.getParameter("dragon");
        out.println(a.toUpperCase());
    %>
</c:catch>
<c:if test="${not empty ex}">
아 에러 났네 . .. ${ex.message}<br/>
</c:if>

<!--

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
 -->
<c:set var="msg2">Hwang,황엄마, you 유지혜 </c:set>
기억자 :  ${fn:length(msg2) }</br>
대문자 : ${fn:toUpperCase(msg2)}<br/>
일행문자 : ${fn:substring(msg2,0,6) }
지혜로끈나니?  : ${fn:endsWith(msg2,"지혜") }<br/>
<c:set var="arr2" value="${fn:split(msg2,',') }"/>
<c:forEach items="${arr2 }" varStatus="st">
    ${st.count}, ${st.current} <br/>
</c:forEach>

</body>
</html>

 

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

Login&Logout 기본패턴  (0) 2012.05.08
log4j  (0) 2012.05.08
JSTL  (0) 2012.05.08
JSP 에러코드 모음  (0) 2012.05.08
include  (0) 2012.05.08
Posted by 사라링

JSTL

 | JSP
2012. 5. 8. 18:15
하단 메뉴의 http://sararing.springnote.com/pages/11288712 사용 하기 를 반드시 읽을것

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

 

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%
    request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>19/ core_2.jsp</title>
</head>
<body>
<%-- <%
    request.setAttribute("jumsu", "97");
%> --%>
<c:set var ="jumsu" value="86" scope="request"/>
<c:if test="${jumsu>90}">수</c:if>
<c:if test="${jumsu gt 80 and jumsu<=90}">우</c:if>
<c:if test="${jumsu>70 && jumsu le 80}">미</c:if>
<c:if test="${jumsu<=60}">나가 죽어 </c:if>
<hr />
<!--
    밑에 스위치문
 --><c:choose>
    <c:when test="${jumsu>90}">참 잘했어요</c:when>
    <c:when test="${jumsu>80}">잘했어요</c:when>
    <c:when test="${jumsu>70}">분발 하세요. </c:when>
    <c:when test="${jumsu>60}">너 낳고 미역국을 먹은 내가 잘못 </c:when>

</c:choose>
<c:set var = "sum" value="0"/>
<c:forEach begin="1" end="100" var="i" step="2">
    <c:set var="sum" value="${sum+i}"/>
</c:forEach>
1부터 100까지의 홀수의 합 =${sum}
<br/>
<hr />
<c:set var="dan"  value="5" />
    <c:forEach begin="1" end="9" var="i" step="1">
        ${dan} * ${i}=${dan*i}  <br>
    </c:forEach>

</body>
</html>

 

 

 

JSTL 기초, Part 1: Expression Language (한글)

JSP 애플리케이션용 MA 단순화하기

Mark A. Kolb, 소프트웨어 엔지니어

요약:  JSP Standard Tag Library (JSTL)은 일반적인 웹 애플리케이션 기능(반복(iteration)과 조건, 데이터 관리 포맷, XML 조작, 데이터베이스 액세스)을 구현하는 커스텀 태그 라이브러리 모음이다. 소프트웨어 엔지니어인 Mark Kolb은 JSTL 태그의 사용방법을 설명한다. 표현층(presentation layer)에서 소스 코드를 제거하여 소프트웨어 관리를 단순화시키는 방법도 설명한다. 이외에도 JSTL의 단순화된 Expression Language에 대한 설명도 포함되어 있다.

 

원문 게재일:  2003 년 11 월 18 일 (출판일: 2003 년 2 월 11 일)
난이도:  초급
페이지뷰:  19026 회
의견:   0 (보기 | 의견 추가 - 로그인)

평균 평가 등급 4 개 총 32표 평균 평가 등급 (32 투표수)
아티클 순위

 

 

JavaServer Pages (JSP)는 J2EE 플랫폼을 위한 표준 표현 레이어(presentation-layer) 이다. JSP는 페이지 콘텐트를 동적으로 생성할 수 있는 전산을 수행 할 수 있는 스크립팅 엘리먼트와 액션을 제공한다. 스크립팅 엘리먼트는 프로그램 소스 코드가 JSP 코드에 포함될 수 있도록 한다. 페이지가 사용자 요청에 대한 응답으로 렌더링 될 때 실행할 목적이다. 액션(actions)은 전산 작동을 JSP 페이지의 템플릿 텍스트를 구성하고 있는 HTML 이나 XML과 거의 흡사하게하는 태그로 인캡슐한다. JSP 스팩에 표준으로 정의된 몇 가지의 액션들이 있다. 하지만 JSP 1.1 부터 개발자들은 커스텀 태그 라이브러리 형태로 자신만의 액션들을 만들 수 있다.

JSP Standard Tag Library (JSTL)는 JSP 1.2 커스텀 태그 라이브러리 모음으로서 광범위한 서버측 자바 애플리케이션에 일반적으로 쓰이는 기본 기능들을 구현한다. JSTL은 데이터 포맷, 반복 콘텐트 또는 조건 콘텐트 같은 전형적인 표현 레이어를 위한 표준 구현을 제공하기 때문에, JSP 작성자들이 애플리케이션 개발에 집중하는데 도움이 된다.

물론, 스크립틀릿, 익스프레션, 선언 같은 JSP 스크립팅 엘리먼트를 사용하는 태스크를 구현할 수 있다. 예를 들어 조건 콘텐트(conditional content)는 세 개의 스크립틀릿(Listing 1의 하이라이트 부분)을 사용하여 구현될 수 있다. 페이지 내에 프로그램 소스 코드를 임베딩하는 것에 의존하기 때문에 스크립팅 엘리먼트가 소프트웨어 관리 태스크를 매우 복잡하게 하는 경향이있더라도 JSP 페이지는 그들을 사용한다. Listing 1의 스크립틀릿 예제는 브레이스들의 적절한 매칭에 매우 의존한다. 조건화된 콘텐트 내에 추가 스크립틀릿을 중첩하는 것은 신택스 에러가 갑자기 일어났다면 페이지가 JSP 콘테이너에 의해 컴파일 될 때 결과 에러 메시지를 합리화하는 것은 도전이 될 수 있다.


Listing 1. 스크립틀릿을 통해 조건 콘텐트 구현하기

  <% if (user.getRole() == "member")) { %>
    <p>Welcome, member!</p>
<% } else { %>
    <p>Welcome, guest!</p>
<% } %>

 

그와 같은 프로그램을 해결하는데에는 프로그래밍 경험이 많이 필요하다. JSP 페이지의 마크업이 페이지 레이아웃과 그래픽 디자인에 익숙한 디자이너에 의해 개발 및 관리되는데 반해 그와 같은 페이지 내의 스크립팅 엘리먼트는 문제가 생길 때 프로그래머가 개입해야한다. 하나의 파일안에 있는 코드에 대한 책임을 공유하는 것은 JSP 페이지의 개발, 디버깅, 향상을 성가신일로 만든다. JSTL은 일반적인 기능을 커스텀 태그 라이브러리의 표준 세트로 패키징했기 때문에 JSP 작성자들이 스크립팅 엘리먼트에 대한 필요를 줄이고 관련된 관리 비용을 피할 수 있도록 한다.

이 기사의 다른 시리즈

Part 2, "core 분석" (2003년 3월)
Part 3, "보여지는 것도 중요하다!" (2007년 3월)
Part 4, "SQL과 XML 콘텐트에 액세스 하기" (2007년 4월)

JSTL 1.0

02년 6월에 릴리스된 JSTL 1.0은 네 개의 커스텀 태그 라이브러리(core, format, xml, sql)와 두 개의 범용 태그 라이브러리 밸리데이터(ScriptFreeTLV & PermittedTaglibsTLV)로 구성되어 있다. core 태그 라이브러리는 커스텀 액션을 제공하여 범위 변수를 통해 데이터를 관리할 수 있도록하며 페이지 콘텐트의 반복과 조건화를 수행할 수 있도록 한다. 또한 URL에서 생성 및 작동할 수 있는 태그도 제공한다. format 태그 라이브러리는 이름이 시사하는 바와 같이 데이터 특히 숫자와 날짜를 포맷하는 액션을 정의한다. 국지화 된 리소스 번들을 사용하여 JSP 페이지의 국제화도 지원한다. xml 라이브러리에는 XML을 통해 표현된 데이터를 조작할 수 있는 테그가 포함되어 있다. sql 라이브러리는 관계형 데이터베이스를 쿼리하는 액션을 정의한다.

두 개의 JSTL 태그 라이브러리 밸리데이터는 개발자들이 JSP 애플리케이션 내에서 표준을 코딩하도록 한다. ScriptFreeTLV 밸리데이터를 설정하여 JSP 페이지 내에 있는 다양한 스크립팅 엘리먼트(스크립틀릿, 표현, 선언)의 다양한 유형을 사용하는 것을 막는다. 이와 비슷하게 PermittedTaglibsTLV 밸리데이터는 애플리케이션의 JSP 페이지들에 의해 액세스된 커스텀 태그 라이브러리(JSTL 태그 라이브러리 포함)을 제한한다.

JSTL은 J2EE 플랫폼에 필요한 컴포넌트가 될 것이지만 적은 수의 애플리케이션 서버들만이 이를 포함하고 있는 것이 현실이다. JSTL 1.0의 레퍼런스 구현은 Apache Software Foundation의 Jakarta Taglibs 프로젝트의 일부로서 사용할 수 있다. (참고자료). 레퍼런스 구현에 있는 커스텀 태그 라이브러리는 JSTL 지원을 추가하기 위해 JSP 1.2와 Servlet 2.3 이상 스팩을 지원하는 모든 애플리케이션 서버에 통합될 수 있다.

Expression language

JSP 1.2에서 JSP 액션의 애트리뷰트는 정적 캐릭터 스트링이나 익스프레션을 사용하여 지정된다. 예를 들어 Listing 2의 경우 정적 값들은 <jsp:setProperty> 액션의 nameproperty 애트리뷰트를 위해 지정된다. 반면 익스프레션은 이것의 값 애트리뷰트를 지정하는데 사용된다. 이 액션은 요청 매개변수의 현재 값을 이름이 붙여진 빈 속성으로 할당하는 효과를 갖고 있다. 이러한 방식으로 사용된 익스프레션은 request-time attribute values이라 일컬어지며 애트리뷰트 값을 동적으로 지정하기위한 JSP 스팩에 내장된 유일한 메커니즘이다.


Listing 2. request-time attribute value을 결합하는 JSP 액션

<jsp:setProperty name="user" property="timezonePref"
          value='<%= request.getParameter("timezone") %>'/>

 

request-time attribute values가 익스프레션을 사용하여 지정되기 때문에 다른 스크립팅 엘리먼트와 같은 소프트웨어 관리 문제가 일어날 수 있다. 이런 이유로 인해 JSTL 커스텀 태그는 동적 애트리뷰트 값을 지정하기 위한 대안 메커니즘을 지원한다. JSP 익스프레션을 사용하는 것 보다 JSTL 액션용 애트리뷰트 값이 단순화 된 expression language (EL)를 사용하여 지정될 수 있다. EL은 JSP 컨테이너에 있는 데이터를 검색 및 조작할 식별자, 접근자, 연산자를 제공한다. EL은 EcmaScript(참고자료)와 XML Path Language (XPath)에 약간 의존하기 때문에 신택스는 페이지 디자이너와 프로그래머 모두 에게 익숙하다. EL은 객체와 속성들을 검색하면서 간단한 작동을 수행한다. 이것은 프로그래밍 언어도 스크립팅 언어도 아니다. JSTL 태그와 결합하면 간단하고 편리한 표기를 사용하여 복잡한 작동이 표현될 수 있다. EL 익스프레션은 달러 표시($)와 중괄호 ({})를 앞에 붙여 사용하여 범위를 정한다.(Listing 3)


Listing 3. JSTL 액션: EL 익스프레션 범위 지정

<c:out value="${user.firstName}"/>

 

여러개의 익스프레션들과 정적 텍스트를 결합하여 스트링 연결을 통해 동적 애트리뷰트 값을 만들 수 있다.(Listing 4). 개별 익스프레션들은 식별자, 접근자, 리터럴, 연산자로 구성되어 있다. 식별자는 데이터 센터에 저장된 데이터 객체를 참조하는데 사용된다. EL은 11 개의 식별자를 보유하고 있다. 11 개의 EL 내장 객체에 상응하는 것들이다. 다른 모든 식별자들은 범위 변수를 참조하는 것으로 간주된다. 접근자는 객체의 속성 또는 컬렉션의 엘리먼트를 검색하는데 사용된다. 리터럴은 고정된 값들(숫자, 문자, 스트링, 부울, null)을 나타낸다. 연산자는 데이터와 리터럴이 결합 및 비교될 수 있도록 한다.


Listing 4. 정적 텍스트와 여러 EL 익스프레션을 결합하여 동적 애트리뷰트 값 지정하기

<c:out value="Hello ${user.firstName} ${user.lastName}"/>

 

범위 변수(Scoped variables)

<jsp:useBean> 액션을 통한 JSP 에이피아이는 데이터가 저장될 수 있도록 하며 JSP 컨테이너 내에 네 개의 다른 범위에서 데이터가 검색될 수 있도록 한다. JSTL은 이러한 범위 내에 객체를 할당하고 제거할 추가 액션을 제공한다. 더욱이, EL은 범위 변수 같은 객체들을 검색하는 빌트인 지원을 제공한다. 특히 EL의 내장 객체 중 하나라도 상응하지 않는 EL 익스프레션에 있는 식별자는 네 개의 JSP 스콥 중 하나에 저장된 객체를 참조하는 것으로 자동 간주된다:

  • 페이지 범위
  • 요청 범위
  • 세션 범위
  • 애플리케이션 범위

페이지 범위에 저장된 객체들은 특정 요청에 대한 페이지가 프로세스 되는 동안 검색될 수 있다. 요청 범위 내에 저장된 객체들은 요청 프로세스에 참여한 모든 페이지들이 프로세스 하는 동안 검색될 수 있다. 객체가 세션 범위에 저장되어있다면 웹 애플리케이션과의 단일 인터랙트브 세션 동안 사용자가 액세스 한 페이지로 검색될 수 있다. 웹 애플리케이션이 언로드(unload) 될 때 까지 애플리케이션 범위에 저장된 객체는 모든 페이지에서 접근가능하며 모든 사용자들이 접근할 수 있다.

캐릭터 스트링을 희망하는 범위에 있는 객체로 매핑하여 범위안에 객체를 저장할 수 있다. 이러한 경우에는 같은 캐릭터 스트링을 제공하여 범위에서 객체를 검색할 수도 있다. 스트링은 범위 매핑 중 검색되고 매핑된 객체는 리턴된다. Servlet API 내에서 그와 같은 객체들은 상응하는 범위의 애트리뷰트로서 언급된다. EL의 경우 애트리뷰트와 관련된 캐릭터 스트링은 변수 이름으로 간주될 수도 있다.

EL에서 내장 객체들과 관련이 없는 식별자들은 JSP 범위에 저장된 객체들을 명명하는 것으로 간주된다. 그와 같은 식별자는 페이지 범위를 검사하고 그 다음에는 요청 범위, 세션 범위, 애플리케이션 범위 순으로 검사한다. 식별자의 이름이 그 범위에 저장된 객체 이름과 매칭되는지의 여부가 테스트된다. 첫 번째 매치는 EL 식별자의 값으로 리턴된다. EL 식별자는 범위 변수를 참조하는 것으로 간주될 수 있다.

기술적인 관점에서 보면 내장 객체로 매핑하지 않는 식별자는 PageContext 인스턴스의 findAttribute() 메소드를 사용하여 평가되면서 현재 핸들되는 요청에 대해 익스프레션이 발생하는 페이지의 프로세싱을 나타낸다. 식별자의 이름은 이 메소드에 대한 인자로서 전달된다. 이것은 같은 이름을 가진 애트리뷰트에 대한 네 개의 범위를 검색한다. 발견된 첫 번째 매치는 findAttribute() 메소드 값으로 리턴된다. 그와 같은 애트리뷰트가 네 개의 범위 중에 없으면 null이 리턴된다.

궁극적으로 범위 변수는 네 개의 EL 식별자로서 사용될 수 있는 이름을 가진 JSP 범위의 에트리뷰트라고 할 수 있다. 영숫자 이름으로 할당되는 한 범위 변수는 JSP 에 존재하는 모든 메커니즘으로 만들어져 애트리뷰트를 설정할 수 있다. 여기에는 빌트인 <jsp:useBean> 액션은 물론 setAttribute() 메소드가 포함된다. 게다가 네 개의 JSTL 라이브러리에서 정의된 많은 커스텀 태그들은 스스로 범위 변수로서 애트리뷰트 값을 설정할 수 있다.

내장 객체(Implicit objects)

11 개의 EL 내장 객체용 식별자는 표 1과 같다. JSP 내장 객체와 혼동하지 말것!

표 1. EL 내장 객체

 

Category 식별자 설명
JSP pageContext 현재 페이지의 프로세싱과 상응하는 PageContext 인스턴스
범위 pageScope 페이지 범위 애트리뷰트 이름과 값과 관련된 Map
requestScope 요청 범위 애트리뷰트 이름과 값과 관련된 Map
sessionScope 세션 범위 애트리뷰트 이름과 값과 관련된 Map
applicationScope 애플리케이션 범위 애트리뷰트 이름과 값과 관련된 Map
요청 매개변수 param 요청 매개변수의 기본 값을 이름으로 저장하는 Map
paramValues 요청 매개변수의 모든 값을 String 어레이로서 저장하는 Map
요청 헤더 header 요청 헤더의 기본 값을 이름으로 저장하는 Map
headerValues 요청 헤더의 모든 값을 String 어레이로서 저장하는 Map
쿠키 cookie 요청에 수반되는 쿠키들을 이름으로 저장하는 Map
초기화 매개변수 initParam 웹 애플리케이션의 콘텍스트 초기화 매개변수를 이릉으로 저장하는 Map

JSP와 EL 내장 객체가 일반적인 하나의 객체를 갖는 반면(pageContext) 다른 JSP 내장 객체는 EL에서 접근 가능하다. 페이지콘텍스트가 다른 8 개의 JSP 내장 객체 모두에 액세스 할 수 있는 속성을 갖고 있기 때문이다.

남아있는 모든 EL 내장 객체들은 맵(map)이다. 이름에 상응하는 객체들을 탐색한다. 첫 번째 네 개의 맵은 이전에 언급한 다양한 애트리뷰트 범위를 나타낸다. 특정 범위 내의 식별자들을 검색하는데 사용될 수 있다. EL이 기본적으로 사용하는 순차적인 탐색 프로세스에 의존하지 않는다.

다음 네 개의 맵은 요청 매개변수와 헤더의 값을 반입하는 용도이다. HPPT 프로토콜이 요청 매개변수와 헤더가 다중 값을 가질 수 있도록 하기 때문에 각각 한 쌍의 맵이 있다. 각 쌍 중에서 첫 번째 맵은 요청 매개변수 또는 헤더에 대한 기본 값을 리턴한다. 실제 요청 시 첫 번째로 지정된 값이 무엇이든 상관없다. 두 번째 맵은 매개변수나 헤더의 값 모두 검색될 수 있도록 한다. 이 맵의 핵심은 매개변수 또는 헤더의 이름이다. 값들은 String 객체의 어레이이다.

쿠키 내장 객체는 요청으로 설정된 쿠키에 대한 접근을 제공한다. 이 객체는 요청과 관련된 모든 쿠키들의 이름을 Cookie 객체들로 매핑하면서 쿠키들의 속성을 나타낸다.

마지막 EL 내장 객체인 initParam은 웹 애플리케이션과 관련된 모든 콘텍스트 초기와 매개변수의 이름과 값을 저장하는 맵이다. 초기화 매개변수들은애플리케이션의 WEB-INF 디렉토리에 있는 web.xml 전개 디스크립터 파일을 통해 정의된다.

접근자(Accessors)

EL 식별자는 내장 객체 또는 범위 변수로서 설명될 수 있기 때문에 자바 객체로 평가해야한다. EL은 상응하는 자바 클래스에서 프리머티브를 래핑/언래핑한다. 하지만 대부분의 경우 식별자들은 자바 객체에 대한 포인터가 된다.

결과적으로 이러한 객체들의 속성이나, 어레이와 컬렉션의 경우 그들의 엘리먼트에 액세스하는 것이 바람직하다. 이를 위해 EL은 두 개의 다른 접근자를 제공한다. 닷(dot) 오퍼레이터(.)와 브래킷 오퍼레이터([])이다. 이들은 속성과 엘리먼트들이 EL을 통해 연산될 수 있도록 한다.

닷 연산자는 객체의 프로퍼티에 접근하는데 사용된다. ${user.firstName} 익스프레션에서 닷 연산자는 user 식별자에 의해 참조된 객체 중 firstName이라는 이름을 가진 속성에 액세스 한다. EL은 자바 빈 규정을 사용하여 객체 속성에 접근하기 때문에 이 속성에 대한 게터(일반적으로 getFirstName())는 이 익스프레션이 정확히 계산하기 위해서 반드시 정의되어야 한다. 액세스되는 속성이 객체일 때 닷 연산자는 재귀적으로 적용될 수 있다. 예를 들어 가상의 user 객체가 자바 객체로서 구현된 address 속성을 갖고 있다면 닷 연산자는 이 객체의 속성에 액세스 하기 위해 사용될 수도 있다. ${user.address.city} 익스프레션은 이 address 객체 중 중첩된 city 속성을 리턴한다.

브래킷 연산자는 어레이와 컬렉션의 엘리먼트를 검색하는데 사용된다. 어레이와 컬렉션(java.util.List를 구현하는 컬렉션)의 경우 검색될 엘리먼트 인덱스는 브래킷 안에 나타난다. 예를 들어 ${urls[3]} 익스프레션은 이 urls 식별자에 의해 참조된 어레이 또는 컬렉션의 네 번째 엘리먼트를 리턴한다.

java.util.Map 인터페이스를 구현하는 컬렉션의 경우 브래킷 연산자는 관련 키를 사용하여 맵에 저장된 값을 찾는다. 이 키는 브래킷 내에서 지정되고 상응하는 값은 익스프레션 값으로 리턴된다. 예를 들어 ${commands["dir"]} 익스프레션은 commands 식별자에 의해 참조된 Map"dir" 키와 관련된 값을 리턴한다.

익스프레션이 브래킷안에 나타날 수 있다. 중첩된 익스프레션의 계산 결과는 컬렉션이나 어레이의 적절한 엘리먼트를 검색하는 인덱스 또는 키로 작용한다. 닷 연산자가 true라면, 브래킷 연산자도 재귀적으로 적용될 수 있다. 이는 EL이 다차원 어레이, 중첩 컬렉션, 또는 둘의 결합에서 엘리먼트를 검색 할 수 있도록 한다. 더욱이 닷 연산자와 브래킷 연산자는 상호운용성이 있다. 예를들어 한 어레이의 엘리먼트가 객체라면 브래킷 연산자는 그 어레이의 엘리먼트를 검색하는데 사용될 수 있고 닷 연산자와 결합하여 엘리먼트 속성 중 하나를 검색할 수 있다. (예를 들어 ${urls[3].protocol}).

EL이 동적 애트리뷰트 값을 정의하는 간한한 언어로서 작용한다고 볼 때, 자바 접근자와는 다른 EL 접근자의 재미있는 특성 중 하나는 null에 적용될 때 예외를 던지지 않는다는 점이다. EL 접근자가 적용되는 객체(예를 들어 ${foo.bar}${foo["bar"]}foo 식별자)가 null이면 접근자 적용 결과 역시 null이다. 이는 대부분의 경우, 도움이 되는 일이다.

마지막으로 닷 연산자와 브래킷 연산자는 상호 교환될 수 있다. 예를 들어 ${user["firstName"]}user 객체의 firstName 속성을 검색하는데 사용될 수 있다. ${commands.dir}commands 맵에서 "dir" 키와 관련된 값을 반입하는데 사용될 수 있는것과 같은 이치이다.

연산자(Operators)

식별자와 접근자를 사용하여 EL은 애플리케이션 데이터(범위 변수를 통해 노출) 또는 환경 관련 정보(EL 내장 객체를 통해 노출)를 포함하고 있는 객체 계층을 트래버스 할 수 있다. 그와 같은 데이터에 간단히 접근하는 것은 많은 JSP 애플리케이션에 필요한 표현 로직을 구현하는데 종종 부적합하다.

EL에는 EL 익스프레션으로 접근된 데이터를 조작 및 비교할 여러 연산자를 포함하고 있다. 이러한 연산자들을 표 2에 요약했다.

표 2. EL 연산자

 

Category 연산자
산술 +, -, *, / (or div), % (or mod)
관계형 == (or eq), != (or ne), < (or lt), > (or gt), <= (or le), >= (or ge)
논리 && (or and), || (or or), ! (or not)
타당성검사 empty

산술 연산자는 더하기, 빼기, 나누기를 지원한다. 다른 연산자들도 제공된다. 나누기와 나머지 연산자들은 비 상징 이름들이라는 대안을 갖고 있다. 산술 연산자의 사용법을 설명하는 예제 익스프레션은 Listing 5에 설명되어 있다. 산술 연산자를 한 쌍의 EL 익스프레션에 적용한 결과는 그러한 익스프레션에 의해 리턴된 숫자 값에 대한 연산자에 적용한 결과이다.


Listing 5. 산술 연산자를 사용하는 EL 익스프레션

${item.price * (1 + taxRate[user.address.zipcode])}

 

관계형 연산자는 숫자 또는 텍스트 데이터를 비교할 수 있도록 한다. 비교 결과는 부울 값으로서 리턴된다. 논리적 연산자는 부울 값이 결합될 수 있도록 하며 새로운 부울 값을 리턴한다. EL 논리적 연산자는 중첩된 관계형 연산자 또는 논리적 연산자의 결과에 적용될 수 있다. (Listing 6).


Listing 6. 관계형 연산자 및 논리적 연산자를 사용하는 EL 익스프레션

${(x >= min) && (x <= max)}

 

EL 연산자는 empty 이다. 데이터의 타당성 검사에 특히 유용하다. empty 연산자는 하나의 익스프레션을 인자로 취한다.(${empty input}). 그리고 익스프레션이 empty 값으로 계산했는지의 여부를 나타내는 부울 값을 리턴한다. null로 계산한 익스프레션은 empty로 간주된다. 어떤 엘리먼트도 없는 컬렉션이나 어레이와 같다. empty 연산자는 인자가 길이가 0인 String으로 계산했다면 true로 리턴한다.

EL 연산자의 우선순위는 표 3에 정리되어 있다. Listing 5와 6에 제안된 것 처럼 괄호는 그룹 익스프레션에 사용되고 일반적인 우선순위를 따른다.

표 3. EL 연산자 우선순위 (위->아래, 왼쪽->오른쪽)

 

[], .
()
unary -, not, !, empty
*, /, div, %, mod
+, binary -
() <, >, <=, >=, lt, gt, le, ge
==, !=, eq, ne
&&, and
||, or

리터럴(Literals)

숫자, 캐릭터 스트링, 부울, null은 EL 익스프레션에서 리터럴 값으로 지정될 수 있다. 캐릭터 스트링은 싱글 쿼트 또는 더블 쿼트로 범위가 지정된다. 부울 값은 truefalse로 계산된다.

Taglib 지시문

앞서 언급했지만 JSTL 1.0에는 네 개의 커스텀 태그 라이브러리가 포함되어 있다. 익스프레션 언어로 JSTL 태그의 인터랙션을 설명하기 위해 JSTL core 라이브러리에서 여러 태그들을 검토할 것이다. 모든 JSP 커스텀 태그 라이브러리로 true가 된다면 taglib 지시문은 이 라이브러리 태그를 사용할 수 있는 페이지에 포함되어야한다. 이 특정 라이브러리에 대한 지시문은 Listing 7에 나타나있다.


Listing 7. JSTL core 라이브러리의 EL 버전용 테그립 지시문

<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>

 

실제로 JSTL core 라이브러리에 상응하는 두 개의 Taglib 지시문이 있다. JSTL텐에서 EL은 옵션이기 때문이다. JSTL 1.0 의 네 개의 커스텀 태그 라이브러리들은 동적 애트리뷰트 값을 지정할 때 EL 대신 JSP 익스프레션을 사용하는 대안 버전을 갖고있다. 이러한 대안 라이브러리는 JSP의 전통적인 요청시간 애트리뷰트 값에 의존하기 때문에 RT 라이브러리로 일컬어진다. 반면 익스프레션 언어를 사용하는 것은 EL 라이브러리라고 한다. 개발자들은 대안 Taglib 지시문을 사용하는 각각의 라이브러리의 버전들을 구별한다. RT 버전의 코어 라이브러리를 사용하기 위한 지시문은 Listing 8에 나와있다. 하지만 지금은 EL에 집중해야 하기 때문에 지금 필요한 것은 이 지시문들 중 첫 번째 것이다.


Listing 8. RT 버전의 JSTL core 라이브러리용 태그립 지시문

<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c_rt" %>

 

변수 태그

첫 번째 JSTL 커스텀 태그는 <c:set> 액션이다. 이미 언급했듯이 범위 변수는 JSTL에서 핵심적인 역할을 하고 <c:set> 액션은 태그 기반의 매커니즘을 제공하여 범위 변수의 생성 및 설정에 쓰인다. 이 액션의 신택스는 Listing 9와 같다. var 애트리뷰트는 범위 변수 이름을 정하고 scope 애트리뷰트는 변수가 머물게 될 범위를 나타내고, value 애트리뷰트는 변수가 될 값을 지정한다. 지정된 변수가 이미 존재하면 지시된 값으로 할당된다. 그렇지 않다면 새로운 범위 변수가 만들어지고 그 값으로 초기화된다.


Listing 9. <c:set> 액션 신택스

<c:set var="name" scope="scope" value="expression"/>

 

scope 애트리뷰트는 선택적이며 page로 기본 설정되어 있다.

<c:set>의 두 예제는 Lisitng 10에 설명되어 있다. 첫 번째 예제에서 세션 범위 변수는 String 값으로 설정된다. 두 번째에서는 익스프레션은 숫자 값을 설정하는데 사용된다. square라는 페이지 범위 변수는 x 라는 요청 매개변수 값을 배가시킨 결과로 할당된다.


Listing 10. <c:set> 액션 예제

<c:set var="timezone" scope="session" value="CST"/>
<c:set var="square" value="${param['x'] * param['x']}"/>

 

애트리뷰트를 사용하는 대신 범위 변수용 값을 <c:set> 액션의 바디 콘텐트로 설정할 수 있다. 이러한 접근방식을 사용하여 Listing 10의 첫 번째 예제를 Listing 11과 같이 재작성할 수 있다. 더욱이 <c:set> 태그의 바디 콘텐트가 커스텀 태그를 적용하는 것도 가능하다. <c:set>의 바디 안에서 만들어진 모든 콘텐트는 String 값 같이 지정된 변수에 할당된다..


Listing 11. 바디 콘텐트를 통해 <c:set> 액션용 값 지정하기

<c:set var="timezone" scope="session">CST</c:set>

 

JSTL core 라이브러리에는 범위 변수를 관리하는 두 번째 태그(<c:remove>)가 포함되어 있다. 이름에서 시사되는 바와 같이 <c:remove> 액션은 범위 변수를 지우는데 사용되고 두 개의 애트리뷰트를 취한다. var 애트리뷰트는 제거될 변수를 명명하고 선택적인 scope 애트리뷰트는 제거되어야 할 범위를 나타낸다. (Listing 12).


Listing 12. <c:remove> 액션 예제

<c:remove var="timezone" scope="session"/>

 

아웃풋

<c:set> 액션은 익스프레션의 결과가 범위 변수로 할당될 수 있도록 하는 반면 개발자들은 익스프레션 값을 저장하는 대신 간단히 디스플레이하기를 원한다. 이는 JSTL의 <c:out> 커스텀 태그의 몫이다. (Listing 13). 이 태그는 value 애트리뷰트에서 지정된 익스프레션을 계산한다. 그런다음 결과를 프린트한다. 선택적 default 애트리뷰트가 지정되면 value 애트리뷰트의 익스프레션이 null 또는 비어있는 String으로 계산될 때 <c:out> 액션은 값을 프린트한다.


Listing 13. <c:out> 액션 신택스

<c:out value="expression" default="expression" escapeXml="boolean"/>

 

escapeXml 애트리뷰트 또한 선택사항이다. "<", ">", "&" 같은 캐릭터가 <c:out> 태그에 의해 아웃풋 될 때 종료되는지의 여부를 제어한다. escapeXml이 true로 설정되어 있다면 이 캐릭터들은 상응하는 XML 인터티(<, >, &)로 바뀐다.

예를 들어, user라는 세션 범위 변수가 있다고 가정해보자. 이것은 사용자에 대한 usernamecompany라는 두 개의 속성들을 정의하는 클래스의 인스턴스이다. 이 객체는 사용자가 사이트에 접근할 때마다 세션에 할당된다. 하지만 이 두 개의 속성들은 사용자가 실제로 로그인하기 전까지 설정되지 않는다. (Listing 14). 일단 사용자가 로그인하면 "Hello"가 디스플레이 되고 뒤따라서 사용자 이름과 감탄부호가 나온다. 사용자가 로그인하기 전에 여기에서 생긴 콘텐트는 "Hello Guest!" 라는 구(phrase)가 된다. 이 경우 username 속성이 초기화되지 않았기 때문에 <c:out> 태그는 default 애트리뷰트 값을 프린트한다.


Listing 14. <c:out> 액션 예제 (디폴트 콘텐트)

Hello <c:out value="${user.username}" default=="Guest"/>!

 

<c:out> 태그의 escapeXml 애트리뷰트를 사용하는 Listing 15를 보자. company 속성이 자바 String 값인 "Flynn & Sons"으로 설정되었다면 이 액션에서 생긴 콘텐트는 Flynn & Sons이 된다. 이 액션이 HTML 또는 XML 콘텐트를 만드는 JSP 페이지의 일부라면 이 캐릭터의 스트링 중간에 있는 앰퍼샌트 부호는 HTML 또는 XML이 문자를 제어하고 이 콘텐트의 렌더링 또는 파싱을 방해하는것으로 해석하고 끝난다. escapeXml 애트리뷰트의 값이 true로 설정되면 생성된 콘텐트는 Flynn & Sons이 된다. 이 콘텐트를 만나는 브라우저 또는 파서는 인터프리테이션에 아무 문제가 없다. HTML과 XML이 JSP 애플리케이션에서 가장 일반적인 콘텐트 유형이라면 escapeXml 애트리뷰트의 디폴트 값이 true라는 것은 놀라운 일이 아니다.


Listing 15. <c:out> 액션 예제)

<c:out value="${user.company}" escapeXml=="false"/>

 

디폴트 값으로 변수 설정하기

동적 데이터를 단순하게 하는 것 외에도 디폴트 값을 지정하는 <c:out>의 기능은 <c:set>을 통해 변수 값을 설정할 때에도 유용하다. 범위 변수에 할당된 값이 <c:set> 태그의 바디 콘텐트로 지정될수 있고 value 애트리뷰트로서도 가능하다. <c:out> 액션을 <c:set> 태그의 바디 콘텐트에 중첩하여 변수 할당은 이것의 디폴트 값을 이용할 수 있다. (Listing 11).

이러한 접근 방식은 Listing 16에도 설명되어 있다. 외부 <c:set> 태그의 작동은 단순하다.


Listing 16. <c:set>과 <c:out> 결합: 디폴트 변수 값 제공

<c:set var="timezone" scope=="session">
   <c:out value="${cookie['tzPref'].value}" default=="CST"/>
</c:set>

 

소셜 북마크

요청에 제공된 tzPref 라는 이름의 쿠키가 없다. 내장 객체를 사용한 검색은 null이 된다는 것을 의미한다. 익스프레션은 전체적으로 null을 리턴한다. value 애트리뷰트를 계산한 값이 null 이기 때문에 <c:out> 태그는 default 애트리뷰트를 계산한 결과를 아웃풋한다.

 

참고자료

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

log4j  (0) 2012.05.08
JSTL- 사용 하기  (0) 2012.05.08
JSP 에러코드 모음  (0) 2012.05.08
include  (0) 2012.05.08
IBatis  (0) 2012.05.08
Posted by 사라링

JSP 에러코드 모음

 | JSP
2012. 5. 8. 18:15

 JSP 각종 에러코드 번호입니다. 에러 발생시 참고하세요.

빈번하게 자주 발생하는 에러코드는 200, 404, 500 입니다.

참고로 한말씀을 드리자면 에러를 많이 봐야 프로그램 코딩 실력이 늡니다.

100 Continue

101 Switching Protocols

200 OK (에러 없이 전송 성공)

202 Accepted - 서버가 클라이언트의 명령을 받음

203 Non-authoritative Information - 서버가 클라이언트 요구 중 일부만 전송함

204 Non Content - 클라이언트 요구를 처리했으나 전송할 데이터가 없음


205 Reset Content

 

206 Partial Content

 

300 Multiple Choices - 최근에 옮겨진 데이터를 요청함

 

301 Moved Permanently - 요구한 데이터를 변경된 임시 URL 에서 찾음

 

302 Move Permanently - 요구한 데이터가 변경된 URL 에 있음을 명시함

 

303 See Other - 요구한 데이터를 변경하지 않았기 때문에 문제가 있음

 

304 Non modified

 

305 Use Proxy

 

400 Bad Request, 요청실패 - 문법상 오류가 있어서 서버가 요청 사항을 이해하지 못함

 

401.1 Unauthorized, 권한없음 - 접속 실패. 서버에 로그온 하려는 요청 사항이 서버에

들어있는 권한과 비교했을 시 맞지 않을 경우 발생

(접근 권한을 받기 위해 서버 운영자에게 요청해야 함)

 

401.2 Unauthorized, 권한없음 - 서버 설정으로 인한 접속 실패. 서버에 로그온 하려는

요청 사항이 서버에 들어있는 권한과 비교했을 시 맞지

않을 경우 발생

(www-authenicate head field 를 전송하지 않아서 발생)

 

401.4 Unauthorized, 권한없음 - 필터에 의한 권한 부여 실패. 서버에서 서버에 접속하는

사용자들을 확인하기 위해 설치된 필터 프로그램이 있음을

의미함.

(서버 접속에 이용되는 인증 과정이 필터 프로그램에 의해

거부된 것)

 

402 Payment Required, 예약됨

 

402.3 Unauthorized, 권한없음 - 자원에 대한 ACL 에 기인한 권한 없음.

클라이언트가 특정 자원에 접근할 수 없을 때 발생

페이지가 될 수도 있고, 클라이언트의 주소 입력란에

명기된 파일일 수도 있고, 클라이언트가 해당 주소로

접속할 때 이용되는 또 다른 파일일 수도 있다.

(접근할 주소를 확인해보고 서버 운영자에게 자신의 접근

권한 유무 확인)

 

403.1 Forbidden, 금지 - 수행 접근 금지. CGI 나 ISA-PI, 혹은 수행시키지 못하도록 되어

있는 디렉토리 내의 실행 파일을 수행시키려고 했을 때 발생

 

403.2 Forbidden, 금지 - 읽기 접근 금지. 브라우저가 접근한 디렉토리에 가용한 디폴트

페이지가 없을 경우에 발생함.

 

403.4 Forbidden, 금지 - SSL 필요. 접근하려는 페이지가 SSL 로 보안 유지되고 있는 것일

때 발생함

 

403.5 Forbidden, 금지 - SSL 128 필요. 접근하려는 페이지가 SSL로 보안 유지되고 있을

때 발생, 브라우저가 128 bit 의 SSL 을 지원하는지를 확인해야함

 

403.6 Forbidden, 금지 - IP 주소 거부됨. 서버가 사이트에 접근이 허용되지 않는 IP 주소로

사용자가 접근하려 했을 때 발생

 

403.7 Forbidden, 금지 - 클라이언트 확인 필요. 접근하려는 자원이 서버가 인식하기 위해

브라우저에게 클라이언트 SSL을 요청하는 경우 발생. 자원을 이용

할 수 있는 사용자임을 입증하는데 사용됨

 

403.8 Forbidden, 금지 - 사이트 접근 거부. 웹 서버가 요청사항을 수행하고 있지 않았거나,

해당 사이트에 접근하는 것을 허락하지 않았을 경우에 발생함

 

403.9 Forbidden, 접근금지 - 연결된 사용자수 과다. 웹 서버가 busy한 상태에 있어서

요청을 수행할 수 없을 경우 발생

 

403.10 Forbidden, 접근금지 - 설정이 활실하지 않음. 웹 서버의 설정 부분에 문제가 있을

경우 발생

 

403.11 Forbidden, 접근 금지 - 패스워드 변경. 사용자 인증 단계에서 잘못된 패스워드를 입력

했을 경우 발생

 

403.12 Forbidden, 접근 금지 - Mapper 접근 금지. 클라이언트 인증용 맵(map)이 해당 웹

사이트에 접근하는 것을 거부할 경우에 발생함

 

404 Not Found, 문서를 찾을 수 없음 - 클라이언트가 요청한 문서를 찾지 못한 경우에 발생.

 

404.5 Unauthorized, 권한없음 - ISA PI/CGI 어플리케이션에 의한 권한 부여 실패.

서버의 어드레스에 ISA PI 나 CGI 프로그램이 설치되어

있어 사용자의 권한을 검증함. 서버 접속에 이용되는 인증

과정이 프로그램에 의해 거부됨.

 

405 Method not allowed, 메소드 허용 안 됨 - Request 라인에 명시된 메소드를 수행

하기 위한 해당 자원의 이용이 허용되지

않았을 경우 발생

 

406 Not Acceptable, 받아들일 수 없음 - 요청 사항에 필요한 자원은 요청 사항으로 전달

된 Accept header 에 따라

"Not Acceptable" 내용을 가진 사항이 있

을 경우에 발생

 

407 Proxy Authenication Required, Proxy 인증이 필요함

- 해당 요청이 수행되도록 Proxy 서버에게 이증을 받아야 할 경우 발생

 

408 Request timeout, 요청 시간이 지남

 

409 Conflict

 

410 Gone, 영구적으로 사용할 수 없음.

 

411 Length Required

 

412 Precondition Failed, 선결조건 실패 - Request-header Field 에 하나 이상에

선결조건에 대한 값이 서버에서 테스트 결과

false 로 나왔을 경우 발생

 

413 Request entity too large

 

414 Request-URI too long, 요청한 URI 가 너무 김 - 요청한 URI 의 길이가 너무 길어

서 서버가 요청 사항의 이행을

거부했을 경우 발생

 

415 Unsupported media type

 

500 Internal Server Error, 서버 내부 오류 - 웹 서버가 요청사항을 수행할

수 없을 경우에 발생.

(코딩 실수가 대부분)

 

501 Not Implemented, 적용 안 됨 - 웹 서버가 요청사항을 수행하는데 필요한 기능을

지원하지 않는 경우에 발생

 

502 Bad geteway, 게이트웨이 상태 나쁨 - 게이트웨이 상태가 나쁘거나 서버의 과부하

상태일 때 발생

 

503 Service Unavailable, 서비스 불가능 - 서비스가 현재 멈춘 상태 또는 현재 일시적인

과부하 또는 관리 상황일 때 발생될 수 있다.

 

504 Geteway timeout

 

505 HTTP Version Not Supported

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

JSTL- 사용 하기  (0) 2012.05.08
JSTL  (0) 2012.05.08
include  (0) 2012.05.08
IBatis  (0) 2012.05.08
Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
Posted by 사라링

include

 | JSP
2012. 5. 8. 18:15

JSP - include //

<%@ include file="파일경로.jsp" %>

 

JSP로 작업하였을 경우 웹상에서 한글이 깨져 보일때

<html>위에 (즉, 페이지 맨 위)

 

<%@ page contentType="text/html; charset=EUC-KR"%>

or

<%@ page contentType="text/html; charset=euc-kr" language="java" import="java.sql.*" errorPage="" %>


을 넣어준다


------------------------------------------------------------------------------------------------------

 

JSP에서 인크루드 시킬때는

 

<% @include file="../inc/top_menu.jsp" %>

 

삽입한다

 

 

------------------------------------------------------------------------------------------------------

 

* 참고로 HTML에서 인크루드 삽입할때는

  <!--#include file="파일경로" -->

 

사용방법 1 :프로그램적 사용.

인크루드대상파일.jpg

 

이렇게 파일을 생성 // list 메서드. 변수 등의 자주 사용 하는 것을 파일로 생성 한다. 첫단에 붙일 경우 prelude ,마지막 단에 붙일 경우 coda 로 사용 하면 됨.

web.xml 파일의 수정

webxml_추가.jpg

이렇듯 web.xml 에 추가 시키면 메서드 이름및 변수 이름을 직접 사용 바로 사용 하여 사용가능 하다.

바로사용.jpg

 

사용방법2:디자인적 사용(자주사용 하는 부분 메뉴&보조메뉴&FOOTER)

먼저 top.jsp 를 생성. 

top.jpg

이후 ./ 메인jsp 에 인크루드 하여야 한다.

메인안의top.jpg

여기서 div의 아이디는 css 에서 내부 설정을 위해 필요 한 부분 이며 DIV 의 id 를 통해 css 로 설정한 경우 내부의 ul li 도 설정 이 가능 하다는것을 알수 있다.

중간 <jsp:param value="<%=msg %>" name="msg"/> 경우 초기 생성시 에 이 div 에 대한 부분에 대한 권한을 top.jsp 로 넘겨 주면서 msg변수의 값을

파라미터 형식으로 보내 줄수 있다. 여기서. 보내 줄때 기본적으로 로마 기호를 따름으로. top.jsp 에서는 euc-kr 로 인코딩 하여도

 ex)<%request.setCharacterEncoding("utf-8"); %> -- 하더라도 보내주는 값이 로마xx 에 따름으로 한글이 깨져 출력 된다 따라서 코딩 상단에.

 path.jsp 에서  .// <%request.setCharacterEncoding("utf-8"); %> 를 해줘야 한다.

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

JSTL  (0) 2012.05.08
JSP 에러코드 모음  (0) 2012.05.08
IBatis  (0) 2012.05.08
Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
Posted by 사라링

IBatis

 | JSP
2012. 5. 8. 18:15

 

기본_구조(1).jpg

아이바 티스 기본구조 이다. 

최초 실행 해야 할것은 ibatis-2.3.4.726.jar  파일과 ojdbc14.jar 을 빌더 패치 하여 프로젝트 에 Jars Import 시켜야만 한다.

그리고 프로젝트에서 new Folder 생성후 폴더에서 마우스 오른쪽 bulder path -> use as source Folder  클릭하여 소스폴더로 변경 한다.

소스폴더로 변경된 conf 폴더에 SqlMapConfig.properties 과 SqlMapConfig.xml 파일을 생성 한다. 생성되어 있는것을 옮겨 놓아 수정 하거나하자 .

=================================================================================================================================

 

SqmlMapConfigproperties.jpg

SqlMapConfig.properties  파일 이다.

내용을 보면 dirver 에서 jdbc 패키지를 이용 하여 오라클에 접속을 시도 하며

url 을통해 오라클서버로 접속 함을 알수 있다

@ 이후에 서버명:포트번호:오라클버전 등을 적는것을 볼수 있다.

=================================================================================================================================

 

SqmlMapConfig.jpg

 

SqlMapConfig.xml 파일의 소스 이다.

앞에서 만든 SqlMapConfig.properties 를 이용 하여 정의 됨을 알수 있다.

기본 셋팅후 JDBC 의 기본 구조를 가지고 자신을 sqlMap 화 시킴으로서 사용 할수 있도록 대기 상태화 된다.

=========================================================================================================================================

 

memberxml.jpg

member.xml 파일 이다.

보기와 같이 sqlMap 생성 과정 에서 select 문을 질의 하고 있는데 그 값을 가져 오는 쿼리 문을 id화 시켜 정의 하고 있다. getMemberName 과 getMembers 두개가 정의 됨을 알수 있다.

따라서 생성 후에 <select></select > 안에 사용 하는 쿼리 문장을 추가,수정,삭제 등을 통해 알맞게 변동 하면서 id 값을 다르게 하여 명백하게 후 개발과정에서

알아 볼수 있도록 정의 하면 좋을것 이다. 이에 추가 하여 다이나믹화 시킬수 있는데 그것은 따로. 셀렉트 안에 <dynamic> 문을 추가 하여 조건에 따른 셀렉문장 지정

========================================================================================================================================================================

 

IbatisMain.jpg

 

Ibatise 의 메인 클래스다 실제 JSP 사용중에 이렇게 만들지는 않지만 잘 만들어져있는지 테스트 하기 위해 만들어져 있다. 실행 환경및 소스를 분석해 보자.

먼저 IbatisService 를 getInstance ghk 화 하여 service라는 이름으로 변수화 하였다. 이후.

MemberBean 을 제너레이션 하여 ArrayList 를 생성 하며 기본 값을 null 을 주었다.

이후 a001 의 param 을 생성 하며 조건 값을 주었다. 즉 오라클의 member 테이블의 id 값이 "a001" 인 것을 결과 값으로 내놓기 위한 것이다. 이하 서비스를 설명 하기 위해 IbatisService를 확인 해보자

===================================================================================================================================================================================

IbatisService.jpg

IbatisService.java 이다.

소스 분석을 해보자. 

소스 윗부분부터 보면 알수 있듯이 IbatisServie 의 경우 생성을 위해 기본적으로 getInstace 화 하여 (IbatisService.getInstace()) 생성 하게 만들었는데

그 이유는 그냥 만들어도 되지만 메모리의 효율을 위해 싱클톤 패턴으로 생성 한것이다. Ibatis의 기본생성자는 private 화 되어 보통 만들듯이 new IbatisServise 로는 만들수 없다.

반드시 IbatisService 의 getInstance() 메서드를 사용 하염 만들게 되는대 이 메서드의 클래스 내의 service 생성자를 보유 할수 있게 되며 주소 값이 공유 되게 되어 있다.

즉 최초 만들시에는 service 생성이 안되어 있음으로 service 가 새로 생성되어 보내 주고. 후에 생성될 경우 service 는 static 화 되어 있음으로 모두 공유 하게 되어 service 는

null이 아니며 최초 만들었던 service 를 돌려 주게 되며 실제 사용 할때는 변수명의 차이는 있을 지언정 같은 메서드를 사용 하게 됨으로서 같은 기능의 메서드를 구지

여러개 만들지 않게 되어 메모리를 절약 할수 있다.  이하 IbatisDao 에서도 같은 방식으로 코딩을 하게 됨으로 참고 하자. 내부 메서드는 구조만 보면 될듯 하다.

===========================================================================================================================================================

IbatisDao.jpg

IbatisDao 이다. 이 장에서 싱클톤 분야는 제외 하겠따. 중요 한것은 메서드의 중복 성을 뛰며 자체 적으로 생성한 클래스를 이용 하여 건너 뛰며 실행 하게 되는것이다 즉

main - > service -> dao->sqlMapClient(SqlMapConfig.xml)->JDBC->오라클 형식으로 만드는것은 어찌 보면 효율이 없어 보인다. 단순히 main과 sqlMapClient 는 제쳐

두고라도 service dao 등의 클래스 건너 뛰기는 일견 효율성이 떨어져 보이지만. 만약 가져온 데이터의 가공 또는 연산등이 필요 한 경우

dao 는 sql 과 jdbc 등의 파일과 연결 됨으로 그 파일을 수정 하기보다는 service 라는 클래스의 완충 장치를 통해 수정 이 가능 하며 dao ~ Jdbc 사이의 가져오는 연산 량을

범위를 높인후 또다른 service2 와 연동 하여 각자 원하는 메서드를 통해 값을 가져오는 것도 가능 하다 . 이 모든 기능은 각기 같은 메모리 값을 가지고 실행 되기 때문에 효율적이다. 

 

=======================================================================================================================================================================

 

BuilderSqlMapClient.jpg

 

BuildedSqlMapClient  클래스 이다. 소스 에서 sqlMapConfig.xml 등을 통해 JDBC 에 접근 함을 알수 있다. 여기서 Ibatis.jar 의 SqlMapClient 에 접근 하며 만들어 진다.

이상 2012년 3월 15일 Ibatise 의 기본 학습을 마치며 미진한 부분은 추가로 지속해서 수정 해야 겠다;;;

 

 

다음으로 신세계 마이바 티스를 공부해 보자.

 

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

JSP 에러코드 모음  (0) 2012.05.08
include  (0) 2012.05.08
Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
Posted by 사라링
  1. 필터를 이용한 웹 프로그래밍 Part1, 필터란 무엇인가! 
    서블릿 2.3에 새롭게 추가된 필터가 무엇이며, 어떻게 구현하는지에 대해서 살펴본다. 

                          요약 
    서블릿 2.3 규약은 2.2 규약에서 지적되었던 단점을 보완하기 위해서 노력했으며 그 노력 중의 하나가 바로 필터(Filter)이다. 이 글에서는 필터가 무엇이며 필터를 어떻게 구현하는 지에 대해서 살펴본다. 

    프로바이더: 최범균 
    --------------------------------------------------------------------------------
    필터!! 


    현 재 서블릿 2.3 규약은 Proposed Final Draft 2 상태에 있다. 조만간 서블릿 2.3과 JSP 1.2 최종 규약 이 발표될 것으로 예상되며 우리는 당연히 새롭게 추가된 것들이 무엇인지에 관심이 쏠리게 된다. 서블릿 2.3 규약에 새롭게 추가 된 것 중에 필자가 가장 눈여겨 본 것은 바로 필터(Filter) 기능의 추가이다.

    그 동안 필자는 서블릿 2.2 와 JSP 1.1에 기반하여 웹 어플리케이션을 구현하는 동안 몇몇 부분에서 서블릿 2.2 규약의 부족한 면을 느낄 수 있었으 며, 특히 사용자 인증 처리, 요청 URL에 따른 처리, XSL/T를 이용한 XML 변환(Transformation) 등 개발자들 이 직접 설계해야 하는 부분이 많았었다. 하지만, 이제 서블릿 2.3 규약에 새롭게 추가된 필터(Filter)를 사용함으로써 개발 자들이 고민해야 했던 많은 부분을 덜어낼 수 있게 되었다. 이 글에서는 필터가 무엇이며 어떻게 필터를 구현하는지에 대해 살펴볼 것 이다.

    간단하게 말해서, 필터는 'HTTP 요청과 응답을 변경할 수 있는 재사용가능한 코드'이다. 필터는 객체의 형 태로 존재하며 클라이언트로부터 오는 요청(request)과 최종 자원(서블릿/JSP/기타 문서) 사이에 위치하여 클라이언트의 요 청 정보를 알맞게 변경할 수 있으며, 또한 필터는 최종 자원과 클라이언트로 가는 응답(response) 사이에 위치하여 최종 자원 의 요청 결과를 알맞게 변경할 수 있다. 이를 그림으로 표현하면 다음과 같다.

     
    그림1 - 필터의 기본 구조 
    그 림1에서 자원이 받게 되는 요청 정보는 클라이언트와 자원 사이에 존재하는 필터에 의해 변경된 요청 정보가 되며, 또한 클라이언트 가 보게 되는 응답 정보는 클라이언트와 자원 사이에 존재하는 필터에 의해 변경된 응답 정보가 된다. 위 그림에서는 요청 정보를 변 경하는 필터와 응답 정보를 변경하는 필터를 구분해서 표시했는데 실제로 이 둘은 같은 필터이다. 단지 개념적인 설명을 위해 그림1 과 같이 분리해 놓은 것 뿐이다.

    필터는 그림1에서처럼 클라이언트와 자원 사이에 1개가 존재하는 경우가 보통이지만, 여러 개의 필터가 모여 하나의 체인(chain; 또는 사슬)을 형성할 수도 있다. 그림2는 필터 체인의 구조를 보여주고 있다.

     
    그림2 - 필터 체인 
    그 림2와 같이 여러 개의 필터가 모여서 하나의 체인을 형성할 때 첫번째 필터가 변경하는 요청 정보는 클라이언트의 요청 정보가 되지 만, 체인의 두번째 필터가 변경하는 요청 정보는 첫번째 필터를 통해서 변경된 요청 정보가 된다. 즉, 요청 정보는 변경에 변경 에 변경을 거듭하게 되는 것이다. 응답 정보의 경우도 요청 정보와 비슷한 과정을 거치며 차이점이 있다면 필터의 적용 순서가 요 청 때와는 반대라는 것이다. (그림2를 보면 이를 알 수 있다.)

    필터는 변경된 정보를 변경하는 역할 뿐만 아니 라 흐름을 변경하는 역할도 할 수 있다. 즉, 필터는 클라이언트의 요청을 필터 체인의 다음 단계(결과적으로는 클라이언트가 요청 한 자원)에 보내는 것이 아니라 다른 자원의 결과를 클라이언트에 전송할 수 있다. 필터의 이러한 기능은 사용자 인증이나 권한 체크 와 같은 곳에서 사용할 수 있다.



    필터 관련 인터페이스 및 클래스 


    필터를 구현 하는데 있어 핵심적인 역할을 인터페이스 및 클래스가 3개가 있는 데, 그것들은 바로 javax.servlet.Filter 인터페이 스, javax.servlet.ServletRequestWrapper 클래 스, javax.servlet.ServletResponseWrapper 클래스이다. 이 중 Filter 인터페이스는 클라이언트 와 최종 자원 사이에 위치하는 필터를 나타내는 객체가 구현해야 하는 인터페이스이다. 그리 고 ServletRequestWrapper 클래스와 SerlvetResponseWrapper 클래스는 필터가 요청을 변경한 결 과 또는 응답을 변경할 결과를 저장할 래퍼 클래스를 나타내며, 개발자는 이 두 클래스를 알맞게 상속하여 요청/응답 정보를 변경하 면 된다.

    Filter 인터페이스

    먼저, Filter 인터페이스부터 살펴보자. Filter 인터페이스에는 다음과 같은 메소드가 선언되어 있다.

    public void init(FilterConfig filterConfig) throws ServletException 
    필터를 웹 콘테이너내에 생성한 후 초기화할 때 호출한다. 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException 
    체인을 따라 다음에 존재하는 필터로 이동한다. 체인의 가장 마지막에는 클라이언트가 요청한 최종 자원이 위치한다. 
    public void destroy() 
    필터가 웹 콘테이너에서 삭제될 때 호출된다. 
    위 메 소드에서 필터의 역할을 하는 메소드가 바로 doFilter() 메소드이다. 서블릿 콘테이너는 사용자가 특정한 자원을 요청했 을 때 그 자원 사이에 필터가 존재할 경우 그 필터 객체의 doFilter() 메소드를 호출하며, 바로 이 시점부터 필터가 작용하 기 시작한다. 다음은 전형적인 필터의 구현 방법을 보여주고 있다.


      public class FirstFilter implements javax.servlet.Filter {
      
         public void init(FilterConfig filterConfig) throws ServletException {
            // 필터 초기화 작업
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response
                              FilterChain chain)
                              throws IOException, ServletException {
            // 1. request 파리미터를 이용하여 요청의 필터 작업 수행
            // 2. 체인의 다음 필터 처리
            chain.doFilter(request, response);
            // 3. response를 이용하여 응답의 필터링 작업 수행
         }
         
         public void destroy() {
            // 주로 필터가 사용한 자원을 반납
         }
      }


    위 코 드에서 Filter 인터페이스의 doFilter() 메소드는 javax.servlet.Servlet 인터페이스 의 service() 메소드와 비슷한 구조를 갖는다. 즉 만약 클라이언트의 자원 요청이 필터를 거치는 경우, 클라이언트의 요청 이 있을 때 마다 doFilter() 메소드가 호출되며, doFilter() 메소드는 서블릿과 마찬가지로 각각의 요청에 대해서 알 맞은 작업을 처리하게 되는 것이다.

    위 코드를 보면 doFilter() 메소드는 세번째 파라미터 로 FilterChain 객체를 전달받는 것을 알 수 있다. 이는 클라이언트가 요청한 자원에 이르기까지 클라이언트의 요청이 거쳐가 게 되는 필터 체인을 나타낸다. FilterChain을 사용함으로써 필터는 체인에 있는 다음 필터에 변경한 요청과 응답을 건내 줄 수 있게 된다.

    위 코드를 보면서 우리가 또 하나 알아야 하는 것은 요청을 필터링한 필터 객체가 또 다시 응답 을 필터링한다는 점이다. 위 코드의 doFilter() 메소드를 보면 1, 2, 3 이라는 숫자를 사용하여 doFilter() 메 소드 내에서 이루어지는 작업의 순서를 표시하였는데, 그 순서를 다시 정리해보면 다음과 같다.

    request 파리미터를 이용하여 클라이언트의 요청 필터링
    1 단계에서는 RequestWrapper 클래스를 사용하여 클라이언트의 요청을 변경한다. 
    chain.doFilter() 메소드 호출
    2 단계에서는 요청의 필터링 결과를 다음 필터에 전달한다. 
    response 파리미터를 사용하여 클라이트로 가는 응답 필터링
    3 단계에서는 체인을 통해서 전달된 응답 데이터를 변경하여 그 결과를 클라이언트에 전송한다. 
    1단계와 3단계 사이에서 다음 필터로 이동하기 때문에 요청의 필터 순서와 응답의 필터 순서는 그림2에서 봤듯이 반대가 된다.

    필터의 설정

    필 터를 사용하기 위해서는 어떤 필터가 어떤 자원에 대해서 적용된다는 것을 서블릿/JSP 콘테이너에 알려주어야 한다. 서블릿 규약 은 웹 어플리케이션과 관련된 설정은 웹 어플리케이션 디렉토리의 /WEB-INF 디렉토리에 존재하는 web.xml 파일을 통해서 하 도록 하고 있으며, 필터 역시 web.xml 파일을 통해서 설정하도록 하고 있다.

    web.xml 파일에서 필터를 설정하기 위해서는 다음과 같이 <filter> 태그와 <filter-mapping> 태그를 사용하면 된다.


      <web-app>
         
         <filter>
            <filter-name>HighlightFilter</filter-name>
            <filter-class>javacan.filter.HighlightFilter</filter-class>
            <init-param>
               <param-name>paramName</param-name>
               <param-value>value</param-value>
            </init-param>
         </filter>
         
         <filter-mapping>
            <filter-name>HighlightFilter</filter-name>
            <url-pattern>*.txt</url-pattern>
         </filter-mapping>
         
      </web-app>


    여 기서 <filter> 태그는 웹 어플리케이션에서 사용될 필터를 지정하는 역할을 하며, <filter- mapping> 태그는 특정 자원에 대해 어떤 필터를 사용할지를 지정한다. 위 예제의 경우는 클라이언트가 txt 확장자를 갖 는 자원을 요청할 경우 HithlightFilter가 사용되도록 지정하고 있다.

    <init- param> 태그는 필터가 초기화될 때, 즉 필터의 init() 메소드가 호출될 때 전달되는 파라미터 값이다. 이는 서블릿 의 초기화 파라미터와 비슷한 역할을 하며 주로 필터를 사용하기 전에 초기화해야 하는 객체나 자원을 할당할 때 필요한 정보를 제공하 기 위해 사용된다.

    <url-pattern> 태그는 클라이언트가 요청한 특정 URI에 대해서 필터링을 할 때 사용된다. 서블릿 2.3 규약의 11장을 보면 다음과 같이 url-pattern의 적용 기준을 명시하고 있다.

    '/'로 시작하고 '/*'로 끝나는 url-pattern은 경로 매핑을 위해서 사용된다. 
    '*.'로 시작하는 url-pattern은 확장자에 대한 매핑을 할 때 사용된다. 
    나머지 다른 문자열을 정확한 매핑을 위해서 사용된다. 
    에를 들어, 다음과 같이 <filter-mapping> 태그를 지정하였다고 해 보자.


         <filter-mapping>
            <filter-name>AuthCheckFilter</filter-name>
            <url-pattern>/pds/*</url-pattern>
         </filter-mapping>


    이 경우 클라이언트가 /pds/a.zip 을 요청하든 /pds/b.zip 을 요청하는지에 상관없이 AuthCheckFilter가 필터로 사용될 것이다.

    <url- pattern> 태그를 사용하지 않고 대신 <servlet-name> 태그를 사용함으로써 특정 서블릿에 대한 요청 에 대해서 필터를 적용할 수도 있다. 예를 들면 다음과 같이 이름이 FileDownload인 서블릿에 대해 서 AuthCheckFilter를 필터로 사용하도록 할 수 있다.


         <filter-mapping>
            <filter-name>AuthCheckFilter</filter-name>
            <servlet-name>FileDownload</servlet-name>
         </filter-mapping>
         
         <servlet>
            <servlet-name>FileDownload</servlet-name>
            ...
         </servlet>




    래퍼 클래스 


    필 터가 필터로서의 제기능을 하기 위해서는 클라이언트의 요청을 변경하고, 또한 클라이언트로 가는 응답을 변경할 수 있어야 할 것이 다. 이러한 변경을 할 수 있도록 해 주는 것이 바로 ServletRequestWrapper 와 ServletResponseWrapper이다. 서블릿 요청/응답 래퍼 클래스를 이용함으로써 클라이언트의 요청 정보를 변경하 여 최종 자원인 서블릿/JSP/HTML/기타 자원에 전달할 수 있고, 또한 최종 자원으로부터의 응답 결과를 변경하여 새로운 응 답 정보를 클라이언트에 보낼 수 있게 된다.

    서블릿 요청/응답 래퍼 클래스로서의 역할을 수행하기 위해서 는 javax.servlet 패키지에 정의되어 있는 ServletRequestWrapper 클래스 와 ServletResponseWrapper 클래스를 상속받으면 된다. 하지만, 대부분의 경우 HTTP 프로토콜에 대한 요청/응답 을 필터링 하기 때문에 이 두 클래스를 상속받아 알맞게 구현한 HttpServletRequestWrapper 클래스 와 HttpServletResponseWrapper 클래스를 상속받는 경우가 대부분일 것이다.

    HttpServletRequestWrapper 클 래스와 HttpServletResponseWrapper 클래스는 모두 javax.servlet.http 패키지에 정의되어 있으 며, 이 두 클래스는 각각 HttpServletRequest 인터페이스와 HttpServletResponse 인터페이스에 정의되 어 있는 모든 메소드를 이미 구현해 놓고 있다. 필터를 통해서 변경하고 싶은 정보가 있을 경우 그 정보를 추출하는 메소드를 알맞 게 오버라이딩하여 필터의 doFilter() 메소드에 넘겨주기만 하면 된다. 예를 들어, 클라이언트가 전송한 "company" 파 리머터의 값을 무조건 "JavaCan.com"으로 변경하는 요청 래퍼 클래스는 다음과 같 이 HttpServletRequestWrapper 클래스를 상속받은 후에 getParameter() 메소드를 알맞게 구현하면 된 다.


      package javacan.filter;
      
      import javax.servlet.http.*;
      
      public class ParameterWrapper extends HttpServletRequestWrapper {
         
         public ParameterWrapper(HttpServletRequest wrapper) {
            super(wrapper);
         }
         
         public String getParameter(String name) {
            if ( name.equals("company") ) {
               return "JavaCan.com";
            } else {
               return super.getParameter(name);
            }
         }
      }


    오 버라이딩한 getParameter() 메소드를 살펴보면 값을 구하고자 하는 파라미터의 이름이 "company"일 경 우 "JavaCan.com"을 리턴하고 그렇지 않을 경우에는 상위 클래스(즉, HttpServletRequestWrapper 클래 스)의 getParameter() 메소드를 호출하는 것을 알 수 있다.

    이렇게 작성한 래퍼 클래스는 필터 체인을 통 해서 최종 자원까지 전달되어야 그 효과가 있을 것이다. 즉, 최종 자원인 서블릿/JSP에 서 request.getParameter("company")를 호출했을 때 ParameterWrapper 클래스 의 getParameter() 메소드가 사용되기 위해서는 ParameterWrapper 객체 가 HttpServletRequest 객체를 대체해야 하는데, 이는 Filter 인터페이의 doFilter() 내에 서 ParameterWrapper 객체를 생성한 후 파라미터로 전달받은 FilterChain의 doFilter() 메소드를 호출함 으로써 가능하다. 좀 복잡하게 느껴질지도 모르겠으나 이를 코드로 구현해보면 다음과 같이 간단한다.


      package javacan.filter;
      
      import javax.servlet.*;
      import javax.servlet.http.*;
      
      public class ParameterFilter implements Filter {
         
         private FilterConfig filterConfig;
         
         public ParameterFilter() {
         }
         
         public void init(FilterConfig filterConfig) {
            this.filterConfig = filterConfig;
         }
         
         public void destroy() {
            filterConfig = null;
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response,
                              FilterChain chain)
                              throws java.io.IOException, ServletException {
            // 요청 래퍼 객체 생성
            HttpServletRequestWrapper requestWrapper = 
                         new ParameterWrapper((HttpServletRequest)request);
            // 체인의 다음 필터에 요청 래퍼 객체 전달
            chain.doFilter(requestWrapper, response);
         }
      }


    응답 래퍼 클래스 역시 요청 래퍼 클래스와 비슷한 방법으로 구현된다.

    앞 에서도 언급했듯이 요청 정보의 변경 및 응답 정보 변경의 출발점은 래퍼 클래스이다. XML+XSL/T 기법이나 사용자 인증과 같 은 것들을 최종 자원과 분리시켜 객체 지향적으로 구현하기 위해서 요청/응답 래퍼 클래스를 사용하는 것은 필수적이다. 2부에서 실 제 예를 통해서 어떻게 필터와 요청/응답 래퍼 클래스를 효과적으로 사용할 수 있는 지 살펴보게 될 것이다.

    필터 체인의 순서

    앞 에서 필터는 체인을 형성할 수 있다고 하였다. 체인을 형성한다는 것은 어떤 특정한 순서에 따라 필터가 적용된다는 것을 의미한 다. 예를 들면, 여러분은 '인증필터->파라미터 변환 필터->XSL/T 필터->자원->XSL/T 필터 ->파라미터 변환 필터->인증필터'와 같이 특정 순서대로 필터를 적용하길 원할 것이다. 서블릿2.3 규약은 다음과 같 은 규칙에 기반하여 필터 체인 내에서 필터의 적용 순서를 결정한다.

    url-pattern 매칭은 web.xml 파일에 표기된 순서대로 필터 체인을 형성한다. 
    그런 후, servlet-name 매칭이 web.xml 파일에 표기된 순서대로 필터 체인을 형성한다. 


    결론 


    이 번 1 부에서는 서블릿 2.3 규약에 새롭게 추가된 필터가 무엇인지 그리고 필터를 어떻게 구현하며 또한 필터를 어떻게 서블릿이 나 JSP와 같은 자원에 적용할 수 있는지에 대해서 알아보았다. 아직 구체적으로 필터의 응용방법에 대해서 설명하지 않았기 때문 에 필터의 장점이 머리에 떠오르지 않을것이다. 다음 2 부에서는 구체적으로 필터의 예를 살펴봄으로써 필터의 활용함으로써 얻게 되 는 장점에 대해서 살펴보도록 하자.













    필터를 이용한 웹 프로그래밍 Part2, 필터의 응용! 
    서블릿 2.3에 새롭게 추가된 필터를 이용한 사용자 인증, XSL/T 변환을 구현해본다. 

    요약 
    1부에 이어 2부에서는 사용자 인증 필터와 XSL/T 변환 필터를 구현해 봄으로써 필터를 어떻게 응용할 수 있는 지에 대해서 살펴보도록 하자. 

    프로바이더: 최범균 
    --------------------------------------------------------------------------------
    필터의 응용 


    사 실 필터는 그 동안 많은 개발자들이 필요로 하는 기능이었으며, 다른 페이지로 이동하거나(forwarding) 다른 페이지를 포함하 는(include) 방법을 사용하여 서블릿 체인(또는 서블릿과 JSP의 체인) 형태로 필터링을 구현하는 경우가 많았다. 이러한 필 터링을 적용할 수 있는 분야에는 다음과 같은 것들이 있을 것이다.

    데이터 변환(다운로드 파일의 압축 및 데이터 암호화 등) 
    XSL/T를 이용한 XML 문서 변경 
    사용자 인증 
    자원 접근에 대한 로깅 
    이 외 에도 많은 활용 방법들이 존재할 수 있겠지만 여기서 제시한 네 가지 정도가 가장 많이 사용되지 않을까 생각된다. 특히, 데이터 변 환이나 XSL/T는 필터를 적용하기에 가장 알맞은 구조를 갖고 있다. 데이터 변환의 경우 데이터 압축 필터와 암호화 필터를 체인으 로 만들어 암호화한 데이터를 다시 압축하는 등의 다양한 방식이 존재할 수 있다. 또한, XSL/T를 사용할 경우 최종 자원 은 XML 문서를 생성하고, 그 문서를 다양한 포맷으로 변환해주는 필터가 존재할 수 있다. 예를 들어, 서블릿/JSP는 사용자 가 요청한 정보를 XML로 출력하고 필터는 XSL/T를 사용하여 자원의 출력 결과를 다양한 기기(웹브라우저, 모바일 폰 등)에 맞 게 변형할 수도 있을 것이다.

    이 글에서는 사용자 인증 필터, XSL/T 필터에 대해서 살펴볼 것이다. 이 두가 지 필터는 필터를 통한 흐름 제어 및 응답 데이터의 변경 방법 등을 보여주고 있기 때문에, 이 두 가지 형태의 필터가 어떤 식으 로 구현되는 지 이해한다면 여러분은 그 외의 모든 다른 종류의 필도 어렵지 않게 구현할 수 있게 될 것이다. 지금부터 차례대로 살 펴보도록 하자.



    로그인 검사 필터 


    요즘 많은 웹 사이트들이 회원제로 운영되 고 있으며 로그인을 한 이후에 컨텐츠에 접근할 수 있도록 제한한 곳도 많다. 특히 컨텐츠의 유료화 추세에 발맞추어 이처럼 사용 자 인증이 필요한 사이트는 점차적으로 증가하고 있다. 심지어 무료 사이트 조차도 사용자가 인증을 거친 이후에 컨텐츠에 접근 할 수 있도록 하고 있다. 이처럼 사용자 인증이 웹 사이트의 필수 요소로 등장하면서 개발자들은 각각의 JSP/서블릿 등의 코드 에 사용자가 로그인을 했는지의 여부를 판단하기 위한 코드를 삽입하고 있다. 여기서 각각의 JSP/서블릿은 같은 코드 블럭을 갖 게 되며 이는 회원 인증이 변할 경우 모든 페이지를 변경해주어야 한다는 문제를 일으키게 된다.

    이러한 문제는 로그인 을 검사하는 필터를 사용함으로써 말끔하게 해소할 수 있다. 1부에서도 살펴봤듯이 클라이언트의 요청은 서블릿/JSP에 전달되기 전 에 먼저 필터를 통과하게 된다. 따라서, 필터에서 조건에 따라 알맞게 흐름을 제어할 수 있다는 것을 의미한다. 먼저 소스 코드부 터 작성해보자. 어떤 형태로 구현했는지 집중적으로 관찰하기 바란다.


      import javax.servlet.*;
      import javax.servlet.http.*;
      
      public class LoginCheckFilter implements Filter {
         
         public void init(FilterConfig filterConfig) throws ServletException {
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response,
                              FilterChain chain)
                     throws java.io.IOException, ServletException {
            if (LoginChecker.isLogin((HttpServletRequest)request)) {
               // 로그인을 했다면 다음 필터를 실행한다.
               chain.doFilter(request, response);
            } else {
               // 로그인을 하지 않았을 경우 로그인 페이지로 이동한다.
               ((HttpServletResponse)response).sendRedirect(LOGIN_URL);
            }
         }
         
         public void destroy() {
         }
         
         private static String LOGIN_URL = "/login.jsp";
      }
      

    로 그인 여부를 검사하는 필터는 위와 같이 매우 간단하다. 여기서 LoginChecker.isLogin() 메소드는 파라미터로 전달받 은 HttpServletRequest를 사용하여 로그인 여부를 판단해주는 일종의 보조 클래스이다. 회원이 로그인을 했 을 때 LoginChecker.isLogin() 메소드가 true를 리턴한다고 가정할 경우, 위 코드와 같이 로그인을 하면 필 터 체인의 다음 필터로 이동하고 로그인을 하지 않은 상태로 판단되면 response를 사용하여 다른 페이지로 이동하면 된다. 로그 인을 하지 않았을 때 이동하는 페이지는 일반적으로 로그인 폼을 보여주는 페이지가 될 것이다.

    위 코드를 보면 알겠지 만 클라이언트의 요청이 반드시 필터 체인의 모든 필터를 통과해야 하는 것은 아니며, 어떤 필터든지 간에 다음 필터로 이동하지 않 고 중간에 체인을 끝낼 수 있도록 되어 있다. 사용자 인증 필터는 바로 그러한 특징을 이용한 것이다. 사용자 인증 필터는 거의 대 부분의 회원 서비스에서 사용될 것이며 다음과 같이 web.xml을 설정하여 사용자 인증 필터를 적용하면 될 것이다.


       <filter>
          <filter-name>LoginChecker</filter-name>
          <filter-class>LoginCheckFilter</filter-class>
       </filter>
       
       <filter-mapping>
          <filter-name>LoginChecker</filter-name>
          <url-pattern>/board/*</url-pattern>
       </filter-mapping>


    인 증 필터를 사용함으로써 얻게 되는 장점은 서블릿/JSP와 같은 최종 자원에서 일일이 로그인 여부를 판단하지 않아도 된다는 점이 다. 이는 서블릿과 JSP는 클라이언트의 요청에 알맞은 작업만을 수행하는 역할을 맡게 되고 사용자 인증을 검사하는 역할은 맡지 않 아도 된다는 것을 의미한다. 즉, 역할에 알맞게 객체가 분리되는 것이다.



    XSL/T 필터 


    필 터가 나옴으로써 객체 지향적으로 변화된 부분이 있다면 바로 XML과 관련된 부분일 것이다. 필터가 생김으로써 서블릿과 JSP 는 더 이상 XSL/T를 이용하여 XML 문서를 HTML이나 WML과 같은 다른 통신 표준으로 변경해줄 필요가 없게 되었다. 이 제, XSL/T를 이용한 변환 작업은 이제 필터가 맡게 되었으며 서블릿과 JSP는 XML 문서를 생성하는 것 이외에 다른 작업 은 할 필요가 없게 되었다.

    XSL/T 필터는 응답 데이터를 변경해주는 필터이다. 즉, 서블릿/JSP가 생성 한 XML 문서를 XSL/T를 이용하여 완전히 새로운 형태로 재구성하는 것이 XSL/T 필터의 역할이다. 서블릿/JSP의 응답 결 과인 XML 문서를 완전히 새로운 형태로 변경해주기 위해서는 서블릿/JSP가 출력한 XML 데이터를 클라이언트(웹브라우저)에 곧바 로 전송해서는 안된다. 대신, 서블릿/JSP가 출력한 데이터를 임의의 버퍼에 저장한 후, 그 버퍼에 저장된 XML 데이터를 XSL /T를 사용하여 변환해야만 한다. 이를 위해 먼저 버퍼의 역할을 할 출력 스트림을 작성해야 하며, 또한 그 출력 스트림은 서블릿 과 JSP에서 주로 사용되는 PrintWriter 타입이어야만 한다. 다음은 이 예제에서 서블릿/JSP가 출력하는 데이터를 저장해 둘 버퍼의 역할을 하는 ReponseBufferWriter 클래스이다.


      class ReponseBufferWriter extends PrintWriter {
         
         public ReponseBufferWriter() {
            super(new StringWriter(4096) );
         }
         
         public String toString() {
            return ((StringWriter)super.out).toString();
         }
      }


    특 별히 어렵지는 않으며, ResponseBufferWriter는 print(), println(), write() 등의 메소드를 통 해서 전달된 데이터를 StringWriter에 저장하는 기능을 한다. toString() 메소드는 StringWriter에 저장 된 데이터를 String 타입으로 변환해주는 역할을 한다.

    출력 버퍼를 만들었으니 그 다음으로 해야 할 일은 최 종 자원인 서블릿과 JSP가 ResponseBufferWirter를 출력 스트림으로 사용하도록 하는 응답 래퍼 클래스를 작성하 는 것이다. 이 예제에서 사용할 응답 래퍼 클래스는 다음과 같다.


      class XSLTResponseWrapper extends HttpServletResponseWrapper {
         
         private ReponseBufferWriter buffer = null;
         
         public XSLTResponseWrapper(HttpServletResponse response) {
            super(response);
            buffer = new ReponseBufferWriter();
         }
         
         public PrintWriter getWriter() throws java.io.IOException {
            return buffer;
         }
         
         public void setContentType(String contentType) {
            // do nothing
         }
         
         public String getBufferedString() {
            return buffer.toString();
         }
      }


    위 코 드를 보면 XSLTResponseWrapper 클래스가 복잡하지 않다는 것을 알 수 있다. XSLTResponseWrapper 클 래스의 getWriter() 메소드는 실제 클라이언트로의 응답에 해당하는 스크림을 리턴하는 대신 앞에서 작성 한 ResponseBufferWriter를 리턴한다. 이렇게 함으로써 ServletResponse의 getWriter() 메소드 를 호출하는 서블릿/JSP는 클라이언트로의 응답 스트림이 아닌 ResponseBufferWriter를 출력 스트림으로 사용하게 된 다. 또 하나 주의해야 할 부분이 바로 setContentType() 메소드가 아무 기능도 하지 않는다는 점인데, 이 이유에 대해 서는 뒤에서 설명하도록 하겠다.

    이제 XML 데이터를 임시로 저장할 Writer도 구현하였고 또한 응답 래퍼도 구현하였다. 이제 마지막으로 남은 것은 필터를 구현하는 것 뿐이다. 필터는 다음과 같은 4 단계로 작업을 처리한다.

    응답 래퍼(XSLTResponseWrapper)를 생성한다. 
    생성한 응답 패퍼를 체인의 다음 필터에 전달한다. 
    래퍼로부터 서블릿/JSP가 출력한 데이터를 읽어와 XSL/T를 사용하여 HTML로 변환한다. 
    변환된 결과인 HTML을 실제 응답 스트림에 출력한다. 
    이 과정을 구현한 것이 바로 XSLTFilter 클래스이다. 


      import javax.servlet.*;
      import javax.servlet.http.*;
      import java.io.*;
      
      import javax.xml.transform.TransformerFactory;
      import javax.xml.transform.Transformer;
      import javax.xml.transform.stream.StreamSource;
      import javax.xml.transform.stream.StreamResult;
      
      public class XSLTFilter implements Filter {
         
         public void init(FilterConfig filterConfig) throws ServletException {
         }
         
         public void doFilter(ServletRequest request,
                              ServletResponse response,
                              FilterChain chain)
                     throws java.io.IOException, ServletException {
            response.setContentType("text/html; charset=euc-kr");
            PrintWriter writer = response.getWriter();
            
            XSLTResponseWrapper responseWrapper = 
                          new XSLTResponseWrapper((HttpServletResponse)response);
            chain.doFilter(request, responseWrapper);
            
            // XSL/T 변환
            try {
               TransformerFactory factory = TransformerFactory.newInstance();
               Reader xslReader = new BufferedReader(new FileReader("c:/book.xsl"));
               StreamSource xslSource = new StreamSource(xslReader);
               
               Transformer transformer = factory.newTransformer(xslSource);
               
               String xmlDocument = responseWrapper.getBufferedString();
               Reader xmlReader = new StringReader(xmlDocument);
               StreamSource xmlSource = new StreamSource(xmlReader);
               
               StringWriter buffer = new StringWriter(4096);
               
               transformer.transform( xmlSource, new StreamResult(buffer) );
               
               writer.print(buffer.toString());
            } catch(Exception ex) {
               throw new ServletException(ex);
            }
            
            writer.flush();
            writer.close();
         }
         
         public void destroy() {
         }
      }


    XSLTFilter 클 래스의 doFilter() 메소드를 차근 차근 분석해보도록 하자. doFilter() 메소드가 가장 먼저 하는 것은 응답의 컨텐 츠 타입을 text/html로 지정하는 것이다. 물론, 한글을 사용하기 때문에 뒤에 charset도 추가해주었다. 여기 서 response 객체는 클라이언트에 대한 응답을 나타내며, 클라이언트는 결과 데이터를 HTML 문서로 인식하게 된다. 앞에 서 XSLTResponseWrapper의 setContentType() 메소드에서 아무것도 하지 않았었는데, 그 이유 는 XSLTFilter의 doFilter() 메소드에서 지정한 컨텐츠 타입을 변경할 수 없도록 하기 위함이다.

    그 다 음에는 래퍼 클래스를 생성한다. 래퍼 클래스는 XSLTResponseWrapper이며, 생성된 래퍼 클래스 는 chain.doFilter()를 통해서 다음 필터에 전달된다. 필터 체인의 실행이 완료되 면 XSLTResponseWrapper 객체에는 서블릿이나 JSP가 출력한 XML 응답 데이터가 저장되며, 그 데이터 는 responseWrapper.getBufferedString() 메소드를 통해서 구할 수 있게 된다. 이렇게 해서 구 한 XML 데이터는 JAXP 1.1에서 제공하는 Transformer의 transform() 메소드를 통해서(즉, XSL/T를 통 해서) HTML 형식으로 변환된다.

    이제 XSL/T 필터와 관련된 모든 클래스의 구현은 끝이 났다. 이제 남은 것 은 XSL/T에서 사용할 XSL 문서를 작성하고 XML 문서를 생성해주는 JSP/서블릿을 프로그래밍하고 그리고 web.xml 파일 을 통해서 XSLTFilter를 필터로 등록하는 것이다. 먼저 web.xml 파일을 필터를 사용하여 지정해보자.


       <filter>
          <filter-name>XSLT</filter-name>
          <filter-class>XSLTFilter</filter-class>
       </filter>
       
       <filter-mapping>
          <filter-name>XSLT</filter-name>
          <url-pattern>/xml/*</url-pattern>
       </filter-mapping>


    이 제 /xml/로 들어오는 모든 요청은 XSLTFilter를 토?서 처리된다. 이제 XML 문서를 생성해주는 JSP 페이지를 작성해 보자. 여기서는 테스트를 위해서 다음과 같이 간단한 JSP 페이지를 사용하였다. (이 JSP를 book.jsp라 하자.)


      <?xml version="1.0" encoding="euc-kr" ?>
      
      <%@ page contentType="text/xml; charset=euc-kr" %>
      
      <list>
         
         <book>
            <title>JavaCan.com의 JSP Professional</title>
            <author>이동훈, 최범균</author>
            <price>24,000</price>
         </book>
         
         <book>
            <title>JavaCan.com의 Victory Java</title>
            <author>이동훈, 최범균</author>
            <price>30,000</price>
         </book>
         
      </list>


    위 JSP 페이지는 보다시피 XML 문서를 생성해낸다. 이 XML 문서를 HTML로 변환해주기 위해 사용되는 XSL은 다음과 같다. (여기서는 XSL에 대한 내용은 설명하지 않겠다.)


     <?xml version="1.0" encoding="euc-kr" ?>
      
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:output method = "html" indent="yes" encoding="euc-kr" />
      
        <xsl:template match="list">
        <html>
        <head><title>책 목록</title></head>
        <body>
          현재 등록되어 있는 책의 목록은 다음과 같습니다.
          <ul>
          <xsl:for-each select="book">
            <li><b><xsl:value-of select="title" /></b>
                (<xsl:value-of select="price" /> 원)
                <br />
                <i><xsl:value-of select="author" /></i>
            </li>
          </xsl:for-each>
          </ul>
        </body>
        </html>
        </xsl:template>
        
      </xsl:stylesheet>


    book.jsp 를 웹어플리케이션의 /xml 하위디렉토리에 복사한 후 웹브라우저에서 book.jsp를 요청한 결과의 소스 코드를 보면 다음과 같 이 XML이 아닌 XSLTFilter 필터를 통해서 변경된 결과가 오는 것을 확인할 수 있을 것이다.


      <html>
      <head>
      <META http-equiv="Content-Type" content="text/html; charset=euc-kr">
      <title>책 목록</title>
      </head>
      <body>
           현재 등록되어 있는 책의 목록은 다음과 같습니다.
           <ul>
      <li>
      <b>JavaCan.com의 JSP Professional</b>
                 (24,000 원)
                 <br>
      <i>이동훈, 최범균</i>
      </li>
      <li>
      <b>JavaCan.com의 Victory Java</b>
                 (30,000 원)
                 <br>
      <i>이동훈, 최범균</i>
      </li>
      </ul>
      </body>
      </html>




    결론 


    여 기서 살펴본 필터의 예제는 매우 간단하게 구현되는 것들이었지만, 아마 필터를 구현하는 데 있어서 가장 기본적인 형태를 갖는 예제 가 아닐까 생각된다. 이번 필터 예제를 통해서 여러분들은 필터의 쓰임새가 많겠구나 하고 생각했을 것이다. 어쩌면 벌써부터 압축 필 터나 이미지 생성 필터 등을 생각하고 있을지도 모르겠다. 하지만 필터의 응용을 생각하기 이전에 다음과 같은 점을 염두해두었으면 한 다.

    필터는 재사용이 가능해야 한다. 즉, 필터들은 객체 지향적으로 설계되고 구현되어야 한다. 
    필터를 통과하지 않아도 JSP/서블릿/기타 자원은 알맞은 결과를 출력해야 한다. 
    필터간에 커플링(coulpling; 결합도)이 존재해서는 안 된다. 
    이러한 것들을 염두하고서 필터를 설계하고 구현한다면, 그 필터는 여러분이 개발하게 될 웹 어플리케이션 곳곳에서 사용될 것이며 그만큼 여러분의 웹 어플리케이션은 역할별로 알맞게 분리된 구조를 갖게 될 것이다.
  2. 추가 이미지
  3.  















     

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

include  (0) 2012.05.08
IBatis  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
Posted by 사라링

FileUploadRequestWrapper

 | JSP
2012. 5. 8. 18:14
  1. package kr.or.ddit.wrapper;

    import java.awt.ItemSelectable;
    import java.io.File;
    import java.io.UnsupportedEncodingException;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;

    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.FileUploadException;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;

    /**
     * multipart/form-data
     */
    public class FileUploadRequestWrapper extends HttpServletRequestWrapper {

        private boolean multipart = false;
        private Map<String, String[]> parameterMap;
        private Map<String, FileItem[]> fileItemMap;

        public FileUploadRequestWrapper(HttpServletRequest request)
                throws FileUploadException {
            this(request, -1, -1, null);
        }

        public FileUploadRequestWrapper(HttpServletRequest request, int threshold,
                int max, String repositoryPath) throws FileUploadException {
            super(request);
            parsing(request, threshold, max, repositoryPath);
        }

        private void parsing(HttpServletRequest request, int threshold, int max,
                String repositoryPath) throws FileUploadException {
            if (ServletFileUpload.isMultipartContent(request)) {
                multipart = true;
                parameterMap = new HashMap<String, String[]>();
                fileItemMap = new HashMap<String, FileItem[]>();
                DiskFileItemFactory factory = new DiskFileItemFactory();
               
                // 메모리에 저장할 사이즈, 기본사이즈 10kb
                if (threshold != -1) {
                    factory.setSizeThreshold(threshold);
                }
                // 임시 저장 경로, 기본적으로 시스템의 탬프 , 웹서버의 경로
                if (repositoryPath != null) {
                    factory.setRepository(new File(repositoryPath));
                }
               
                ServletFileUpload upload = new ServletFileUpload(factory);
                String encodingName = request.getCharacterEncoding();
                System.out.println(encodingName);
                if (encodingName == null)
                    encodingName = "utf-8";
                upload.setSizeMax(max); // 최대 업로드 사이즈 (total)

                // upload.setFileSizeMax(); // 개별 업로드 사이즈
                List<FileItem> list = upload.parseRequest(request);
                for (int i = 0; i < list.size(); i++) {
                    FileItem fileItem = list.get(i);
                    String name = fileItem.getFieldName();
                    if (fileItem.isFormField()) {
                        // 일반 필드
                        try {
                            String value = fileItem.getString(encodingName);
                            String[] values = parameterMap.get(name);
                            if (values == null) {
                                // 한글이 만약 깨진다면  아래처럼 변경, 예전 FileUpload API,
                                // values = new String[]{new String(value.getBytes("iso-8859-1"),encodingName)};
                                values = new String[] { value };
                            } else {
                                String[] tempValues = new String[values.length + 1];
                                System.arraycopy(values, 0, tempValues, 0, values.length);
                                // 한글이 깨진다면
                                // tempValues[tempValues.length - 1] = new String(value.getBytes("iso-8859-1") ,encodingName);
                                tempValues[tempValues.length - 1] = value;
                                values = tempValues;
                            }
                            parameterMap.put(name, values);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // input type="file" 인 경우  fileItemMap에 저장
                        FileItem[] items = fileItemMap.get(name);
                        if (items == null) {
                            items = new FileItem[] { fileItem };
                        } else {
                            FileItem[] tempItems = new FileItem[items.length + 1];
                            System.arraycopy(items, 0, tempItems, 0, items.length);
                            tempItems[tempItems.length - 1] = fileItem;
                            items = tempItems;
                        }
                        fileItemMap.put(name, items);
                    }
                }
            }
        }

        public boolean isMultipartContent() {
            return multipart;
        }

        public String getParameter(String name) {
            if (multipart) {
                String[] values = (String[]) parameterMap.get(name);
                if (values == null)
                    return null;           
                return values[0];
            } else
                return super.getParameter(name);
        }

        public String[] getParameterValues(String name) {
            if (multipart)
                return (String[]) parameterMap.get(name);
            else
                return super.getParameterValues(name);
        }

        public Enumeration getParameterNames() {
            if (multipart) {
                return new Enumeration() {
                    Iterator iter = parameterMap.keySet().iterator();

                    public boolean hasMoreElements() {
                        return iter.hasNext();
                    }

                    public Object nextElement() {
                        return iter.next();
                    }
                };
            } else {
                return super.getParameterNames();
            }
        }

        public Map getParameterMap() {
            if (multipart)
                return parameterMap;
            else
                return super.getParameterMap();
        }

        public FileItem getFileItem(String name) {
            if (multipart) {
                FileItem[] fileItems = fileItemMap.get(name);
                if (fileItems != null) {
                    return fileItems[0];
                } else {
                    return null;
                }
            } else {
                return null;
            }
        }

        public FileItem[] getFileItemValues(String name) {
            if (multipart)
                return fileItemMap.get(name);
            else
                return null;
        }

        public void delete() {
            if (multipart) {
                Iterator fileItemIter = fileItemMap.values().iterator();
                while (fileItemIter.hasNext()) {
                    FileItem[] fileItems = (FileItem[]) fileItemIter.next();
                    for (FileItem item : fileItems) {
                        item.delete();
                    }
                }
            }
        }

    } // class

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

IBatis  (0) 2012.05.08
Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
Posted by 사라링

FileUpLoad

 | JSP
2012. 5. 8. 18:14

 

 

wrapper구현.png

 

http://www.servlets.com/

cos-26Dec2008.zip

 

 

 

파일업로드는 Post방식으로만 가능하다 Get 방식 불가능

Http 요청 메시지
header : get방식: http://localhost:8080/chap13/index.jsp?파일주소 << 정말 파일의 위치주소만 전송된다.
body

form  enctype="multipart/form-data" 명시를 해야 파일데이터라고 인식을함.
input type="file"

파일을 첨부해서 보낸경우 request.getParameter(무력화당함..) 로 추출을못함. => 통체로 MultipartRequest 에 넘겸줌
 
WEB-INF/lib/cos.jar
cos라이브러리는 동시에 여러 파일을 올리수있다.
maxSize : 모든파일의 합의 제한용량


WEB-INF/fileSelect.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>파일 업로드 예제</title>
</head>
<body>
<!--
method="post" : get방식은 파일업로드 못함
enctype="multipart/form-data" : 파일업로드시 반드시 명시
type="file" : 선택파일을 매치시켜 파일을 업로드
-->
<form name="fimeForm" method="post" enctype="multipart/form-data" action="fileUpload.jsp">
작성자: <input type="text" name="user"><br/>
제목:<input type="text" name="title"><br/>
파일명:<input type="file" name="uploadFile"><br/>
파일명2:<input type="file" name="uploadFile2"><br/>
<input type="submit" value="파일 올리기"><br/>
</form>
</body>
</html>



WEB-INF/fileUpload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>
<!--
MultipartRequest :
DefaultFileRenamePolicy : 기존에 업로드 파일중 이름이 똑같은것이 있는경우  업로드시 이름을 변경해주는것 ex)a.jsp,a1.jsp
 -->
<%@ page import="com.oreilly.servlet.MultipartRequest"%>
<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy"%>
<%@ page import="java.util.*"%>
<%@ page import="java.io.*"%>

<%
    String realFolder = ""; //웹 어플레케이션상의  절대경로

    //파일이 업로드되는 폴더를 지정한다
    String saveFolder = "upload";
    String encType = "utf-8";///엔코딩타입
    int maxSize = 5 * 1024 * 1024;//최대 업로드될 파일크기 5mb

    ServletContext context = getServletContext();
    //현재 jsp 페이지의 웹어플리케이션상의 절대 경로를 구한다.
    realFolder = context.getRealPath(saveFolder);
    out.println("the realpath is: " + realFolder + "<br/>");

    try {
        MultipartRequestmulti= null;

        //전송을 담당할 콘포넌트를 생성하고 파일을 전송한다.
        //전송할 파일명을 가지고 있는 객체, 서버상의 절대경로, 최대 업로드될 파일크기
        /*
        MultipartRequest(
                request, :    요청request를 통채로 넘겨줌
                realFolder, : 웹어플리케이션상의 절대경로
                maxSize, : 최대용량
                encType, : 파일타입
                new DefaultFileRenamePolicy() :같은파일명이있는경우 이름변경
        */
        multi= new MultipartRequest(request, realFolder, maxSize, encType, new DefaultFileRenamePolicy());
       
        //Form의 파라미터 목록을 가져온다.         //getParameterNames: 파일과 관련없는 일반정보인 경우 추출방법        //Enumeration : java.util : 데이터를 여러개 보관할수 있는객체         Enumeration params= multi.getParameterNames();

        //파라미터를 출력한다.
        while (params.hasMoreElements()) {
            //전송되는 파라미터이름
            String name = (String) params.nextElement();
            //전송되는 파라미터값
            String value = multi.getParameter(name);

            out.println(name + "=" + value + "<br>");

        }
        out.println("----------------------------------------------<br/>");

        //전송할 파일 정보를 가져와 출력한다.
        //getFileNames : 파일과 관련있는 정보인 경우의 추출방법
        Enumeration files = multi.getFileNames();

        //파일 정보가 있다면
        while (files.hasMoreElements()) {

            //input태그 속성이 file 인 태그의 name속성값 : 파라미터 이름
            String name = (String) files.nextElement();

            //서버에 저장된 파일 이름
            String filename = multi.getFilesystemName(name);

            //전송전 원래의 파일이름
            String original = multi.getOriginalFileName(name);

            //전송된 파일의 내용타입
            String type = multi.getContentType(name);

            //전송된 파일 속성이  file인 태그의 name 속성값을 이용해 파일 객체 생성
            //용량을 알아내기 위해서 : file.length();
            File file = multi.getFile(name);

            out.println("파라미터 이름 : " + name + "<br/>");
            out.println("실제 파일 이름 : " + original + "<br/>");
            out.println("저장된 파일 이름 : " + filename + "<br/>");
            out.println("파일 타입 : " + type + "<br/>");

            if (file != null) {
                out.println("크기 : " + file.length());
                out.println("<br/>");
            }
        }
    } catch (IOException ioe) {
        System.out.println(ioe);
    } catch (Exception ex) {
        System.out.println(ex);
    }
%>

 

 

다중업로드

 

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

Filter(유저를 this 시켜 보자 )  (0) 2012.05.08
FileUploadRequestWrapper  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
Posted by 사라링

엑셀파일다운로드

 | JSP
2012. 5. 8. 18:14

출처 : http://blog.naver.com/tyboss/70038694288


1. 첫번째 방법

<%
response.setHeader("Content-Disposition", "attachment; filename=diary.xls");
response.setHeader("Content-Description", "JSP Generated Data");
response.setContentType("application/vnd.ms-excel");
%>

 
 

2. 두번째 방법
<%@ page contentType="application/vnd.ms-excel;charset=euc-kr" %>
<%
response.setHeader("Content-Disposition", "attachment; filename=LIST.xls");
response.setHeader("Content-Description", "JSP Generated Data");
%>

 
 

3. 세번째 방법 (한파일에 기본 페이지와 엑셀 다운로드 설정할때)

<%

if(actionKind.equals("Search")){
response.setContentType("text/html; charset=euc-kr");
}else{
response.setHeader("Content-Disposition", "attachment; filename=diary.xls");
response.setHeader("Content-Description", "JSP Generated Data");
response.setContentType("application/vnd.ms-excel");
}
%>

 

4. 네번째 방법 (한글 제목에 브라우저별 처리)

titleName = "한글제목";

titleName = new String(titleName.getBytes("KSC5601"), "8859_1");

if(clientBrowser.indexOf("MSIE 5.5")>-1 || clientBrowser.indexOf("MSIE 6.0") > -1 ){
response.setHeader("Content-Type", "doesn/matter;");
response.setHeader("Content-Disposition", "filename="+titleName+".xls");
}else{
response.setHeader("Content-Type", "application/vnd.ms-excel;charset=EUC-KR");
response.setHeader("Content-Disposition", "attachment; filename="+titleName+".xls");
}

response.setHeader("Content-Transfer-Encoding", "binary;");
response.setHeader("Pragma", "no-cache;");
response.setHeader("Expires", "-1;");

 

 

=========================================================================================================================

=========================================================================================================================

한글깨짐

 

head 에 다음과 같은 처리를 같이 해주면 왠만하면 해결이 된다.

 

<META HTTP-EQUIVE="CONTENT-TYPE" CONTENT="TEXT/HTML; CHARSET=KSC5601">

or
response.reset(); // response 버퍼를 비우고 respose 값을 세로 세팅

=========================================================================================================================

=========================================================================================================================

 

운영서버에서는 한글이 다 깨지는데 개발서버에서는 제대로 나와서 더 해결방법 찾기가 난감했다.

jsp 소스를 운영 거로 업어치고 나서도 여전히 잘만 보이는 개발서버 쪽 엑셀.. 뭐가 문제냐. ㄱ-

DB 상의 데이터까지 운영 쪽으로 맞추니 그제서야 한글이 깨지기 시작했다.

그럼 문제는 데이터라는 소리인데.. 어떻게 봐도 한글이 깨질 건수가 안 보였다.

 

웹 상에 떠도는 한글 깨짐 방지 태그라는

<meta http-equiv="Content-Type" content="text/html; charset=euc-kr">

이 녀석을 써 봐도 여전히 묵묵부답..

 

하지만 해결책은 간단했다.

엑셀 녀석이 데이터를 인코딩 태그로 인식하는 경우가 간혹 생기는데

데이터 타입 앞에 &nbsp;만 입력해주면 끝. 아주 깨끗하게 잘 나온다.

 

<td>&nbsp;<%= crset.getString(1) %></td> ← 이런 식으로

 

 

=========================================================================================================================

=========================================================================================================================

 

 

 

숫자형식 엑셀에서 표현하기

== 사용법 ==

<style type="text/css">

td {mso-number-format:000000;}

</style>

또는

<td align='center' style='mso-number-format:000000'>

 

000000 : 소수도 여섯자리 정수 (반올림)로 표현된다. 여섯자리 앞의 빈칸은 0으로 채워짐

1.23 => 000001, 67.67 => 000068

000.000 : 소수자리 세자리까지 (반올림) 표현된다. 앞 뒤 빈칸은 0으로 채워짐

format은 0.00 인데 숫자가 15.1 인 경우 15.10으로 표현됨

1.5678 => 001.568

\@ : 셀형식을 텍스트형으로 표현

00035.90 인 경우 셀 형식이 숫자형이라면 35.9로 표현되지만 문자형으로 하면 0을 포함하여 보이는 그대로 표현됨

 

 

 

 

그 외 mso-number-format 요소들

mso-number-format:"0"
NO Decimals
mso-number-format:"0\.000"
3 Decimals
mso-number-format:"\#\,\#\#0\.000"
Comma with 3 dec
mso-number-format:"mm\/dd\/yy"
Date7
mso-number-format:"mmmm\ d\,\ yyyy"
Date9
mso-number-format:"m\/d\/yy\ h\:mm\ AM\/PM"
D -T AMPM
mso-number-format:"Short Date"
01/03/1998
mso-number-format:"Medium Date"
01-mar-98
mso-number-format:"d\-mmm\-yyyy"
01-mar-1998
mso-number-format:"Short Time"
5:16
mso-number-format:"Medium Time"
5:16 am
mso-number-format:"Long Time"
5:16:21:00
mso-number-format:"Percent"
Percent - two decimals
mso-number-format:"0%"
Percent - no decimals
mso-number-format:"0\.E+00"
Scientific Notation
mso-number-format:"\@"
Text
mso-number-format:"\#\ ???\/???"
Fractions - up to 3 digits (312/943)
mso-number-format:"\0022£\0022\#\,\#\#0\.00"
£12.76
mso-number-format:"\#\,\#\#0\.00_ \;\[Red\]\-\#\,\#\#0\.00\"
2 decimals, negative numbers in red and signed(1.56 -1.56)

 

 

한 셀 안에서 줄바꿈

<style>

.xl24 {mso-number-format:"\@";}

br {mso-data-placement:same-cell;}

</style>




'JSP' 카테고리의 다른 글

FileUploadRequestWrapper  (0) 2012.05.08
FileUpLoad  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
Posted by 사라링

fileDownload.jsp

 | JSP
2012. 5. 8. 18:14

경로  root/common/download.jsp 

받는 파라미터 ./ pds 의 pds_no //파라미터 네임 : file_no

  1. <%@page import="kr.or.ddit.pds.vo.PdsBean"%>
    <%@page import="kr.or.ddit.pds.dao.PdsDaoMyBatis"%>
    <%@page import="kr.or.ddit.pds.dao.IPdsDao"%>
    <%@ page language="java" pageEncoding="utf-8"  %>
    <%@ page trimDirectiveWhitespaces="true" %>
    <%@page import="java.io.FileInputStream"%>
    <%@page import="java.io.BufferedOutputStream"%>
    <%@page import="java.io.BufferedInputStream"%>
    <%@page import="java.io.File"%>
    <%
        String file_no = request.getParameter("file_no");
        // bo_id 검증 필요!!   
        if(file_no == null || file_no.compareTo("") == 0 ){
            // throw new Exception("다운로드 에러~~~~");
            response.setHeader("Content-type", "text/html");
    %>   
    <%@page import="java.sql.SQLException"%>

    <script type="text/javascript">
            <!--
                alert("너 누구야... ");
                history.back();
            //-->
            </script>
            <%
                return;
            } // if(!StringUtils.isNumeric(bo_no))

                IPdsDao pdsDao = new PdsDaoMyBatis();
                PdsBean bean = pdsDao.findByPk(Integer.parseInt(file_no));
               
                if(bean.getPds_realpath() == null || "".equals(bean.getPds_realpath())){
               
                    // throw new Exception("파일이 없습니다.~~~~");
                    response.setHeader("Content-type", "text/html");
                    // response.setStatus(404); // 404 = HttpServletResponse.SC_NOT_FOUND;
                    %>
                    <script type="text/javascript">
                    <!--
                        alert("파일이 존재하지 않는 글입니다.");
                        history.back();
                    //-->
                    </script>
                    <%
                        return;
                }
               
                //dao.downCount(Integer.parseInt(bo_no)); // 숙제 구현        
                // 경로, 실제 저장된 파일을 읽는다
                //String upPath = application.getRealPath("/upload");
                String upPath = "c:\\upload";
               
                String fileName = bean.getPds_userfile(); // 사용자가 올렸던 이름
                File file = new File(bean.getPds_realpath());

                byte b[] = new byte[2048];           

                if (file.length() > 0 && file.isFile()) {
                    String strClient = request.getHeader("User-Agent");
                    if (strClient.indexOf("MSIE 5.5") > -1) {
                        response.setHeader("Content-Disposition", "filename="
                                + (new String(fileName.getBytes("euc-kr"), "iso-8859-1")) + ";");
                    } else {
                        response.setHeader("Content-Disposition", "attachment;filename="
                                + (new String(fileName.getBytes("euc-kr"), "iso-8859-1")) + ";");
                    }
                   
                    response.setHeader("Content-type", "application/octet-stream");
                    response.setHeader("Content-Length", "" + file.length());
                    response.setHeader("Content-Description", "Inna Site");
                    BufferedInputStream fin = new BufferedInputStream(new FileInputStream(file));
                    BufferedOutputStream outs = new BufferedOutputStream(response.getOutputStream());
                    int read = 0;
                    try {
                        while ((read = fin.read(b)) != -1) {
                            outs.write(b, 0, read);
                        }
                        // 성공적으로 내보냈으니까. 다운횟수 증가
                        //pdsDao.incrementDownHit(bean.getPds_no());
                        //pdsDao.incrementDownHit(bean.getPds_downhit());
                        pdsDao.incrementDownHit(bean.getPds_no());
                        //pdsDao.incrementDownHit(bean.getPds_no());
                       
                        outs.close();
                        fin.close();                   
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    } finally {
                        if (outs != null) outs.close();
                        if (fin != null) fin.close();
                    }
                } else {
                    // response.setStatus(404); // 404 = HttpServletResponse.SC_NOT_FOUND;
                    response.setHeader("Content-type", "text/html");
                    // response.setStatus(404); // 404 = HttpServletResponse.SC_NOT_FOUND;
                    %>
                    <script type="text/javascript">
                    <!--
                        alert("[<%=bean.getPds_userfile()%>]파일이 서버에 존재하지 않습니다.");
                        history.back();
                    //-->
                    </script>
                <%
                    return;
                }
    %>

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

FileUpLoad  (0) 2012.05.08
엑셀파일다운로드  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
Posted by 사라링

FileDownLoad

 | JSP
2012. 5. 8. 18:13

[ 다운로드 ]


많은 분들이 download.jsp등이나 서블릿을 이용해서 download를 구현을 합니다.
그 이유는 download를 통해서 down을 받게함으로서 직접적인 파일의 경로를
감추어 서버의 파일구조를 감추려는 보안적인 이유도 있을 것이고
또 download.jsp안에서 session에 값을 체크하여 인증절차를 거쳐야
자료를 받을 수 있게 하기 위한 것일 수도 있고

request.getHeader("referer")를 가지고 이전 페이지 url을알아내거나
HTTP Headers Information중 Host를 체크하여 무단적인 링크를 방지하기
위한 이유등 많은 이유로 처리를 하고 있을 것입니다.

저는 download를 구현함에 있어 같은 질문이 자주 올라오는 것 같아서
그런 내용들에 대해서 간단한 답변들을 적어보도록 하겠습니다.

1. 다운로드시 익스플로러 5.5 버전에서만 제대로 안되요.

이것 버전에 따라서 Header를 다르게 설정해줘야 합니다.

================================================================
중략...

  1. if( strClient.indexOf("MSIE 5.5") != -1 ) {
        response.setHeader("Content-Type", "doesn/matter;");
        response.setHeader("Content-Disposition", "filename=" + strFileName + ";");
    } else {
        response.setHeader("Content-Type", "application/octet-stream;");
        response.setHeader("Content-Disposition", "attachment;filename="+strFileName + ";");
    };
    response.setHeader("Content-Transfer-Encoding", "binary;");
    response.setHeader("Content-Length", ""+file.length());
    response.setHeader("Pragma", "no-cache;");
    response.setHeader("Expires", "-1;");

중략..
================================================================
위의 소스와 같이 HTTP Response Headers의 User-Agent를 체크해서

버전이 5.5일경우와 그외 버전일 경우 header를 다르게 설정해줘야

합니다.


2. 파일 다운로드우 이상한 문자가 앞에 추가가 됩니다.(서버에 파일은 문제없음)

이것은 jsp일때 앞부분에 page지시자를 기술할때 다른 내용이 들어가서 그문자가

파일에 추가되어 나타나는 현상입니다. 해결방법은
================================================================
<%@ page language="java" import="java.util.*,java.io.*,java.sql.*,java.text.*"%>
<%
   String file_path = "D:/resin/webapps"+request.getParameter("file_path");
중략..
================================================================
과 같이 Enter를 입력해서 벌어진 부분을
================================================================
<%@ page language="java" import="java.util.*"%><%
   String file_path = "D:/resin/webapps"+request.getParameter("file_path");
중략..
================================================================
과 같이 붙여서 만들어주면 해결할 수 있습니다.


3. 엑셀, 워드, 파워포인트 파일의 다운로드

ppt나 xls확장자같은경우 전 아래와같이 해서 다운로드를 받고 있습니다.
어디서 소스를 봤는지 기억은 잘 안나지만..아래는 jsp화일의 일부분입니다.

String type  = request.getParameter("do");
String fileurl  = request.getParameter("fileurl");
String filepath = request.getParameter("filepath");
String filename = fileurl.substring(filepath.length()+1,fileurl.length());
PrintStream printstream = new PrintStream(response.getOutputStream(), true);
try {
         File file = new File(filepath,filename);
         FileInputStream fin = new FileInputStream(file);
         
         int ifilesize = (int)file.length();
         byte b[] = new byte[ifilesize];
         
         response.setContentLength(ifilesize);
         response.setContentType("application/smnet");
         response.setHeader("Content-Disposition","attachment; filename="+filename+";");
         
         ServletOutputStream oout = response.getOutputStream();
         
         fin.read(b);
         oout.write(b,0,ifilesize);
         oout.close();
         fin.close(); 
} catch(Exception e) {  }


4. 초간단 파일다운로드 소스

간단한 파일 다운로드 프로그램임다..
링크는 download.jsp?db=db이름&table=table이름&num=번호 와 같이 하면 됩니다.

-- download.jsp --

<%@ page contentType="text/html; charset=EUC_KR" %>
<%@ page import="java.io.*, IBboard.beans.file.FileDownload" %>
<%
//응답 헤더의 Content-Type을 세팅한다.
response.setContentType("application/x-msdownload");

FileDownload filedown = new FileDownload();

//DB에서 해당 번호에 해당하는 파일의 이름을 얻어온다.
String filename = filedown.NumToFilename(request.getParameter("db"),request.getParameter("table"),Integer.parseInt(request.getParameter("num")));
//Content-Disposition 헤더에 세팅하기위해 file 이름을 코드 변환한다.
//한글파일 Download 시에 에러가 나는게 이 코드가 빠져 있어서 그런거 같은디..^^
String filename2 = new String(filename.getBytes("euc-kr"),"8859_1");
//Content-Disposition 헤더에 파일 이름 세팅.
response.setHeader("Content-Disposition","attachment; filename="+filename2);
//해당 경로의 파일 객체를 만든다.
File file = new File ("/usr/local/apache/htdocs/upload/"+filename);
//파일 스트림을 저장하기 위한 바이트 배열 생성.
byte[] bytestream = new byte[(int)file.length()];
//파일 객체를 스트림으로 불러온다.
FileInputStream filestream = new FileInputStream(file);
//파일 스트림을 바이트 배열에 넣는다.
int i = 0, j = 0;

while((i = filestream.read()) != -1) {

bytestream[j] = (byte)i;

j++;

}

// 응답 스트림 객체를 생성한다.

OutputStream outStream = response.getOutputStream();

// 응답 스트림에 파일 바이트 배열을 쓴다.

outStream.write(bytestream);

outStream.close();

%>

 


<%-- Download.jsp --%>

<%@page contentType="text/html; charset=euc-kr" %>

<%@page import="java.io.*,java.net.*,javax.servlet.*,javax.servlet.http.*"%>
<%
 String realPath = "실제화일명.txt";
 String saveFileName = "저장시사용할화일.txt";

 

/* UTF8로 인코딩된 한글 이름의 파일을 다운로드 할때 경우에 해당한다.

 * 브라우저 옵션 고급에서 "URL을 항상 UTF-8로 보냄"로 체크되어 있을 경우는

 * 화일이름을 UTF8로 다시 인코딩 해 주어야 저장화일 이름이 깨지는 것을 막을 수 있다.

*/

 

saveFileName= URLEncoder.encode(saveFileName,"UTF8");

 

/** 다운로드 구현시 주의 사항

 * 보통 어플리케이션 서버를 root계정으로 돌리는 경우가 많다.

 * 이경우 잘못 하면 해킹의 기회를 제공 하는 경우가 있다.

 * 어떤 사이트를 보면 실제화일명을 파라미터로 받아서 처리 하는 경우 보안상 위험 할 수 있다.

 * 예를 들어 dowload.jsp?file=download/attach/../../../../../../etc/passwd

 * 위와같은 경우로 해커가 파라미터를 조작하면 계정 및 패스워드 화일외에 시스템 정보를

 * 얼마든지 획득 할 수 있다. 편의를 제공하기 우해 만든 다운로드 프로그램이.

 * 판단미스로 본 사이트를 해킹하는 툴로 사용 될 수 있다는 것이다.

 *  여기에서 아래와 같이 문자를 찾아서 해킹시도를 막는다.

  1.  if(realPath.indexOf("..") != -1 || realPath.indexOf("/") != -1) {
      return;
     }
  1.   PrintStream printstream = new PrintStream(response.getOutputStream(), true);
  1.   FileInputStream fin = null;
      ServletOutputStream outs = null;
  1.  try{
  1.  //이부분에서 화일객체 생성시 해킹에 사용 될 수 있다.
  1. // 가급적이면 화일명을 파라미터가 아니 DB에 가져오도록 로직을 구현하는게 좋다.
  1. //웹상에 보여주는 화일명과 실제화일명을 DB에 넣어 숨기는 것이 좋다.
  1. //여기서는 단순 다운로드 구현을 설명 하고자 걍 사용 하기로 한다.
  1.   File file = new File("/home/test/download/" + realpath);
  1.   fin = new FileInputStream(file);
     
      int ifilesize = (int)file.length();
      byte b[] = new byte[ifilesize];
  1.   String strClient = request.getHeader("User-Agent");
  1.   response.setContentLength(ifilesize);
      response.reset() ;
      response.setContentType("application/octet-stream");
     
      if(strClient.indexOf("MSIE 5.5") > -1) {
           response.setHeader("Content-Disposition", "filename=" + new String(saveFileName.getBytes(),"ISO-8859-1") + ";");
      } else {
           response.setHeader("Content-Disposition", "attachment;
  1.               filename=" +  new String(saveFileName.getBytes("EUC-KR"),"ISO-8859-1") + ";");
      }
      response.setHeader("Content-Length", ""+ifilesize );
      outs = response.getOutputStream();
      response.setHeader("Content-Transfer-Encoding", "binary;");
      response.setHeader("Pragma", "no-cache;");
      response.setHeader("Expires", "-1;");
  1.   if (ifilesize > 0 && files.isFile()) {
         int read = 0;
  1.      while((read = fin.read(b)) != -1) {
             outs.write(b,0,read);
         }
      }
     } catch (FileNotFoundException fnfe) {
         System.out.println(fnfe.toString());
     } catch (Exception e) {
         System.out.println(e.toString());
     } finally{
        try {
            if(outs != null) outs.close();
            if(fin != null) fin.close();
        } catch (Exception e) {}
     }
  1. %>

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

엑셀파일다운로드  (0) 2012.05.08
fileDownload.jsp  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
ss  (0) 2012.05.08
Posted by 사라링

cubrid-dbcp설정.

 | JSP
2012. 5. 8. 16:35

 

DBCP 설정 방법

소개:아파치 DBCP(DataBase Connection Pool)을 통한 Statement 풀링(pooling) 사용 방법을 설명하고자 한다.

DBCP에서 Statement 풀링이 커넥션 별로 유지되고 있기 때문에, 응용서버(cas)에서는 질의를 컴파일 한 정보를 계속 유지하게 된다. 이 후 같은 질의에 대해서는 prepare 과정 없이 execute 단계만 반복함으로써 성능 향상을 가져올 수 있다.

이 문서에서 DBCP 설정 방법을 tomcat 사용 시와 일반 자바클래스 사용 시 두 가지 방법을 설명한다.

 

2008-10-15 기술컨설팅팀 손승일,CUBRID

적용 대상: CUBRID2008, JDK1.6.0_04 이상

DBCP Statement 풀링

DBCP Statement 풀링 기능을 사용하기 위해 브로커 파라미터에 STATEMENT_POOLING ON/OFF가 있다.

Statement 풀링이 사용되기 위해서는 DBCP 설정에서 poolPreparedStatementstrue로 설정되어야 하고, 브로커 설정에서 STATEMENT_POOLINGON으로 설정되어야 한다.

Statement 풀링을 사용할 경우는 DBCP와 브로커 둘 다 풀링을 사용하도록 설정되어 있어야 하고, 만약 어느 한 쪽만 설정한 경우는 오히려 성능이 떨어질 수 있다.

브로커 설정

CUBRID가 설치되어 있는 경로의 ~/CUBRID/conf/cubrid_broker.conf를 열어 연결에 사용될 브로커 하단에 STATEMENT_POOLING ON을 추가하고 브로커를 재 시작 하면 설정이 적용된다.

[%BROKER1]

SERVICE=ON

BROKER_PORT=33000

SESSION_TIMEOUT=300

KEEP_CONNECTION= AUTO

STATEMENT_POOLING=ON

DBCP 다운로드

DBCP를 사용하기 위해서는

commons-dbcp-1.2.2.jarhttp://commons.apache.org/downloads/download_dbcp.cgi

commons-pool-1.3.jar http://commons.apache.org/downloads/download_pool.cgi

commons-collections-3.2.1.jar http://commons.apache.org/downloads/download_collections.cgi

에서 각각의 최신 릴리스 바이너리 파일을 다운 받아 압축을 풀면 위 파일들을 얻을 수 있다.

CUBRID JDBC Driver cubrid_jdbc.jar 파일은 CUBRID가 설치되어 있는 경로인 ~/CUBRID/jdbc/에서 얻을 수 있다.

Tomcat에서

위 세 파일과 CUBRID JDBC Driver를 CALTALIA_HOMEcommonlib 아래에두고, CALTALIA_HOMEconfserver.xml을 아래와 같이 추가 설정한다.

server.xml

server.xml의 끝 부분에 존재하는 “</Host>” 위에 아래를 추가한다.

<Context docBase="C:DBTest" path="" reloadable="true">//1


<Resource name="jdbc/CUBRIDDS" auth="Container" type="javax.sql.DataSource"// 2

factory="org.apache.commons.dbcp.BasicDataSourceFactory"

driverClassName="cubrid.jdbc.driver.CUBRIDDriver"// 3

url="jdbc:cubrid:localhost:33000:demodb:::"// 4

username="public" password=""// 5

initialSize="10"

maxActive="10"

maxIdle="10"

maxWait="-1"

poolPreparedStatements="true"/>// 6

</Context>

·1: 루트 디렉토리를 설정하는 부분으로 예제에서는 C:DBTest (Unix/Linus /DBTest) 디렉토리를 생성하고 이곳을 루트 디렉토리로 설정한다. 이곳에 jsp 등 관련 파일이 위치하면 된다.

·2: JNDI name으로 임의로 사용 할 이름을 입력 한다.

·3: JDBC 드라이버 클래스 이름으로 cubrid.jdbc.driver.CUBRIDDriver를 입력하면 된다.

·4: 접속 할 데이터베이스 url로 jdbc:cubrid:IP:PORT:DB이름::: 형식으로 입력한다.

IP : 데이터베이스가 있는 서버 IP

PORT : 큐브리드 브로커의 포트(default : 33000)

DB이름 : 데이터베이스 이름

·5: 데이터베이스에 접속할 아이디.기본적으로 데이터베이스에는 DBAPUBLIC 2개의 아이디가 존재한다.ID 파라미터에 아무 것도 입력하지 않고 “”를 입력하면 기본적으로 PUBLIC 아이디로 데이터베이스에 접속한다. 앞에서 사용한 아이디에 암호가 설정되어 있다면 PASSWORD 파라미터를 입력한다.없다면 그냥 “”를 입력한다.

·6: Statement 풀링이 사용되기 위한 설정으로 true로 설정해야 한다.

간단예제

아래 소스를 CALTALIA_HOMEconfserver.xml의 <Context docBase="C:DBTest" path="" reloadable="true"> 부분에 설정한 루트 디렉토리에 작성한다.

<%@ page contentType="text/html; charset=euc-kr"%>

<%@ page import="java.util.*" %>

<%@ page import="java.sql.*" %>

<%@ page import="javax.sql.*" %>

<%@ page import="javax.naming.*" %><%//1%>

<%

Connection conn = null;

PreparedStatement pstmt = null;

ResultSet rs = null;

try {

// 큐브리드 DBConnect하기 위해서 JNDI를 통해 DataSourse를 가져온다.

Context initContext = new InitialContext();//2

DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/CUBRIDDS");//3

conn = ds.getConnection();//4

String sql = "select name from nation";

pstmt = conn.prepareStatement(sql);

rs = pstmt.executeQuery();

while(rs.next()) {

out.println("name ==> " + rs.getString(1));

out.println("<br>");

}

} catch ( SQLException e ) {

System.err.println(e.getMessage());

} catch ( Exception e ) {

System.err.println(e.getMessage());

} finally {

try{

if ( rs != null ) rs.close();

}catch( Exception e ) {}

try{

if ( pstmt != null ) pstmt.close();

}catch( Exception e ) {}

try{

if ( conn != null ) conn.close();

}catch( Exception e ) {}

}

%>

·1: Connection Pool을 이용한 DB Connection은 JNDI를 통해 DataSourse를 가져오고 DataSourse를 통해 DBConnection하게 되는데, 이 때 ContexDataSource를 사용하기 위해서 javax.naming.*javax.sql.*import 해준다.

·2: JNDI name으로 임의로 사용 할 이름을 입력 한다.

·3: JNDI를 통해 DataSource를 가져오는데 DataSource ds = (DataSource) initContext.lookup("java:comp/env/jdbc/CUBRIDDS"); 와 같이 server.xmlResource name에 설정된 값 앞에 java:comp/env/를 붙여서 사용한다.

·4: DataSource를 통해 DB에 Connection한다.

위 예제 파일명이 dbcp_test.jsp로 가정하고 예제 실행을 위해 웹페이지를 http://localhost:8080/dbcp_test.jsp또는 http://<설치한서버의주소>:8080/dbcp_test.jsp 로 접속하면 결과를 볼 수 있다.

Java class에서

DBCP 사용을 위한 세 파일과 CUBRID JDBC Driver를 classpath에 설정한다.

ConnectionManager.java

DBCP 를 생성하고 Connection을 제공하는 역할을 한다.

import java.sql.*;

import org.apache.commons.dbcp.BasicDataSource;// 1

public class ConnectionManager {

private BasicDataSource ds = null;

// DBCP를 생성한다.

private void setupDataSource() {

if (ds == null) {

ds = new BasicDataSource();

String url = "jdbc:cubrid:localhost:33000:demodb:::";

String className = "cubrid.jdbc.driver.CUBRIDDriver";

String userName = "public";

String passWord = "";

ds.setDriverClassName(className);// 2

ds.setUrl(url);// 3

ds.setUsername(userName);// 4

ds.setPassword(passWord);

ds.setMaxActive(10);

ds.setInitialSize(10);

ds.setMinIdle(5);

ds.setMaxWait(5000);

ds.setPoolPreparedStatements(true);// 5

}

}

// BasicDataSource로부터 connection을 얻어온다.

public Connection getConnection() throws SQLException {

setupDataSource();

return ds.getConnection();// 6

}

}

·1: BasicDataSource을 사용하기 위해 org.apache.commons.dbcp.BasicDataSource을 import 시켜준다.

·2: JDBC 드라이버 클래스 이름으로 cubrid.jdbc.driver.CUBRIDDriver를 입력하면 된다.

·3: 접속 할 데이터베이스 url로 jdbc:cubrid:IP:PORT:DB이름::: 형식으로 입력한다.

IP : 데이터베이스가 있는 서버 IP

PORT : 큐브리드 브로커의 포트(default : 33000)

DB이름 : 데이터베이스 이름

·4: 데이터베이스에 접속할 아이디.기본적으로 데이터베이스에는 DBAPUBLIC 2개의 아이디가 존재한다.ID 파라미터에 아무 것도 입력하지 않고 “”를 입력하면 기본적으로 PUBLIC 아이디로 데이터베이스에 접속한다. 앞에서 사용한 아이디에 암호가 설정되어 있다면 PASSWORD 파라미터를 입력한다.없다면 그냥 “”를 입력한다.

·5: Statement 풀링이 사용되기 위한 설정으로 true로 설정해야 한다.

·6: BasicDataSource로 부터 connection을 얻어온다.

execDBCP.java

ConnectionManager를 이용하여 connection을 얻어오고 질의를 수행한다.

import java.sql.*;

public class execDBCP{

public static void main(String args[]){

Connection conn = null;

PreparedStatement pstmt=null;

ResultSet rs =null;

String query = "select name from nation";

try {

ConnectionManager cm = new ConnectionManager();

conn = cm.getConnection();//1

pstmt = conn.prepareStatement(query);

rs = pstmt.executeQuery();

while(rs.next()){

System.out.println("result=>"+rs.getString(1));

}

rs.close();

pstmt.close();

} catch (Exception e) {

e.printStackTrace();

}finally {

if(rs != null) try { rs.close(); } catch(Exception e) {}

if(pstmt != null) try { pstmt.close(); } catch(Exception e) {}

if(conn != null) try { conn.close(); } catch(Exception e) {}

}

}

}

·1: ConnectionManager을 생성하고 getConnection을 호출하여 DBCP로 부터 connection을 얻어온다.

이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
ss  (0) 2012.05.08
테스트  (0) 2012.05.08
Posted by 사라링

ERROR&cokie

 | JSP
2012. 5. 8. 15:55

 error 에러 메세지 페이지로 넘김

web.xml 파일에 추가 하자.

   <error-page>
        <error-code>404</error-code>
        <location>/error/error404.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/error/error500.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.NumberFormatException</exception-type>
        <location>/error/errorNumber.jsp</location>
    </error-page> 

 

404 에러 500 에러가 가장 많이 검출 되는 에러 이며. 코더 후에 사용자(클라이언트)에 의해 발생 하는 에러를 따로 만든 에러.jsp 에 뛰어 볼수 있도록 설정 가능 하다.

 

cookie


    Cookie cookie = new Cookie("LOVE","gubook");
    response.addCookie(cookie);
   
    cookie =new Cookie("HATE",URLEncoder.encode("황지혜 "));
    cookie.setPath("/");
    response.addCookie(cookie);
%>
<a href="view.jsp">쿠키 보기</a>
<a href="../062/view.jsp">쿠키 보기</a>

를 페이지 하면 쿠기에 내용이 저장 된다.  LOVE 라는 name 값에value 로 gubook 이 들어 갔다.

이렇게 실행된 값은 response.addCookie(변수 )로 서버쪽으로 보내 진다.

view.jsp 에서 링크를 클릭 하여 보자.

 

 

view.jsp 의 소스는

<%
    Cookie[] cookies = request.getCookies();
    if(cookies!=null){
%>
<ul>
    <%
    for(int i =0;i<cookies.length;i++){
    %>
    <li>
        <%=URLDecoder.decode(cookies[i].getValue(),"utf-8")%>
    </li>
<%} %>
</ul>

<%}else { %>
    쿠키가 없네 ? = ㅠ=
<%} %>

를 포함하고 있다. 같고 올때는 request를 쓴다. 가져온 값은 배열 형태로 가져 온다. 

 

 

 


이 글은 스프링노트에서 작성되었습니다.

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ss  (0) 2012.05.08
테스트  (0) 2012.05.08
Posted by 사라링

ss

 | JSP
2012. 5. 8. 15:41

sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

'JSP' 카테고리의 다른 글

fileDownload.jsp  (0) 2012.05.08
FileDownLoad  (0) 2012.05.08
cubrid-dbcp설정.  (0) 2012.05.08
ERROR&cokie  (0) 2012.05.08
테스트  (0) 2012.05.08
Posted by 사라링

BLOG main image
.. by 사라링

카테고리

사라링님의 노트 (301)
JSP (31)
J-Query (41)
JAVA (24)
디자인패턴 (1)
스트러츠 (3)
안드로이드 (11)
오라클 (45)
우분투-오라클 (1)
이클립스메뉴얼 (6)
스프링3.0 (23)
자바스크립트 (10)
HTML5.0 (17)
정보처리기사 (1)
기타(컴퓨터 관련) (1)
문제점 해결 (3)
프로젝트 (2)
AJAX (4)
하이버네이트 (3)
트러스트폼 (11)
Jeus (2)
재무관리(회계) (5)
정규식 (5)
아이바티스 (8)
취미 (2)
소프트웨어 보안 관련모음 (0)
정보보안기사 (6)
C언어 베이직 및 프로그램 (3)
보안 관련 용어 정리 (2)
넥사크로 (6)
웹스퀘어_ (0)
Total :
Today : Yesterday :