'Language/JSP'에 해당되는 글 49건

  1. 2013.04.23 Tomcat과 한글 인코딩
  2. 2013.02.13 JSP pageEncoding속성, 케릭터셋(charset)
  3. 2012.11.07 mybatis[SqlSession]
  4. 2012.11.07 springmvc+mybatis
  5. 2012.11.02 SiteMesh와 Freemarker
  6. 2012.11.02 Core of JSP
  7. 2012.11.02 Basic of JSP
  8. 2012.11.02 ServletContext와 RequestDispatcher
  9. 2012.10.31 스프링 MVC - 뷰 영역 구현
  10. 2012.10.31 web.xml (servlet)

Tomcat과 한글 인코딩

Language/JSP 2013. 4. 23. 18:10

개요

웹프로그래밍을 하다보면 항상 한글인코딩과 관련된 문제에 봉착하게 된다. 그럴때면 보통 웹검색을 통해 대부분의 해결책을 찾고는 하였다. 하지만, 그 해결책이라는 것이 대부분 경험담에 불과하고, 우리가 맞닥뜨린 문제와는 미묘하게 다른부분이 있기도 하여 근본적인 해결방법이 되지 않을 경우도 있다. 우연찮게 해결을 하더라도, 시간에 쫒기다보니, 근본적인 원인을 해결하지 못하고 해결하기에 급급하여 다음에 또 비슷한문제를 겪는 악순환을 겪기도 한다.


실제로 톰캣에서 한글이 어떻게 인코딩/디코딩되는걸까?

본인 역시 늘 고민해오던 문제였는데, 마침 시간이 나게되어, JSP파일에서부터 클라이언트의 응답메시지와 요청메시지, 그리고 톰캣에서의 인코딩과정에 대해 간략하게 정리해보았다.


이 글은 톰캣 5.5.x, 와 6.0.x 에서 테스트 되었다.

JSP 인코딩과 HTML 인코딩

우리가 작성한 JSP 파일은 컨테이너(여기서는 톰캣)에 의해 JAVA 파일로 변경이 된다.
이때 첫번째 인코딩이 일어나게 되는데, 이 시점에서 확인하여야할것은 JSP 파일 인코딩과 JSP 문자 인코딩이다. JSP 파일 인코딩과 JSP 문자 인코딩이 다를 경우 여지없이 한글이 깨져 버린다.


JSP 파일 인코딩은 실제 해당 파일이 작성될때 사용된 캐릭터셋으로

Eclipse 같은 툴을 사용하면 확인할 수 있다.


JSP 문자 인코딩은 컨테이너가 JSP파일을 JAVA로 변경하기위해 읽어들일때, 해당 JSP파일이 어떤 문자셋으로 이루어져 있는가를 컨테이너에게 알려주기 위해 JSP의 page 지시자에 선언되어 있는 캐릭터셋이다.

JSP 파일의 문자 인코딩 캐릭터셋은 JSP의 page 지시자의 속성중에 pageEncoding를 확인하면 된다. 디폴트 값은 ISO-8859-1 이지만, ContentType에서 인코딩을 정의 하지 않았을 경우에만 디폴트 값이 적용된다.


예를 들어 다음과 같은 내용의 JSP파일을 생성하였다고 가정하자. (파일 인코딩은 UTF-8이라고 가정한다)


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="EUC-KR"%>
<!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>
<div>
    <form action="/test2.jsp" method="post">
        <input type="text" name="keyword" > <input type="submit">
    </form>

</div>
</body>
</html>


다음은 위의 JSP 파일이 변경된 JAVA 파일의 일부이다.

(서블릿을 컨테이너에 배포한다음 TOMCAT_HOME/work/ 하위에서 찾을 수 있다)


out.write("\r\n");
      out.write("<head>\r\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
      out.write("<title>�����/title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("<div> \r\n");
      out.write("\t<form action=\"/test2.jsp\" method=\"post\">\r\n");
      out.write("\t\t<input type=\"hidden\" name=\"actions\" value=\"test2\" >\r\n


JSP의 <title>의 한글이 깨져 있음을 확인할 수 있다.


page 지시자에는 pageEncoding 뿐만 아니라 contentType 이라는 속성도 있는데, 이 속성은 MIME 타입과 해당 페이지에서 사용될 인코딩 문자셋을 설정한 다. 즉, 이 값에 따라 브라우져가 서버의 메시지를 어떤 문자셋으로 해석할지가 결정되며, 페이지에서 사용되는 문자들의 인코딩 문자셋이 결정된다. 대부분의 브라우져의 기능을 살펴보면 문자 인코딩을 변경하는 메뉴가 있는데, 여기에 기본으로 선택된 값이 contentType에 설정된 값이다.


contentType 이 생략될 경우 pageEncoding 값을 따르며, pageEncoding 까지 생략되면 기본적으로 ISO-8859-1 문자셋을 따른다.


예를 들어 contentType의 값이 UTF-8 이라면 클라이언트의 브라우져는 서버의 응답을 UTF-8로 해석하여 HTML 파일을 화면에 뿌리게 된다. 그리고 페이지의 모든 문자들은 UTF-8의 문자셋을 기반으로 하게된다.


HTML의 meta 태그내의 content 속성은 단순히 메타 태그로써 페이지의 정보를 알려주는 기능외에 실제 인코딩에 관여하지 않는다는 것에 주의하도록 한다.

 <head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>test</title>
</head>


서블릿과 톰캣 인코딩

톰 캣은 클라이언트에서 서버로의 요청메시지를 서블릿 스펙에 따라 기본적으로 ISO-8859-1 로 인코딩되어 처리되도록 되어 있다. 즉, 클라이언트에서의 문자열이(contentType) UTF-8 이든, EUC-KR 이든 ISO-8859-1로 인코딩 된다는 것이다.

(메시지의 인코딩은 앞에서 언급한바대로 웹페이지의 ContetType 값에 따른다)


즉, 다음의 코드가 실행되는 것과 같다.

//UTF-8 인코딩된 메시지
new String( "UTF-8".getByte("UTF-8"),"ISO-8859-1");

//EUC-KR 인코딩된 메시지
new String("EUC-KR".getByte("EUC-KR"),"ISO-8859-1");


어 라? 이상하지 않은가? 한글 처리를 위한 UTF-8이나 EUC-KR은 2바이트 문자인데, 1바이트 문자인 ISO-8859-1로 인코딩하면 문자열이 깨지지 않겠는가? 깨어지는 것처럼 보이지만 실제로는 문자열의 바이트값이 보존된다. 여기서 인코딩/디코딩의 개념과 ISO-8859-1 이라는 문자셋에 대한 이해가 필요하다.


우선 문자셋이라는 것은 글자와 바이트의 매핑관계를 정의하는 것으로 문자셋에 따라 이 매핑관계가 달라진다.

예를 들어 EUC-KR문자셋에서 "테스트" 라는 문자열은 "c5d7bdbac6ae" 바이트배열값으로 매핑된다.

이때 EUC-KR 문자셋은 2바이트 문자셋이므로 c57b값이 "테" 라는 문자열값에 매핑되며 7bdb와 c6ae는 각각 "스"와 "트"에 매핑이된다.

이렇게 문자열을 바이트 시퀀스로 변환하는 과정을 인코딩, 그 반대 과정을 디코딩이라고 한다.


이 론적으로 EUC-KR은 2바이트 문자로써 65535개의 문자를 매핑하는것이 가능하지만 실제로는 대부분의 바이트가 비어있다. 이는 UTF-8이나 KSC5601, MS949 등 마찬가지이다. 문자셋들은 동일한 바이트값에 대한 매핑문자가 서로 다르며, 문자 구성또한 달라 매핑이 불가능한 문자도 있다. 예를 들어 유니코드 "뷁" 문자를 EUC-KR 로 인코딩할경우 "뷁" 문자가 EUC-KR 문자셋에 정의되어 있지 않으므로, 자바는 이를 "?(0X3F)"로 변경해버린다. (이 또한 JVM 벤더에 따라 다르게 동작한다.) "?"로 매칭된 문자는 원래의 유니코드로 디코딩된다고 해도 원래의 "뷁" 문자를 표현하지 못하게된다. 이처럼 문자간의 인코딩/디코딩시에 데이터 손실이 일어나게되는데, ISO-8859-1 문자셋은 1바이트로 이루어져있지만, 유일하게 빈공간없이 모든 바이트값에 대해 문자를 매핑하고 있다. 따라서, ISO-8859-1 문자셋으로 인코딩되었다하더라도 원래 문자셋으로 디코딩만 하면 데이터의 손실없이 원래의 문자를 표현할수 있게된다.

ISO-8859-1 문자셋에 대해서는 다음을 참고하라 (http://en.wikipedia.org/wiki/ISO/IEC_8859-1#ISO-8859-1)


다시 톰캣이야기로 돌아가서 위에서 언급하길 서블릿 스펙에 따라 톰캣은 모든 요청메시지를 ISO-8859-1로 인코딩한다고 하였다.

즉, EUC-KR 문자열의 바이트 값인 "c5d7bdbac6ae"를 톰캣이 받게되면 이 바이트 배열을 ISO-8859-1 문자셋으로 인코딩된것으로 간주하고 ISO-8859-1문자셋을 이용해 디코딩해버린다. 그렇게 되면 "c5d7bdbac6ae" 바이트배열은"Å×½ºÆ®" 이라는 문자열로 디코딩되는데, ISO-8859-1문자셋은 각 바이트에 매칭되는 문자값이 모두 존재하기때문에 의미없는 문자로 표현될지언정 매칭문자가 없어 "?"로 표현될 일이 없다. 즉, 문자열이 깨지지 않는다. 따라서, "Å×½ºÆ®" 문자열을 다시 EUC-KR 문자셋으로 인코딩하게되면 원래의 문자열로 돌아가게되는것이 보장된다.

byte[] bytes = "테스트".getBytes("EUC-KR");
String str = new String(bytes,"ISO-8859-1");
System.out.println(hex(bytes)); //hex() 는 바이트 배열값을 HEX코드로 출력하는 기능
System.out.println(str);
bytes = "Å×½ºÆ®".getBytes("ISO-8859-1");
str = new String(bytes,"EUC-KR");
System.out.println(hex(bytes));
System.out.println(str);
=============================================
c5d7bdbac6ae
Å×½ºÆ®
c5d7bdbac6ae
테스트


톰캣으로 전달된 ISO-8859-1 인코딩된 데이터는 톰캣 내부에서 사용자의 요청메시지를 처리하기위해 request 객체를 생성하는 과정에서 인코딩 과정을 거치게된다. 이때 적용되는 인코딩 문자셋은 기본적으로 null로써 시스템 프로퍼티인 file.encoding에 정의된 인코딩 문자셋(이하, 시스템 인코딩 문자셋)을 사용하도록 되어 있다. 클라이언트의 요청메시지의 원래 인코딩 문자셋과 시스템 인코딩 문자셋이 다르다면 원래의 문자셋으로 복원이 불가능해지므로 한글 인코딩이 깨어지게 된다. 따라서 한글 데이터를 보존하기 위해서는 new String("문자열".getByte("ISO-8859-1"), "EUC-KR") 같이 클라이언트에서 전송시에 사용된 문자셋으로 디코딩 과정을 먼저 실행한다음 원래대로 문자열을 복원하여야 한다. 번거로운 인코딩작업 대신에 클라이언트의 요청 메시지 전체에 대해 명시적으로 인코딩 문자셋을 지정해주는 방법이 있는데, 그 방법은 아래와 같다. (단 인코딩 문자셋은 request 객체가 생성되기전에 정의되어야 한다)

request.setCharacterEncoding("문자셋이름")

톰 캣은 클라이언트의 요청메시지에 대한 request 객체의 생성이 끝나면, 응답 메시지를 작성하기위해 response 객체를 생성하게 된다. 이때 response 객체의 생성시에 사용되는 인코딩 문자셋은 JSP 파일의 page 지시자의 contentType 속성에 정의된 인코딩 문자셋을 따르게 된다. 서버의 응답 메시지를 생성할때 사용되는 인코딩 문자셋과 브라우져에서 이를 해석하는 문자셋이 항상 contentType을 따르도록 되어있으므로 프로그래머는 이 단계에서는 인코딩에 대해 신경쓰지 않아도 된다. 하지만 문제는 request 객체와 response 객체 사이에서 일어난다. 대부분의 응답메시지는 요청메시지의 데이터를 포함하게 되는데, 요청메시지에 대해 디코딩을 완벽하게 수행했다하더라도, request 객체와 response 객체의 인코딩 문자셋이 다르다면 request 객체의 데이터가 response 객체에서 인코딩이 깨어지게 된다.


이렇게 생성된 서버의 응답메시지는 ISO-8850-1 인코딩되어 클라이언트의 브라우져에 전달된다. 전달된 응답 메시지는 클라이언트의 브라우져에서 contentType에 정의된 인코딩 문자셋을 이용하여 해석된 다음 출력되게 된다.


GET과 POST

앞 절에서 클라이언트 요청메시지에 전체에 대해 명시적으로 인코딩 문자셋을 지정하는 방법을 소개하였다. 하지만 이 경우 request 객체가 생성되기 이전에 인코딩 문자셋을 지정하여야 하므로 톰캣에서는 필터를 사용하여 인코딩 문자셋을 지정할 것을 권장하고 있다. 하지만 이 경우에도 약간의 문제가 있다. 클라이언트의 요청메시지는 GET과 POST 두 가지 방식으로 서버에 전달되는데, 필터에 의한 인코딩 문자셋을 지정하는 방법은 POST요청에 한해서만 유효하다는 점이다. GET 방식으로 전달 되는 파라메터는 URL에 포함되어 전달되는데, URL은 요청 메시지 내부의 파라메터는 지정된 인코딩 문자셋을 사용하지않고 기본적으로 ISO-8859-1 인코딩 문자셋에 따라 해석을 하기 때문이다. 따라서 URL에 대한 인코딩 문자셋을 명시적으로 지정해줄 필요가 있다. 이를 위해서는 톰캣의 tomcat_home/conf/server.xml 파일의 Connector 부분의 설정을 변경하여야 한다. 

 ...
    <Connector ... port="8080" URIEncoding="UTF-8" />
...


정리

지금 까지의 이야기를 정리하자면 한글을 적절하게 처리하기 위해서는 우선, 요청 페이지의 contentType을 확인해야 하며, 사용자의 요청메시지가 GET인지, POST인지를 확인한다. 그런다음 각각의 요청메시지 타입에 따라 서버에서 메시지를 처리하는 인코딩 문자셋을 확인한 다음, 마지막으로 응답 페이지의 contentType을 확인하여 이 4가지 인코딩 문자셋이 일치하는가를 살펴보아야 한다.


이 상의 내용은 실제 톰캣을 소스레벨까지 내려가서 살펴본것이아니라, 다분히 개인적인 지식과 경험에 의해 쓰여진 것이므로 다소 사실과는 다를지도 모른다. 하지만, 여러가지 실험과 근거에 의해 작성되었으므로 신뢰할 만하다고 생각한다. 한글 처리로 곤란함을 격게된다면 위의 과정을 밟아가면서 어디에서 인코딩이 깨지는가를 확인해보면 빠르게 문제를 해결을 할 수 있으리라 생각한다. 부디 한글로 인해 고민하는 날이 없기를 바란다.

출처 - http://blog.naver.com/codechaser?Redirect=Log&logNo=80085455490


:

JSP pageEncoding속성, 케릭터셋(charset)

Language/JSP 2013. 2. 13. 09:12

JSP 페이지를 분석하는 과정에서 현재 페이지가 어떤 인코딩을 이용해서 작성되었는지 검사하여 그 결과로

선택된 케릭터셋을 이용해서 JSP페이지의 문자를 읽어오게 된다.

 

웹 컨테이너가 JSP페이지를 읽어올 때 사용할 케릭터 셋을 결정하는 기본 과정

1)기본 인코딩을 파일을 읽은 후, pageEncoding을 찾는다.

2)pageEncoding을 찾으면 해당 속성을 사용하고, 없으면 contentType을 검색한다.

3)contentType을 찾으면 해당 속성의 charset을 사용한다.

4)모두 해당되지 않는경우 ISO-8859-1 을 사용한다.

#)단, (1)번 과 (3)번의 경우 대상을 찾기 이전에 ASCII문자 이외의 글자가 포함되어 있지 않은 경우에 한해 적용된다.

 

JSP페이지를 구현할때는 "EUC-KR"을, 응답결과는 "UTF-8"로 하고싶다면?

<%@ page contentType="text/html; charset=utf-8" %>

<%@ page pageEncoding="euc-kr" %>

 

위와 같이 설정할 경우 pageEncoding의 속성값으로 JSP 페이지를 읽어오고,

charset의 속성값으로 응답결과를 생성한다.


출처 - http://blog.naver.com/hckind?Redirect=Log&logNo=20153004085

'Language > JSP' 카테고리의 다른 글

인코딩 - 8859_1의 비밀(?)  (0) 2013.04.23
Tomcat과 한글 인코딩  (0) 2013.04.23
mybatis[SqlSession]  (0) 2012.11.07
springmvc+mybatis  (0) 2012.11.07
SiteMesh와 Freemarker  (0) 2012.11.02
:

mybatis[SqlSession]

Language/JSP 2012. 11. 7. 14:14

Using an SqlSession

In MyBatis you use the SqlSessionFactory to create an SqlSession. Once you have a session, you use it to execute your mapped statements, commit or rollback connections and finally, when it is no longer needed, you close the session. With MyBatis-Spring you don't need to use SqlSessionFactory directly because your beans can be injected with a thread safe SqlSession that automatically commits, rollbacks and closes the session based on Spring's transaction configuration.

Note that it is usually not necessary to use a SqlSession directly. In most cases a MapperFactoryBean that will inject mappers into your beans, will be all that is needed. The MapperFactoryBean will be explained in detail in the next chapter.

SqlSessionTemplate

SqlSessionTemplate is the heart of MyBatis-Spring. This class is responsible for managing MyBatis SqlSessions, calling MyBatis SQL methods and translating exceptions. SqlSessionTemplate is thread safe and can be shared by multiple DAOs.

When calling SQL methods, including any method from Mappers returned by getMapper()SqlSessionTemplate will ensure that the SqlSession used is the one associated with the current Spring transaction. In addition, it manages the session life-cycle, including closing, committing or rolling back the session as necessary.

SqlSessionTemplate implements SqlSession and is meant to be a drop-in replacement for any existing use of SqlSession in your code. SqlSessionTemplate should always be used instead of default MyBatis implementation DefaultSqlSession because the template can participate in Spring transactions and is thread safe for use by multiple injected mapper classes. Switching between the two classes in the same application can cause data integrity issues.

SqlSessionTemplate can be constructed using an SqlSessionFactory as a constructor argument.

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

This bean can now be injected directly in your DAO beans. You need a SqlSession property in your bean like the following

public class UserDaoImpl implements UserDao {

  private SqlSession sqlSession;

  public void setSqlSession(SqlSession sqlSession) {
    this.sqlSession = sqlSession;
  }

  public User getUser(String userId) {
    return (User) sqlSession.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

And inject the SqlSessionTemplate as follows

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
  <property name="sqlSession" ref="sqlSession" />
</bean>

SqlSessionTemplate has also a constructor that takes an ExecutorType as an argument. This allows you to construct, for example, a batch SqlSession by using the following in Spring's configuration xml:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
  <constructor-arg index="1" value="BATCH" />
</bean>

Now all your statements will be batched so the following could be coded in a DAO

public void insertUsers(User[] users) {
   for (User user : users) {
     sqlSession.insert("org.mybatis.spring.sample.mapper.UserMapper.insertUser", user);
   }
 }

Note that this configuration style only needs to be used if the desired execution method differs from the default set for the SqlSessionFactory.

The caveat to this form is that there cannot be an existing transaction running with a different ExecutorType when this method is called. Either ensure that calls to SqlSessionTemplates with different executor types run in a separate transaction (e.g. with PROPAGATION_REQUIRES_NEW) or completely outside of a transaction.

SqlSessionDaoSupport

SqlSessionDaoSupport is an abstract support class that provides you with a SqlSession. Calling getSqlSession() you will get a SqlSessionTemplate which can then be used to execute SQL methods, like the following:

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return (User) getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

Usually MapperFactoryBean is preferred to this class, since it requires no extra code. But, this class is useful if you need to do other non-MyBatis work in your DAO and concrete classes are required.

SqlSessionDaoSupport requires either an sqlSessionFactory or an sqlSessionTemplate property to be set. These can be set explicitly or autowired by Spring. If both properties are set, the sqlSessionFactory is ignored.

Assuming a class UserDaoImpl that subclasses SqlSessionDaoSupport, it can be configured in Spring like the following:

<bean id="userMapper" class="org.mybatis.spring.sample.mapper.UserDaoImpl">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

출처 - http://www.mybatis.org/spring/sqlsession.html

'Language > JSP' 카테고리의 다른 글

Tomcat과 한글 인코딩  (0) 2013.04.23
JSP pageEncoding속성, 케릭터셋(charset)  (0) 2013.02.13
springmvc+mybatis  (0) 2012.11.07
SiteMesh와 Freemarker  (0) 2012.11.02
Core of JSP  (0) 2012.11.02
:

springmvc+mybatis

Language/JSP 2012. 11. 7. 11:10

이제 대충 스프링이 뭐하는 프레임워크인지 감을 잡았다면 개발에 필요한 라이브러리와 환경을 구축해보자. 혹시나 MyBatis와 커넥션풀에 대해 잘 모르는 사람이라도 일단 속는 셈치고 함께 설치해주는 것이 좋다. 이 두가지 라이브러리와 더불어 스프링 시큐리티가 포함되면 정말 금상첨화지만 일단 스프링 시큐리티까지 설치하면은 설명이 길어지므로 시큐리티는 차후에 사용 포스트를 작성하도록 하겠다.


먼저 개발환경 구축에 필요한 라이브러리 또는 프레임워크 목록은 위 URL에 버전과 함께 상세히 나열되어있다. 혹시나 이 라이브러리들의 의존 라이브러리들이 포함되지 않았을 수도 있으므로 안전하게 메이븐을 이용해 라이브러리를 설치하도록 하자. 메이븐이란 훌륭한 라이브러리 관리 기술이 있음에도 이를 귀찮다고 이용하지 않는다면 프로그래머의 자격이 없다.


메이븐을 아직 설치하지 않았다면 위의 URL을 통해 메이븐을 설치하길 바란다. 더욱이 상기의 개발환경을 기본으로 구축했다는 전제하에 쓰는 글이므로 진행 중에 오류나 이해가 가지 않는 부분이 생길 수도 있다.




먼저 MyBatis에 대해 잘 모르는 사람을 위해 이 라이브러리를 설명하고자 한다. 사실 꼭 MyBatis를 써야하는 이유는 없지만 분명한 사실은 MyBatis와 같은 라이브러리를 활용하여 데이터 퍼시스턴스(Data Persistence)계층을 효율적으로 관리해야 한다는 점은 이미 웹개발자들에 있어서 일반적인 상식이 됬기 때문이다.

위의 문장에서 데이터 퍼시스턴스는 간단히 DB에서 정보를 꺼내오는 역할을 담당하는 계층을 말한다. 현대의 비지니스 객체 모델은 프리젠테이션 계층, 비지니스 로직 계층, 퍼시스턴스 계층으로 나눌 수 있는데 프리젠테이션은 우리가 현재 보고 있는 페이지 또는 브라우져로 출력되는 페이지들을 말하며 비지니스 로직은 퍼시스턴스와 프리젠테이션 사이에서 조율하는 계층을 일컫는다.


실제로 웹 어플리케이션은 위와같은 구도로 돌아간다. 스프링을 사용한다면 사용자가 보낸 요청을 스프링 컨테이너가 가로챈 뒤에 처리하므로 위의 그림보다 조금 복잡하게 돌아가긴 하지만 원리는 동일하다. 그러므로 상기의 계층 구도는 웹 개발에 있어서 매우 일반적인 구성이라고 할 수 있겠다.

사실 우리가 반드시 위의 구도를 지켜가며 코드를 칠 필요는 없다. 필자도 스프링을 알기 전에는 필자 맘대로 계층을 분류하고 메서드를 만들어대고 패키지를 찍어댔었다. 그런 의미에서 당신도 위와 같이 복잡한 계층을 무시하고 마음대로 코드를 쳐댈 수도 있지만 굳이 많은 사람들이 위와같은 계층을 지키려 노력하며, 보이지 않는 약속을 하는 이유는 어디까지나 효율적인 분업이 가능하게 하기 위해서다.

뜬금없는 소리지만 로마 군대가 강력했던 이유는 효율적인 징병제와 어디를 가더라도 로마 군인이라면 이해할 수 있는 진지를 구축했다는 점인데 이런 시대를 가로지르는 진리는 어느 분야에서든 통하기 마련이다. 로마 군대를 비유로 계층별 분류의 가장 큰 이유를 꼽자면 과거 선배 웹프로그래머들의 많은 시행착오 끝에 나온 결과이며 이런 계층별 분류가 웹프로그래머들만의 강력한 진지라고 할 수 있기 때문이다.

그러므로 퍼시스턴스 계층을 분리하는 것을 당연스럽게 받아들이자. 실제로 MyBatis 개발진들이 발표한 결과로 퍼시스턴스 계층을 분리하면 약 30~40% 정도의 엄청난 양의 코드가 절약된다고 한다. 게다가 양만 줄어드는게 아니라 코드자체가 매우 직관적인 표현으로 변하여 가독성까지 높일 수 있다.


먼저 위의 그림과 같은 순서로 하나의 프로젝트를 만들어보자. 프로젝트를 만들었다면 초기에 pom.xml에 구성되있는 라이브러리들이 조금 낡았다는 생각이 들 것이다. 일단 pom.xml파일을 열어 현재 문서의 가장 상단에 있는 POM.XML의 설정을 위한 라이브러리 최신 버전에 있는 모든 라이브러리들을 설치하도록 하자. 라이브러리 교체는 직접 pom.xml을 수정해도 되고 이클립스의 플러그인 기능을 이용하여 수정해도 되는데 이 과정은 매우 쉬우므로 따로 설명하지는 않겠다.

라이브러리들을 최신버전으로 바꾸어 주었다면 먼저 MyBatis와 스프링을 연동시켜야 한다. MyBatis와 스프링이 연동되려면 2가지 XML 파일들이 필요한데 매퍼 설정파일과 매퍼 XML파일이 바로 그 것이다. 이름은 어떤 것으로 해도 상관없지만 현재과정을 따라가는 만큼 이 문서에서 제시된 파일명으로 통일하는 게 좋겠다.

먼저 두가지 XML파일 중에 매퍼 설정파일을 보도록 하자. MyBatis가 별도의 매퍼 설정파일을 필요로 하는 이유는 매퍼 설정을 통해 퍼시스턴스 계층(MySQL, Oracle 등)의 옵션을 세부적으로 조절할 수 있으며 DB의 테이블 관리를 위해 만들어진 자바빈 파일들을 MyBatis만의 타입으로 매핑시키고 SQL구문이 들어있는 매퍼 XML파일을 등록시킬 수 있기 때문이다. 아래 이미지처럼 프로젝트 패키지에 XML파일을 만들고 위의 DTD를 선언하도록 해보자.


XML파일 이름은 아무래도 mybatis의 설정파일이니 mybatis-config.xml 정도가 괜찮겠다. XML파일을 만들었다면 아래의 DTD를 선언하고 본격적으로 설정을 해주도록 하자.

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "HTTP://mybatis.org/dtd/mybatis-3-config.dtd">

설정에는 다양한 옵션이 존재하는데 옵션에 관한 부분만 해도 어마어마하므로 MyBatis 한글 레퍼런스 문서를 참조해가며 직접 구현해보는 것을 추천한다. 다행히 iBatis때부터 열성적인 한국 사용자들이 존재해서 그런지 MyBatis 프로젝트 페이지에 한글 번역 레퍼런스 문서가 당당히 존재하고 있다. 

잡담이지만 MyBatis는 다른 인기 프레임워크와 다르게 별도의 관련 서적이 없는데, 그 이유는 서적이 필요 없을 정도로 쉽고 간편하기 때문이라고 생각한다. 여하튼 필자의 설정방법은 그냥 예제 정도로만 인식하고 실제 개발에 착수할 때는 각각의 옵션이 어떤 역할을 하고 있는지 정확히 인식하고 사용하여야 한다.

mybatis-config.xml의 예제

MyBatis 한글 레퍼런스 문서와 상기 이미지를 참조해가며 <configuration> 요소를 완성시키도록 하자. 위의 <settings>요소만 채웠다해서 매퍼 설정파일이 완성됬다고 할 수는 없지만 중요한 것은 과정을 밟아가며 이해하는 것이므로 이 정도에서 다음 과정으로 넘어가도록 하자.

매퍼 설정파일의 <settings>요소를 작성하였다면 이젠 매퍼 XML파일을 만들 차례다. 매퍼 XML파일은 직접 데이터베이스에서 동작하는 SQL구문을 작성하는 공간인데 이 역시 MyBatis 한글 레퍼런스 문서에 자세한 설명이 있으니 꼭 한번 정독해보기를 권장한다.

개인적으로 매퍼 XML파일을 배치시키는 가장 좋은 방법은 매퍼 XML파일 하나로 모든 쿼리를 처리하는 것보다 테이블에 맞춰 잘게 자른 다음에 패키지 별로 분류 시키는 것이 유리하다. 만약 이런 패키지 분류에 대해 잘 모르겠다면 아래의 문서를 읽기를 권장한다. 패키지 분류는 매우 중요한 상식이므로 이런 분류법에 대해 잘 모른다면 문서를 읽는데 큰 어려움이 있을 것이다.

필자의 매퍼 XML파일 배치방법


현재까지의 과정을 잘 따라왔다면 상단의 이미지처럼 MyBatis 설정 파일들이 위치되었을 것이다. 상단의 이미지에서 users 패키지는 앞으로 우리가 users란 테이블을 만든 뒤 이 테이블을 이용해 테스트할 예정이므로 미리 선행으로 패키지를 분류해둔 것이다. users-sql.xml란 이름으로 매퍼 XML파일을 만들었다면 다음과 같은 DTD를 선언해 주도록 하자.

<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

users-sql.xml의 예제

상단의 이미지는 필자가 작성한 매퍼 XML파일의 모습이다. <mapper>요소 안에 별다른 내용이 없는 이유는 아직 users 테이블도 만들지 않은 데다 마땅히 넣을 만한 SQL구문도 없으므로 향후 작성을 위해 가장 기본적인 형태만 유지한 것이다. 여기서 주의 할 점은 namespace 속성은 Java 코드에서 <mapper>요소를 구분하는데 쓰이는 중요 속성이므로 반드시 설정하도록 한다.(테이블 명과는 아무 관계가 없다. 다만 통일감을 위해 동일명으로 지었을 뿐이다.)


MyBatis의 두 설정파일은 위와 같은 의존관계를 갖고 있다. 매퍼설정파일은 하나 이상의 매퍼XML파일을 매핑할 수 있으며 스프링 컨텍스트 또한 하나 이상의 매퍼설정파일을 매핑할 수 있다. 이제 만들어 둔 users-sql.xml을 mybatis-config.xml에 매핑시켜보도록 하자. mybatis-config파일은 퍼시스턴스 계층의 세부옵션과 더불어 이처럼 세부적인 매퍼 XML파일들을 묶어주는 큰 역할을 담당하고 있다.

mybatis-config.xml파일에 user-sql.xml을 매핑시키는 예제

위의 예제처럼 설정했다면 이제 매퍼XML파일은 매퍼설정파일을 통해 스프링컨텍스트와 연동될 수 있게 되었다. 간단한 테스트를 위해 users테이블을 만들고 mybatis-config.xml파일을 스프링 컨텍스트와 연결시켜보자.

위의 예제와 같이 users란 테이블을 만들고 필드 또한 일치시키도록 한다. 그리고 스프링 컨텍스트 파일은 mybatis만 독자적으로 관리할 수 있게끔 mybatis-context.xml이란 새 컨텍스트 파일을 만들기로 하자. 모든 준비가 끝났다면 mybatis-context.xml에 커넥션풀을 활용한 dataSource 빈을 만들 차례다.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/데이터베이스명" />
<property name="username" value="DB접속 아이디" />
<property name="password" value="DB접속 비밀번호" />
</bean>

위의 dataSource 빈은 apache에서 제공하는 커넥션풀을 이용하여 생성한 것이다. 커넥션풀이란 매번 접속 때마다 커넥션을 생성하는 게 아닌, 미리 일정량의 커넥션을 구현해 놓은 뒤 커넥션 요청이 올 때마다 이 커넥션을 제공하는 방식을 일컫는다. 커넥션풀은 단순히 좋아서 쓰는게 아니라 반드시 구현해야 하는 기술이다. 당신의 DB가 요청마다 커넥션을 생성하는데는 어느 정도 한계가 있으며 오라클 같이 DB를 만들어 파는 회사는 이런 커넥션 양에 값을 매기기도 한다.

물론 상업용 WAS(Web Application Server)같은 경우는 서버 단에서 DB 풀서비스를 제공해주기도 하지만 우리처럼 가난한 영세민이 사용하는 톰캣에서는 그런 기능을 기대할 수 없다. (그렇다고 톰캣을 폄하하는 것은 절대 아니다.) 우리는 커넥션풀 라이브러리를 적극 활용하여 현재 확보된 커넥션을 최대한 효율적으로 사용 함과 동시에 대량의 커넥션 요청에도 유연하게 대처할 수 있도록 반드시 커넥션풀을 이용해 dataSource를 구현해야만 한다.

여담이지만 오픈소스 커넥션풀에는 dbcp와 c3p0 두가지가 있다. 위의 예제에서는 dbcp를 이용하여 구현하였지만 둘 중에 어느 것을 활용한다 해도 상관은 없다. 어디까지나 선택은 독자의 몫이다.

위의 빈을 mybatis-context.xml에 입력한 다음 부족한 세부 설정을 맞추어서 자신의 DB와 연동시키도록 하자. 모든 설정을 끝마쳤다면 이제 당신은 커넥션 풀을 이용한 데이터베이스와 스프링 프로젝트의 연동에 성공한 셈이다. 이제 dataSource빈을 MyBatis에 주입시키기만 하면 끝이므로 다음과 같이 mybatis-context.xml파일에서 아래와 같은 빈을 만들어 보도록 하자.

<context:annotation-config />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:org/danzka/vodka/mybatis-config.xml" />
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
</bean>


MyBatis를 처음 접한 사람이라면 위의 설정만으론 이해가 가지 않을 것이다. 자세한 설명을 위해 먼저 SqlSessionFactoryBean을보도록 하자. 이 빈은 mybatis의 SqlSessionFactory 객체를 스프링의 팩토리빈을 활용하여 생성시켜준다. 팩토리빈이란 다이나믹 프록시를 스프링에서 쉽게 구현할 수 있도록 한 것인데 자세한 것은 토비의 스프링 - 트랜잭션 부분을 보면서 심화학습하도록 하고 중요한 것은 이 빈이 SqlSessionFactory 빈을 만들어 준다는 것이다.

SqlSessionFactory 클래스는 데이터베이스 정보와 설정파일을 필요로 하는데 dataSource와 mybatis-config.xml파일을 각각 매핑시키면 된다. 여기서 주의할 점은 SqlSessionFactoryBean 클래스는 setConfigLocation란 메서드를 통해 해당 XML을 resource객체로 변환시키기 때문에 보통 경로설정 문제로 난처한 상황을 겪는 경우가 많다. 하지만 우리는 mybatis-config.xml파일을 클래스가 위치하는 폴더에 존재하기 때문에 'classpath:'를 통해 쉽게 설정파일을 가져올 수가 있게 되었다. 지금은 잘 느끼지 못하겠지만 이런 파일배치의 장점은 향후 JUnit을 이용한 테스트와 웹으로 직접 배포하는 테스트를 동시에 하려고 하는 과정에서 그 편리함을 깨닫게 될 것이다.

여기까지 완료되었다면 SqlSessionTemplate 클래스 생성자에 작성한 sqlSessionFactory를 주입시켜주면 되는데 <constructor-arg>라는 요소를 활용하여 해당 클래스의 생성자에 주입해주도록 하자. SqlSessionTemplate 클래스는 디테일한 설정을 위해 3가지 방법으로 생성자를 주입받을 수 있는데 위와 같이 단순히 sqlSessionFactory만 주입하면 기본설정을 따르게 된다는 사실을 기억해야 한다.

이렇게 mybatis-context.xml파일의 설정이 모두 끝났으므로 마지막으로 컨테이너가 인식할 수 있도록 web.xml에 만든 컨텍스트 파일을 등록시키기만 하면 된다. 상기의 이미지와 같이 입력하면 자동으로 컨테이너가 시작될 때 mybatis-context.xml 파일도 함께 읽어 들일 것이다.



이제 본격적으로 MyBatis의 기능을 이용해 보도록 하자. MyBatis를 이용하기 위해선 위에서 만든 SqlSessionFactory와 SqlSessionTemplate를 주입받아 직접적으로 MyBatis의 기능을 이용할 수 있는 클래스 파일을 만들어야 하는데 정말 감사하게도 mybatis는 스프링을 이용하는 유저를 위해 이런 역할을 담당하는 클래스를 만들어 두었다. 그 클래스는 바로 라이브러리 중 mybatis-spring-1.0.2.jar 파일에 들어있는 SqlSessionDaoSupport란 클래스인데 우리는 이 클래스를 상속받아 쓰거나 주입받아 쓰기만 하면 된다.

SqlSessionDaoSupport클래스의 주입부분

위의 소스 중에 설명도 없이 스리슬쩍 넘어간 부분이 있는데 바로 <context:annotation-config />란 설정이다. 이 설정을 해두면 코드에서 @Autowired를 활용하여 주입없이도 바로 xml 객체를 불러올 수 있게 된다. 아직 한번도 @Autowired를 사용한 기억이 없는데 왜 설정이 필요하냐면 바로 위의 이미지처럼 우리가 활용할 SqlSessionDaoSupport 클래스가 이 설정을 요구하기 때문이었다. 만약에 위의 설정이 없다면 우리는 직접 SqlSessionDaoSupport빈을 만들고 직접 주입시켜주어야만 한다. 대신 이 설정이 있다면 별도의 주입없이도 자연스럽게 필요한 객체를 불러들일 수 있게 된다.

1장의 내용은 여기까지로 하고 다음 장에서는 SqlSessionDaoSupport클래스를 활용하여 직접 users 테이블에서 데이터를 불러오고 테스트하는 방법을 다루도록 하겠다. 참고로 이 클래스를 이용하는 방법은 주입방법과 상속방법이 있는데 우리는 상속을 통한 방법으로 접근 하고자 한다. 더불어 스프링을 활용한 트랜잭션 설정까지 함께 다루도록 하겠다.


출처 - http://springmvc.egloos.com/492767

'Language > JSP' 카테고리의 다른 글

JSP pageEncoding속성, 케릭터셋(charset)  (0) 2013.02.13
mybatis[SqlSession]  (0) 2012.11.07
SiteMesh와 Freemarker  (0) 2012.11.02
Core of JSP  (0) 2012.11.02
Basic of JSP  (0) 2012.11.02
:

SiteMesh와 Freemarker

Language/JSP 2012. 11. 2. 16:30

SiteMesh와 Freemarker

SiteMesh개념

-많은 페이지로 구성되는 대형 사이트를 구축할때 웹 페이지 레이아웃을 쉽게 구성하기위한 프레임워크이다.
-SiteMesh는 web-page layout이고, decoration(장식) 프레임웍이고, 일관된 look/fee, navagation, layout scheme를 필요로하는 많은 페이지로 구성된 큰 사이트를 만드는것을 도와주는 web-application 통합 프레임웍이다.
-Servlet Filter 이다. 그러므로, Servlet 2.3스펙을 따른 컨테이너가 필요하다. 
-순수자바로 구현되어있기 때문에 플랫폼에 독립적이다.

  • JRE 1.3보다 높은 버전이 요구된다. 좋은 성능을 위해 JRE 1.4.x 또는 보다 높은 버전을 사용해라.
    -Java SDK 1.4 또는 그 이상, Jakarta Ant version 1.5 또는 그이상에서 빌드해라.
    참고 : http://www.opensymphony.com/sitemesh/requirements.html/

웹서버에 요구된 정적/동적요청(request)를 가로채고, 
요청된 페이지 자원을 파싱하며 설정된 프로퍼티와 랜더링할 데이터를 컨덴츠로부터 읽어 들인 후원래의 페이지에 장식과 수정을 가하여 최종 페이지를 생성한다. 
디자인 패턴을 기반으로 만들어진 프레임워크이다.

SiteMesh 동작방식

SiteMesh 설치

  • SiteMesh 는 Filter 형태로 작용한다.
    이를 위해서 web.xml 에 다음과 같은 내용을 추가해야 한다.
    <!-- sitemesh  -->
     <filter>
            <filter-name>sitemesh</filter-name>
      <!--JSP를 위한 필터-->
            <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
     </filter>
      <!--velocity를 위한 필터 
      <filter-class>
          org.apache.struts2.sitemesh.VelocityPageFilter
      </filter-class>
      Freemarker를 위한필터
      <filter-class>
          org.apache.struts2.sitemesh.FreeMarkerPageFilter
      </filter-class>
      -->
     <filter-mapping>
            <filter-name>sitemesh</filter-name>
            <url-pattern>/*</url-pattern>
     </filter-mapping>

    *sitemesh.xml 파일설정
    decorators.xml경로와 이름을 정의한다.

  • decorators.xml 파일설정
    WEB-INF 에 decorators.xml 라는 이름의 파일을 만들고 다음 내용을 입력한다.
    애플리케이션의 각모듈 화면구성
    데코레이터가 적용될 url패턴
    데코레이터 페이지의 경로
    데코레이터를 적용하지 않을 url패턴
<?xml version="1.0" encoding="ISO-8859-1"?>

<decorators defaultdir="/chapter10/sitemesh/decorators">
    <!-- Any urls that are excluded will never be decorated by Sitemesh -->
    <excludes>
        <pattern>/debug.jsp</pattern>
        <pattern>/style/*</pattern>
        <pattern>/script/*</pattern>
        <pattern>/img/*</pattern>
        <pattern>/dojo/*</pattern>
        <pattern>/struts/*</pattern>
        <pattern>/exclude.jsp</pattern>
        <pattern>/exclude/*</pattern>
    </excludes>

    <decorator name="layout" page="layout.jsp">
        <pattern>/sitemesh*</pattern>
    </decorator>
    
    <decorator name="panel" page="layoutPanel.jsp"/>
    <decorator name="header" page="layoutHeader.jsp"/>
    <decorator name="footer" page="layoutFooter.jsp"/>    
</decorators>

*장식자 태그,페이지 태그

장식자 태그,페이지 태그장식자 페이지를 생성할 때 사용되는 태그
<decorator:head/>장식될 페이지의 <head>태그의 내용을 삽입
<decorator:body/>장식될 페이지의 <body>태그의 내용을 삽입
<decorator:title/>장식될 페이지의 <title>태그의 내용을 삽입
<decorator:getProperty/>장식이 완료된 HTML 페이지의 <body>태그 내에 이벤트 핸들러를 생성하기 위해 사용
<decorator:usePage/>장식자 페이지에서 장실될 페이지의 페이지 객체를 얻을 수 있게 함

*페이지 태그

페이지 태그장실자 페이지 내에서 다른 장식자를 포함할 때 사용
<page:applyDecorator/>현재 장식자 페이지 내에 장식될 페이지와 장식잘ㄹ 지정하여 삽입한다.
<page:param/><page:applyDecorator/> 사용시 해당 장식자에세 파라미터를 전달하기 위해 사용

FreeMarker 개념

공식 배포 사이트에서는 다음과 같이 FreeMarker를 정의하고 있다. 
"FreeMarker는 템플릿 엔진이며 템플릿을 사용하여 (어떠한 포맷이라도)텍스트를 출력하는 역할을 담당합니다. 자바 클래스 형태로 패키지로 묶어 배포하고 있으며 개발자를 위한 도구입니다"
다음 그림처럼 자바 객체에서 데이터를 생성해서 템플릿에 넣어주면, FreeMarker에서 템플릿에 맞게 변환하여 최종적으로 HTML 파일을 생성한다. 다른 언어 사용의 가능성을 배제하고 있지는 않지만, JVM에서 돌아가는 엔진이므로 주로 자바에서 사용되고 있다.

FreeMarker는 HTML 출력만을 위한 엔진은 아니고 텍스트라면 그 어떠한 것도 가능하다. 이는 텍스트에서 텍스트로의 변환이기 때문에 너무나도 당연한 얘기이다. 그렇기에 FreeMarker는 웹기반 프레임워크가 아니고 완전한 POJO기반 템플릿 엔진이다.

FreeMarker 설정

1. http://prdownloads.sourceforge.net/freemarker/freemarker-2.3.1pre.tar.gz 에서 파일 다운 
2.압축을 푼 후 freemarker.jar 파일을 WEB-INF/lib 폴더에 복사 
(예제)
template.html

<html>
<head><title>FreeMarker Test Program</title>
</head>
<body>
        ${name} 님의 방문을 환영 합니다~ <br>
        주 소 : ${info.address} <br>
</body>
</html>

template.jsp

<%@ page import = "java.util.*" pageEncoding="euc-kr" %>
<%@ page import = "java.io.*" %>
<%@ page import = "java.servlet.*" %>
<%@ page import = "java.servlet.http.*" %>
<%@ page import = "freemarker.template.*" %>

<%
        Configuration cfg = Configuration.getDefaultConfiguration();

        //현재 폴더에서 template file 로딩 하겠다는 의미
        cfg.setServletContextForTemplateLoading(getServletContext(), ".");

        //Template File Loading
        Template temp = cfg.getTemplate("template.html");

        //Create Data Model
        Map root = new HashMap();
        root.put("name","jclee");

        //계층 구조를 표시 하기 위해 HashMap에 HashMap을 추가
        //즉 템플릿에서 info.address 표시 가능
        Map info = new HashMap();
        root.put("info", info);
        info.put("address","서울 역삼동");

        //get OutStream
        Writer fout = new BufferedWriter(
                            new OutputStreamWriter(response.getOutputStream(), 
                                                temp.getEncoding()));

        //response.setContentType("text/html; charset=euc-kr");

        try {
            //Template File과 Data Model의 결합
                temp.process(root, fout);
        }
        catch(TemplateException e) {
                throw new ServletException("Error... freemarker processor");
        }

%>

freemarker문법

1. @macro

  • 프리마커 템플릿 전역에서 공통으로 사용되는 UI 디펜던트한 함수는 매크로로 만들어 여러 ftl에서 사용할 수 있도록 해준다.
  • 형식 : <@매크로명 변수1, 변수2, ... />
    - 샘플1) 긴 문자열을 적당한 크기로 자르는 기능의 매크로
       *사용법 : <@trimX ${item.title}, 20 />
       *매크로 : 
    
       <#macro trimX src max><#compress>
       <#if src?length &gt; max>
          ${src[0..max-1]}..
       <#else>
          ${src}
        </#if>
       </#compress></#macro>
     - 샘플2) YYYYMMDD 형식의 문자열을 YYYY.MM.DD 형식으로 변환하는 매크로
       *사용법 : <@parseDay ${item.regdate} />
       *매크로 : 
    
       <#macro parseDay src><#compress>
       <#if src?length == 8>
            ${src[0..3]}.${src[4..5]?number}.${src[6..7]?number}
       <#else>
          ${src}
        </#if>
       </#compress></#macro>

    2. #list

  • 배열 형식의 오브젝트를 루핑 처리할때 사용하는 프리마커 지시자이다. "로컬엘리어스_index" 라는 변수는 0부터 시작하는 시퀀스번호이다.
  • 형식 : <#list 배열객체 as 로컬엘리어스명></#list>
    - 샘플1)
    
       <#list LIST as item>
          번호 : ${item_index+1} | 이름 : ${item.name} | 아이디 : ${item.id}
       </#list>

    3. #if

  • 프리마커 조건문에 사용되는 지시자이다.
  • 형식 : <#if 조건식></#if>
    - 샘플1) string 비교
    
       <#if ENTITY.usergrade == "A" >......</#if>
    
     - 샘플2) number 비교
    
       <#if ENTITY.userclass?number == 3>.....</#if>
    
     - 샘플3) boolean 비교
    
       <#if ENTITY.isAuth()>.....</#if>

    4. #break

  • Loop문을 중단하고 다음 스크립트로 넘어가려고 할때 사용되는 지시자이다.
  • 형식 : <#break>
    - 샘플1) 루프문을 실행하는 중 5번째에서 escape 하는 예
    
     <#list LIST as item>
      <#if item_index &gt; 3><#break></#if>
     </#list>

    5. #assign

  • 프리마커내에서 사용자 정의 로컬변수가 필요할 때 사용하는 지시자이다.
  • 형식 : <#assign 로컬변수명 = 초기화값>
    - 샘플1) 
    
    <#assign CHECK = item_index>

    6. [x...y]

  • 문자열의 일정 범위를 자를때 사용하는 함수
  • 형식 : $
    Unknown macro: {문자열[1..5]}
    - 샘플1)
    
     ${item.name[1..5]}

    7. ?has_content

  • 리스트형 오브젝트가 null이 아니고 최소 1개 이상의 컨텐츠를 가지고 있는지 체크하는함수로써 ?has_content는 ?exists와 ?size>0 두가지 체크를 동시에 해주는 함수이다.
  • 형식 : 리스트오브젝트?has_content
    - 샘플1) 
    
    <#if LIST?has_content>.....</#if>

    8. ?exists

  • NULL체크 함수. if_exists는 <#if 지시자 없이도 사용할 수 있게 해주는 표현식이다.
  • 형식 : 오브젝트?exists
    - 샘플1) 
    
    <#if ENTITY.username?exists>${ENTITY.username?substring(0, 5)}</#if>
    
     - 샘플2) 
    
    <#if LIST?exists && LIST?size &gt; 0>.....</#if>
    
     - 샘플3)
     ${ENTITY.username?if_exists}

    9. ?default

  • NULL값을 대체해주는 함수
  • 형식 : 오브젝트?default(디폴트값)
    - 샘플1) ${item.userclass?default("99")}
     - 샘플2) ${item.age?default(20)}

    10. ?string

  • 문자열로 형변환하는 함수
  • 형식 : 오브젝트?string
    - 샘플1) <#if item.age?string == "29">.....</#if>
     - 샘플2) ${item.regdate?string("yyyy/MM/dd HH:mm")}
     - 샘플3) 숫자를 통화표시로 나타내는 예
      <#assign MONEY = 1234567>
      ${MONEY?string(",##0")}

    11. ?number

  • 숫자로 형변환하는 함수
  • 형식 : 오브젝트?number
    - 샘플1) <#if item.userclass?number &gt; 3>.....</#if>
     - 샘플2) ${LIST_POINTS[item.gid?number].entityname?default("")}

    12. ?js_string

  • 문자열을 자바스크립트에 유효하도록 필터링해주는 함수.
    문자열내에 싱글쿼테이션(')등이 포함되어 스크립트에 오류가 나는것을 방지하기 위하여 사용되는 함수이다. 
    화면상에는 HTML 태그로 취급된다.
  • 형식 : 오브젝트?js_string
    - 샘플1) 문자열 <img src='/image/enterprise.gif'>을 js_string으로 처리했을때 소스보기를 하면 <img src=\'/image/enterprise.gif\'>으로 출력된다.
     - 샘플2) <a href="javascript:getName('${item.homeurl?js_string}');">

    13. ?html

  • 문자열을 HTML Symbolic Entity로 필터링해주는 함수. 문자열내의 HTML태그등을 깨뜨려 화면상으로 출력되도록 할때 사용하는 함수이다. 화면상에 HTML태그가 아닌 일반 문자열로 취급된다.
  • 형식 : 오브젝트?html
  • 샘플1)
    문자열 <img src='/image/enterprise.gif'>을 html로 처리하면 화면상에 <img src='/image/enterprise.gif'> 로 출력되고 소스보기를 하면 <img src='/image/enterprise.gif'>로 출력된다.

14. ?index_of

  • 특정 문자(열)가 시작되는 위치를 정수형으로 반환한다. 인덱스는 0부터 시작됨.
  • 형식 : 오브젝트?index_of(특정문자)
    - 샘플1) "abcde"?index_of("c") 는 2를 반환한다.

    15. ?replace

  • 문자열의 일부를 주어진 문자로 대체하는 함수
  • 형식 : 오브젝트?replace(찾을문자열, 대체할문자열)
    - 샘플1) ${item.content?replace(">", "&gt;")}

    16. item_has_next
    -리스트 객체의 다음 컨텐츠가 존재하는지(EOF) 체크하는 함수
    -형식 : 리스트엘리어스이름_has_next
    -샘플1) 이름과 이름사이에 , 를 찍어주되 마지막은 찍지 않는 경우의 예

    <#list LIST as item>
          ${item.name?default("")}<#if item_has_next>,</#if>
      </#list>

참고자료

이미지 출처: http://opensymphony.com/sitemesh
스트럿츠 2 프로그래밍

문서에 대하여

'Language > JSP' 카테고리의 다른 글

mybatis[SqlSession]  (0) 2012.11.07
springmvc+mybatis  (0) 2012.11.07
Core of JSP  (0) 2012.11.02
Basic of JSP  (0) 2012.11.02
ServletContext와 RequestDispatcher  (0) 2012.11.02
:

Core of JSP

Language/JSP 2012. 11. 2. 11:00

01. JSP에서 사용하는 스크립팅 요소들

- 스크립트형태의 프로그램을 지원하기 위해서 사용하는 태그는 아래와 같다.

스크립트릿 : <% %>  -------> 역할 : _jspService에 프로그램 추가

표현식 : <%= %>       ------>  역할 : _jspService에 프로그램 추가 (out.print의 간략화)

선언문 : <%! %>       ------->  역할 : .java파일에 멤버필드와 멤버메서드 추가

지시문 : <%@ %>     ------->  역할 : .java파일에 여러가지 속성 정하기(지시문의 형태에 따라 .java파일의 적당한 장소에 추가)

 

02. _jspService내의 지역변수와 내정객체들

- _jspService의 구성

* 매개변수 2개

  1. HttpServletRequest request

  2. HttpServletResponse response

* 필요한 지역변수

  1. PageContext pageContext = null

  2. HttpSession session = null;

  3. ServletContext application = null;

  4. ServletConfig config = null;

  5. JspWriter out = null;

  6. Object page = this;

  7. JspFactory _jspxFactory = null;

  8. String _value = null;

* 지역변수 초기화

  1. _jspxFactory = JspFactory.getDefaultFactory();

  2. pageContext = _jspxFactory.getPageContext(this, request, response, "", true, 8192, true);

  3. application = pageContext.getServletContext();

  4. config = pageContext.getServletConfig();

  5. session = pageContext.getSession();

  6. out = pageContext.getOut();

 

- 단순히 _jspService메서드내의 지역변수들이지만 이 지역변수들은 아주 조직적으로 만들어지고 초기화

기본적으로 _jspService의 매개변수로 request와 response가 넘어오게 된다. 이것은 서블릿에서와 같은 원리로 넘어온다.

<<서블릿 : request와 response는 service메서드의 매개변수로 넘어오게 되며, 이것은 다시 클라이언트의 요청 방식에 따라서

doGet과 doPost의 매개변수로 넘어가게된다.

JSP : 위의 서블릿과 마찬가지로 _jspService의 매개변수로 서블릿 컨테이너에서 request와 response을 넘겨주게 된다.>>

그리고, 제일 먼저 초기화 되는 변수는 _jspxFactory라는 지역변수..

 

_jspxFactory객체는 JspFactory클래스의 static 메서드인 getDefaultFactory를 이용하여 생성하며, 이렇게 생성된

_jspxFactory 객체를 사용하여 다른 내장객체를 생성하는 pageContext를 생성한다. 이 pageContext를 사용하여 다른 내장객체

들을 생성하고, 사용하는 것임.

 

<< 결과적으로 _jspxFactory객체가 다른 내장객체들을 생성하는 것임. 하지만 JspFactory의 객체인 _jspxFactory는 JSP작성자

가 직접 사용하지는 않고 내부적으로 사용되며, JSP가 Servlet으로 변환 될 때 자동으로 실행되는 객체이다. >>

 

참고---> _jspService내에서 사용되는 내장객체의 구성에 대한 설명은 소설같은 JSP를 참조..

 

03. Servlet 과 JSP의 HttpServletRequest 와 HttpServletResponse의 비교

- 서블릿의 service() 메서드의 매개변수와 JSP의 _jspService() 메서드의 매개변수는 동일한 방식으로 JSP와 서블릿으로

전달된다. Servlet과 JSP의 서비스되는 메서드들의 원형 비교.

* Servlet

 - service(ServletRequest request, ServletResponse response)

 - doGet(HttpServletRequest request, HttpServletResponse response)

 - doPost(HttpServletRequest request, HttpServletResponse response)

 

* JSP

 - _jspService(HttpServletRequest request, HttpServletResponse response)

 

결론적으로 Servlet과 JSP 에서 HttpServletRequest request , HttpServletResponse response 의 역할은 모두가 같다고 말할수 있다.


출처 - http://cafe.naver.com/jspstudy1103/156

'Language > JSP' 카테고리의 다른 글

springmvc+mybatis  (0) 2012.11.07
SiteMesh와 Freemarker  (0) 2012.11.02
Basic of JSP  (0) 2012.11.02
ServletContext와 RequestDispatcher  (0) 2012.11.02
스프링 MVC - 뷰 영역 구현  (0) 2012.10.31
:

Basic of JSP

Language/JSP 2012. 11. 2. 11:00

1. JSP의 개요
- JSP는 HTML로만 이루어진 정적인 웹 페이지의 단점을 보완하여
동적인 웹 페이지를 보다 효울적으로 만들어 줄 수 있는 Server Side Script이다.
JSP내부에서 자바를 그대로 사용할 수 있기 때문에 더욱 효울적이며 스크립트방식으로
프로그램 가능하기 때문에 편리성과 확장성이 매우 뛰어나다.

 

정적 페이지의 특징(HTML)
- HTML은 정적인 문서. 이미 만들어져 있는 문서
- 클라이언트에서 서버 측에 문서를 요구할 때 서버는 클라이언트에게 이미
작성되어진 문서를 그대로 서비스한다.

 

동적 페이지의 특징(JSP)
- 원하는 정보를 클라이언트가 요청할 때 서버에서 실시간으로 작업을 처리하여
보다 동적으로 클라이언트에게 서비스한다.
- 서버에서는 데이터베이스 작업이나 클라이언트가 원하는 작업을 대신 처리하고
그 결과만을 클라이언트에게 서비스한다.


참고>> 
동적인 페이지는 서버에서 처리하는 메커니즘을 두게 되는데 이때 이용되는 프로그램이
JSP이다. 왜 JSP가 동적인 웹 페이지를 만드는데 효율적인가?


- JSP는 자바를 서버환경에서 사용하는 스크립트 방식의 언어
(즉 서버 페이지내에 자바를 그대로 가져다 사용할수 있다는 것)
- JSP는 사용하는 언어가 자바로 되어 있기 때문에, 자바라는 언어는 어떤 플랫폼이나
어떤 웹 서버에서도 사용가능
- JSP는 단일 스레드로 클라이언트의 요청에 서비스한다.
 요청이 있을때마다 프로세스를 생성하는 기존의 CGI와는 달리 하나의 메모리를
 공유하면서 서비스되는 원리이며 서버 측에 부하를 줄여준다.
- JSP는 보여주는 부분과 작업 부분을 분리 할수 있음(빈과 스크립트)

 

2. JSP의 동작
- JSP가 실행되기 위해서는 JSP스크립트가 포함되어진 JSP파일을 Servlet파일로 변환하는
과정을 거침.  즉, Servlet파일은 .java형태로 되어 있으며 자바 파일을 실시간에
컴파일 하게 된다. 그리고 난 다음 Servlet컨테이너는 해당 클래스를 이용하여 객체를 생성,
로딩하게 된다. 이렇게 로딩된 객체는 서비스를 시작할수 있게 된다.

--> JABOOK > 그림 참조.

 

3. JSP 내부의 주기
- JSP변환 서블릿 프로그램은 jspInit(), _jspService(), jspDestory() 세가지 메서드에
의해서 초기화, 서비스, 파괴의 과정을 거친다. 
jspInit 메서드는 단 한번 호출한 후 _jspService 메서드를 서비스 요청이 있을때마다 호출하게 된다.
서블릿 객체가 더 이상 서비스를 하지 않고 있는 경우 메모리에서 제거 되며 이때 호출되어지는
메서드는 jspDestroy메서드이다. 
jspDestroy메서드가 호출되면 가비지 콜렉터는 객체의 메모리를 제거하게 된다.

--> JABOOK > JSP 내부의 주기 (그림 참조)

 

4-5. JSP 에서 생성되는 자바파일 및 자바파일의 구조
- JSP가 실행되면 자바(.java)와 클래스(.class)로 변환되어 그에 따라 페이지를 동적으로 생성.
즉, 미리 준비된 문서가 아닌 새로운 문서를 만들어서 보여준다는 것

참고> jsp 의 실행증거를 테스트하기 위한 예제 - (Hello_jsp.java, Hello_jsp.class) 

톰캣에서 실행했을 경우
C:\Tomcat6\work\Catalina\localhost\Jabook3\org\apache\jsp\hello 대략 이러한 경로에
파일이 생성되어집니다.

- Hello_jsp 클래스는 HttpJspBase를 상속받는다.

- 자북에서 말한 hello$jsp 생성자/_jspx_init메서드 비어있다???  <--- 실제 컴파일했을때 조금 다름...(존재하지 않음)
내가 컴파일 한것은 Hello_jsp 생성자는 없고, _jspx_init 도 없다.
단지 _jspInit()만 있을 뿐이다.

 

public void _jspInit() {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
}

- _jspService(HttpServletRequest request, HttpServletResponse response)

이안에 jsp파일 내에 직접 작성한 내용들이 out객체에 의해 기록되어 있음


* Servlet은 HttpServlet을 상속 받고 init, service(doGet, doPost), destory를 호출
그리고 JSP는 HttpJspPage로부터 상속을 받고 _jsp_init, _jspService, _jspDestroy를 사용
분명 같은 성질을 가지고 있다는 것을 알수가 있음.

 

6. _jspService 메서드의 구조
** _jspService 의 구성 **
매개변수 2개
1. HttpServletRequest request
2. HttpServletResponse response

필요한 지역변수
1. PageContext pageContext = null;
2. HttpSession session = null;
3. ServletContext application = null;
4. ServletConfig config = null;
5. JspWriter out = null;
6. Object page = this;
7. JspFactory _jspxFactory = null;
8. String _value = null;

--------- 조금 다른부분도 존재 ----------

* 실제 톰캣에서 컴파일 된 _jspService부분
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
------------------------------------------


지역변수 초기화
1. _jspxFactory = JspFactory.getDefaultFactory();
2. pageContext = _jspxFactory.getPageContext(this, request, response, "", true, 8192, true);
3. application = pageContext.getServletContext();
4. config = pageContext.getServletConfig();
5. session = pageContext.getSession();
6. out = pageContext.getOut();

--------- 조금 다른부분도 존재 ----------

* 실제 톰캣에서 컴파일 된 _jspService부분
pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
------------------------------------------

 

위의 초기화 된 변수와 매개변수들을 내장객체라고 한다.

_jspService 메서드 내용
1. 우선 IOException, ServletException이 선언
2. 메서드 내부에서 지역 변수를 선언
3. 이 선언된 객체변수를 초기화 한 후 _jspService 메서드 내에서 사용

참고) <% %>태그와 _jspService의 지역변수
- ** jsp문서의 <% %>태그 안에 들어있는 부분은 _jspService메서드안에  삽입 **
- ** <% %> 내에서 _jspService 메서드 내의 지역변수(내장객체)를 모두 사용가능 **

 

*** JSP 내장객체 ***
ServletRequest request(HttpServletRequest request) : 클라이언트의 http요청을 담고 있는 객체
ServletResponse response(HttpServletResponse response) : 클라이언트로 응답을 전송할 객체
PageContext pageContext : 다른 내장객체를 얻거나, 요청을 처리할 제어권을 다른 페이지로
위임하는 객체
HttpSession session : 클라이언트와 서버와의 세션데이터를 가지고 있는 객체
ServletContext application : Web application이 실행되는 실행 환경에 대한 정보를 담고 있는 객체
JspWriter out : Servlet이 요청을 처리하여 응답을 전송할 때 전송할 응답에 대한 출력 스트림 객체
ServletConfig config : Servlet 객체가 참조하게 될 초기 설정 데이터에 대한 정보를 담고 있는 객체
Object page(HttpJspPage) : Servlet 객체를 참조하는 레퍼런스
Throwable exception : 예외가 발생할 경우 에러 페이지에 전달되는 객체


7. JSP에서 멤버변수와 멤버메서드
질문? jsp파일에 코딩 한 부분은 전부 _jspService 메서드 안으로 들어갈까? 대답: No!!!
- jsp는 실제 실행 될 때 하나의 클래스로 이루어진다. 그 구조는 jspInit, _jspService, jspDestroy
3개의 멤버 메서드로 되어있다. 그리고 <% %> 내의 모든 구문과 html코드들은 전부 _jspService로 
들어가지만 클래스의 멤버 메서드와 멤버변수차원에서 클래스내에 삽입할수 있다.
이것을 지원하는 것이 <%! %> 태그이다. 이것을 선언문이라고 부른다.


* jsp에서 클래스의 멤버변수와 멤버메서드를 삽입할 때 사용하는 구문
- <%! %> --> 선언문
- 멤버변수, 멤버메서드를 삽입할 때 사용

- 첨부된 Hello_sum.java를 확인해보변 <%! %>태그에 다른 멤버변수와 메소드가 클래스 내에

   삽입되어 있는 것을 확인할수가 있다.

* <%! %> 선언문 태그
- <%! %>를 사용해서 java파일 내의 멤버메서드와 멤버변수를 사용할수 있다.
- <% %>스크립트릿 내에서 멤버변수와 멤버메서드는 자유로이 사용할수 있다.
- <% %>스크립트릿 안에서 사용하는 out객체, 즉 내장객체는 _jspService내의 지역변수이기
때문에 <%! %>선언문 내에 사용할 수 없다.


8. JSP에서 라이브러리 import
- JSP에서 멤버변수와 멤버메서드를 사용하는 법을 알았다면, 한발 더 나아가서 
import 하는것도 가능할것이다.
jsp에서 import는 <%@ %> 지시문이라는 태그를 사용한다.

예> JSP에서의 내용이 JAVA의 내용으로 변경된다.
JSP
<%@ page language="java" contentType="text/html; charset=euc-kr"%>
<%@ page import="java.util.Vector"%>

JAVA
response.setContentType("text/html; charset=euc-kr");
import java.util.Vector;

- <%@ %>는 클라이언트로 전송되는 응답에 직접적인 영향을 미치기 보다는 JSP
컨테이너에 페이지정보를 전달하는 데 사용된다. 특히 그 중 많이 사용되는 것이
<%@ page %>인데 이것은 JSP페이지의 속성을 지정을 하는 역할을 한다.

 

--------------------------------------------------------------------------------------
<%@ page %> 속성
language : jsp파일에서 사용할 스크립트 언어를 지정하는 속성
extends  : jsp파일에서 Servlet으로 변환 시 상속할 클래스를 지정하는 속성
import   : jsp파일에서 Servlet으로 변환 시 import 할 패키지를 지정하는 속성
session  : session을 유지할 것인지 아닌지를 결정하는 속성
buffer   : out객체를 사용 할 때 buffer를 얼마의 크기로 사용할 것인지 아닌지의 속성
autoFlush: buffer가 다 채워졌을 경우의 속성
isThreadSafe : 다중 클라이언트 요청을 처리하는 속성
info : 해당 페이지의 기능과 특징에 대한 설명을 지정하는 속성
errorPage : 에러 발생시 에러 페이지를 알려주는 속성
isErrorPage : 해당 페이지가 다른 페이지에서 발생한 예외를 처리할 페이지임을 알려주는 속성
contentType : 해당 jsp파일이 클라이언트로 전송할 응답의 MINE형식을 지정하는 속성
pageEncoding: jsp에서 Encoding시 사용할 character encoding 방식을 지정하는 속성
--------------------------------------------------------------------------------------

 

9. JSP에서 Bean 사용하기
- Beans는 일종의 특정한 일을 독립적으로 수행하는 컴포넌트.
 * 빈을 사용하기 위한 규칙
 1. 빈클래스는 agument가 없는 생성자메서드를 가져야한다.
 2. 멤버변수는 public 이어서는 안된다.
 3. 변수에 대한 접근은 setXxxx()메서드나 getXxxx()메서드를 통해서만 가능하다.
    (예외로 Boolean형태의 리턴값을 갖는 메서드는 isXxxx()의 형태를 가짐)
 4. setXxxx()메서드, getXxxx()메서드는 항상 public이어야 한다.

참고> jsp에서 클래스를 만들고 import만 잘 시켜준다면 어디에서 불러 사용해도 상관없다.
페이지 지시어를 통해서 jsp안에 import한 후 객체를 생성하듯이 사용하면 된다.
ex) <%@ page import="hello.HelloWorld"%>


10. JSP에서 jspBean태그를 이용하여 빈 사용하기
- JSP에서 객체를 만드는 방법은 두 가지가 있다.
* JSP에서 객체를 만드는 방법
1. 빈을 이용하는 방법
2. new 연산자를 사용하여 객체를 사용하는 방법

JSP액션태그 중의 하나인 userBean 액션태그를 통해서 빈을 로드하는 방법
useBean태그는 새로운 객체를 생성하거나 이미 생성된 객체의 참조를 얻는데 사용

 

참고> 액션태그를 이용하는 방법과 일반 객체를 생성하는 방법은 같다.
<jsp:useBean id="myhello" class="hello.HelloWorld" scope="request"/>
HelloWorld myhello = new Helloworld();

- 실행을 해보면 new를 통해 자바빈즈를 출력한 것과 별반 차이가 없는 것이 확인가능하다.
JSP코드를 보면 더 간결해진것을 확인할수가 있다.
- useBean --> id: 객체인스턴스를 식별한 이름
class: 패키지이름까지 모두 기술한 클래스의 이름
scope: 빈이 존재할 범위 (scope가 명시되지 않았다면 디폴트값으로 page내에서 빈의 유효성을 가짐)

 

11. JSP 빈태그를 이용한 매개변수 값의 자동셋팅.
- 빈즈를 사용하기 매개변수 값을 자동으로 셋팅을 위해 두가지 태그를 더 사용
 1. jsp:setProperty - 액션태그는 변수를 변경시키는 역할.
 2. jsp:getProperty - 액션태그는 빈의 변수에 접근하기 위해서 사용.


* setProperty와 getProperty를 사용하기 위해서는 useBean이 먼저 기술해야 함.


출처 - http://cafe.naver.com/jspstudy1103/125

'Language > JSP' 카테고리의 다른 글

SiteMesh와 Freemarker  (0) 2012.11.02
Core of JSP  (0) 2012.11.02
ServletContext와 RequestDispatcher  (0) 2012.11.02
스프링 MVC - 뷰 영역 구현  (0) 2012.10.31
web.xml (servlet)  (0) 2012.10.31
:

ServletContext와 RequestDispatcher

Language/JSP 2012. 11. 2. 10:13

어떠한 서블릿 또는 JSP를 만들었다고 합시다. 
그 서블릿은 어떠한 클래스 일까요? 아마도 HttpServlet을 상속받는 어떠한? 클래스 일테죠. 
그 클래스를 보면 아마도 실제 요청을 서비스하는 doGet과 doPost등이 정의되어 있을것이고, init와 destroy역시 준비되어 있겠죠. 
그래서 컨테이너는 HttpServlet의 인스턴스를 생성하고, init를 실행해주고, 그녀석으로 매핑된 URL에 doGet이냐 doPost냐에 따라서 죽어라고 그 메서드만 실행해 주다가 
컨테이너가 종료될때 destroy를 호출해주고 죽겠죠. 
결국 HttpServlet은 아마도(가 아니고 확실히) 하나의 인스턴스만 생성되어서 멀티 스레딩으로 돌아갈것이라고 판단할 수 있겠습니다. 

그러면 이제 ServletContext를 보시죠. HttpServlet의 상속된 클래스의 인스턴스, 즉 서블릿은 스레드 안전하게 설계가 되어야 하고, 따라서 스레드 안전하게 설계되지 않은 경우에 상위 클래스를 마구 접근하다보면 에러가 나겠죠. 그래서 환경설정이나 J2EE서비스에 관한 내용은 ServletContext에서 할수 있게 됩니다. 
이 ServletContext는 getServletContext로만 받을 수 있고, 이녀석은 아마도 동기화가 제대로 구현되어 있는 녀석일겁니다. 
그래서 우리사 ServletContext의 setAttribute와 getAttribute를 마음대로 읽고 써도, 스레드 걱정은 하지 않고 마음대로 읽고 쓸 수 있는겁니다. 
또한 ServletContext의 역할은 여기에서만 그치지 않고, 다른 서블릿 인스턴스를 가져올수 있다거나, 서블릿 환경설정값을 가져올수 있는 기능들을 가지고 있습니다. 
RequestDispatcher 역시 그 기능중의 하나이구요. 

자, 그럼 RequestDispatcher에 대해서 알아 봅시다. 
RequestDispatcher는 디스패치해주는 클래스입니다.(사실 인터페이스 입니다. 정정하자면 HttpServlet역시 ServletContext역시 모두 인터페이스이고 톰캣이나 JBoss같은 녀석이 그 인터페이스를 구현한 클래스를 제공해줍니다.;;;) 

Dispatcher를 사전에서 검색하면 발송자...라는 뜻이 있다고 하네요. 
즉 RequestDispatcher는 요청을 보내주는 클래스라고 이해할 수 있겠습니다. 
따라서 현재의 요청을 다른 서블릿(궁극적으로는 JSP도 서블릿의 하나입니다.)으로 보내야하는데... 
아까 전에 설명드렸죠? 서블릿의 인스턴스는 하나만 생성되고, 이것이 
멀티스레딩으로 돌아간다는 사실. 
그래서 새로운 서블릿을 생성해서 그 서블릿을 실행하는것만으로는 안되고, 
이미 돌아가는 서블릿 인스턴스의 스레드를 하나 더 추가해야 한다는 겁니다. 
그리고 이것은 서블릿 개발자가 해야할 일을 넘었기 때문에 Dispatcher가 대신해준다는거죠. 
하지만 Dispatcher는 HttpServletRequest, HttpServletResponse를 생성해주지는 않습니다. 
그렇기 때문에 Dispatcher가 만들어준 새로운 서블릿 스레드가 있는데 
이 녀석을 실행시키려면 doGet이나 doPost를 실행시켜야 겠죠? 
그래서! 결론으로 dispatch메서드에 
HttpServletRequest, HttpServletResponse를 넘여주는 겁니다.


'Language > JSP' 카테고리의 다른 글

Core of JSP  (0) 2012.11.02
Basic of JSP  (0) 2012.11.02
스프링 MVC - 뷰 영역 구현  (0) 2012.10.31
web.xml (servlet)  (0) 2012.10.31
Spring MVC  (0) 2012.10.31
:

스프링 MVC - 뷰 영역 구현

Language/JSP 2012. 10. 31. 17:50

스프링 MVC - 뷰 영역 구현

1. ViewResolver 설정


  □ 스프링 컨트롤러의 특징
    - 뷰에 의존적이지 않음.
    - 결과를 생성할 뷰의 이름만 지정.
    - 컨트롤러가 지정한 뷰 이름으로부터 응답 결과 화면을 생성하는 View 객체는 ViewResolver가 구함.

 public class HelloController extends AbstractController {
    ...
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
        HttpServletResponse response) throws Exception {

        ModelAndView mav = new ModelAndView();
        // 컨트롤러는 뷰의 이름만 지정.
        mav.setViewName("hello");
        ...
        return mav;
    }
 }

  □ 스프링이 제공하는 주요 ViewResolver 구현 클래스

  □ ViewResolver 인터페이스
    ■ 정의
 package org.springframework.web.servlet;

 import java.util.Locale;

 public interface ViewResolver {
    View resolveViewName(String viewName, Locale locale) throws Exception;
 }
    ■ 특징
      - 뷰 이름과 지역화를 위한 Locale을 파라미터로 전달 받음.
      - 매핑되는 View 객체를 리턴.
      - 매핑되는 View 객체가 존재하지 않으면 null을 리턴.

  □ View 객체
    ■ View 객체
      - 응답 결과를 생성.
      - 모든 뷰 클래스는 View 인터페이스를 구현.
    ■ View 인터페이스
 package org.springframework.web.servlet;
 
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;

 public interface View {
    String getContentType();
    void render(Map model, HttpServletRequest request, HttpServletResponse response)
        throws Exception;
 }
      ○ getContentType() 메서드
        - "text/html"과 같은 응답 결과의 커텐트 타입을 리턴.
      ○ render() 메서드
        - 실제로 응답 결과를 생성.
        - 첫 번째 파라미터인 model에는 컨트롤러가 리턴한 ModelAndView 객체의 모델 데이터가 전달됨.
        - 각각의 View 객체는 이 모델 데이터로부터 응답 결과를 생성하는데 필요한 정보를 구함.


  □ InternalResourceViewResolver 설정
    ■ InternalResourceViewResolver 클래스
      - JSP 나 HTML 파일과 같이 웹 어플리케이션의 내부 자원을 이용하여 뷰를 생성하는 AbstractUrlBasedView 타입의 뷰 객체를

        리턴.
      - 기본적으로 사용하는 View 클래스는 InternalResourceView 클래스. (지정한 자원 경로로 요청을 전달.)
    ■ 설정

 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:prefix="/WEB-INF/viewjsp/" p:suffix=".jsp" />
      - 컨트롤러가 지정한 뷰 이름으로 부터 실제로 사용될 뷰를 선택.
      - 컨트롤러가 지정한 뷰 이름 앞뒤로 prefix 프로퍼티와 suffix 프로퍼티를 붙인 값이 실제로 사용될 자원의 경로가 됨.
    ■ 예
      ○ 컨트롤러에서 뷰 이름 지정
 ModelAndView mav = new ModelAndView("hello");
 return mav;
      ○ InternalResourceViewResolver가 사용하는 자원 경로


    ■ 스프링의 국제화와 관련 정보를 JSTL에서 사용
      - JstlView 클래스를 viewClass 프로퍼티로 지정.
 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:viewClass="org.springframework.web.servlet.view.JstlView"
    p:prefix="/WEB-INF/viewjsp/" p:suffix=".jsp" />

  □ BeanNameViewResolver 설정
    ■ BeanNameViewResolver 클래스
      - 뷰 이름과 동일한 이름을 갖는 빈을 뷰 객체로 사용.
      - 주로 커스텀 View 클래스를 뷰로 사용해야하는 경우에 사용.
    ■ 예
      - 파일 다운로드를 위한 정보를 읽어와 뷰에 전달하는 컨트롤러.
      - 다운로드 관련 정보를 뷰에 전달.
      ○ 컨트롤러 생성
 public class DownloadController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
        throws Exception {

        File downloadFile = getFile(request);
  
        return new ModelAndView("download", "downloadFile", downloadFile);
    }
    ...
 }
      ○ 빈 설정
 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.BeanNameViewResolver" />

 <bean id="download" class="keme.spring.chap05.view.DownloadView" />
         - 결과를 보여주는 View 클래스는 이름이 "download"인 DownloadView 클래스.
         - ViewResolver로서 BeanNameViewResolver 클래스를 사용.
         - DownloadController 클래스의 처리 결과를 DownloadView가 생성.

  □ XmlViewResolver 설정
    ■ XmlViewResolver 클래스
      - BeanNameViewResolver와 마찬가지로  뷰 이름과 동일한 이름을 갖는 빈을 뷰 객체로 사용.
      - 차이점 : 별도의 XML 설정 파일로부터 빈 객체를 검색.
    ■ 설정
      - location 프로퍼티를 이용하여 외부 설정 파일의 경로를 지정.
 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.XmlViewResolver"
    p:location="/WEB/INF/nonHtml-view.xml" />
      - location 프로퍼티의 값을 지정하지 않을 경우 기본값은 "/WEB-INF/views.xml"
      - location 프로퍼티에 지정하는 파일은 스프링 XML 설정 파일.


  □ ResourceBundleViewResolver 설정
    ■ ResourceBundleViewResolver 클래스
      - 리소스 번들(프로퍼티 파일)로부터 뷰 이름과 매핑되는 View 클래스를 구함.
    ■ 설정
      - ResourceBundleMessageSource와 마찬가지로 basename 프로퍼티나 basenames 프로퍼티를 이용해서 뷰 클래스 정보를 저장

        한 리소스 경로를 명시.

 <bean id="viewResolver"
    class="org.springframework.web.servlet.view.ResourceBundleViewResolver"
    p:basename="views/views" />
         - "views/views" 리소스로부터 설정 정보를 로딩.
         - "views/views.properties", "views.views_en.properties" 등 Locale에 따라 알맞은 프로퍼티 파일로부터 뷰 매핑 정보를 로딩.
    ■ 프로퍼티 파일
      - '뷰이름.class=뷰클래스' 형태로 뷰 이름과 뷰 클래스 사이의 매핑을 입력.
 download.class=kame.spring.chap05.view.DownloadView

  □ 다수의 ViewResolver 설정
    - 하나의 DispatcherServlet은 한 개 이상의 ViewResolver를 설정 가능.
    ■ 다수의 ViewResolver를 설정한 경우
      - order 프로퍼티를 이용하여 뷰 이름을 검사할 ViewResolver의 순서를 결정.
      - order 프로퍼티의 값이 작은 ViewResolver가 높은 우선순위를 가짐.
      - 우선순위를 명시하지 않으면 Integer.MAX_VALUE 를 order 프로퍼티의 값으로 가짐.
    ■ 특징
      - 우선순위가 높은 ViewResolver가 null을 리턴하면 그 다음 우선순위를 갖는 ViewResolver에 뷰를 요청.
      - 뷰 객체를 구하면 뷰 객체를 이용하여 응답 결과를 생성.
    ■ 설정
 <bean class="org.springframework.web.servlet.view.XmlViewResolver"
    p:location="/WEB-INF/nonHtml-view.xml"
    p:order="0" />

 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:prefix="/WEB-INF/viewjsp/" p:suffix=".jsp"
    p:order="1" />
    ■ 우선순위 결정 시 주의할 점
      - InternalResourceViewResolver는 항방 뷰 이름에 매핑되는 뷰 객체를 리턴(null을 리턴하지 않음.)하므로 마지막 순위를 갖도록
        지정.
      - VelocityViewResolver와 VelocityLayoutViewResolver의 경우 둘 다 뷰 이름에 매핑되는 Velocity 템플릿 파일이 존재하지 않을
        경우 null을 리턴하지 않고 예외를 발생 시킴.
        (VelocityViewResolver 역시 가장 낮은 우선순위를 갖도록 해야함.)

'Language > JSP' 카테고리의 다른 글

Basic of JSP  (0) 2012.11.02
ServletContext와 RequestDispatcher  (0) 2012.11.02
web.xml (servlet)  (0) 2012.10.31
Spring MVC  (0) 2012.10.31
MyBatis-Spring  (0) 2012.10.31
:

web.xml (servlet)

Language/JSP 2012. 10. 31. 17:32

프링 MVC 를 사용하려면, 가장 먼저 해야 할일이 웹어플리케이션 설정 파일인 web.xml 을 설정하는 것으로 부터 출발한다.

우선 개략적으로 흐름을 살펴보자.

1. 클라이언트로 부터 요청이 들어오면, 이는 DispatcherServlet 으로 전달되고,

2. 이 DispatcherServlet 은 HandlerMapping 을 사용하여 적당한 Controller 객체를 구하게된다.

3. 다시 DispatcherServlet 은 Cotroller 객체의 메소드를 호출하여 요청을 처리한다음에,

4. Cotroller 객체의 메소드가 리턴한 결과를 담은 ModelAndView 객체를 보내주면,

5. 다시 DispatcherServlet 은 ViewResolver로 부터 결과를 만들 View 객체를 구한다.

6. View 객체는 최종 응답을 생성하고.

여기서 우리는 Controller의 개발, View 객체 정도 이다.

이제 본론으로 들어가면,

DispatcherServlet 이 요청을 처리하게 하려면, 이 서블릿의 존재를 웹 어플리케이션에 설정해 주어야지.

web.xml 에서 해당 서블릿 정보를 추가해주자.

....

<servlet>
<servlet-name>goDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>


<servlet-mapping>
<servlet-name>goDispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

....

이렇게 해놓으면,

WEB-INF/web.xml

/goDispatcher-servlet.xml <-- 이 설정파일을 읽게된다.

자동으로 <서블릿이름>-servlet.xml 형태의 설정파일을 읽도록 된다.

goDispatcher-servlet.xml 에는 스프링이 사용할 설정들이 들어가게된다.

예) HandlerMapping , ViewResolver, ExceptionResolver, 등.

근데,,

한개 이상의 설정파일을 읽어야 한다거나, <서블릿이름>-servlet.xml 와 다른이름의 설정파일을

하고싶을땐?

<servlet>
<servlet-name>spring-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/config/spring/web-mvc.xml

classpath:/config/spring/bus-mvc.xml --설정파일의 경로는 웹어플리케이션 루트 디렉토리를 기준으로 한다.
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

그럼.. 1개이상의 DispatcherServlet 을 설정하려고 할떄는? 하나는 web 페이지용, 하나는 REST 서비스용..

<servlet>
<servlet-name>goWebDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/webpage.xml</param-value>

</init-param>
</servlet>

<servlet>
<servlet-name>goRestDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/rest.xml</param-value>

</init-param>
</servlet>

이렇게 해도 상관없다..

하지만, 이렇게 하면, webpage.xml 여기에 선언된 Bean 을 rest.xml 에서 사용하지는 못한다.

그럼... DAO,SERVICE 등의 공통적으로 사용되는 Bean 을 양쪽에 다 선언해줘야 하나?

아니다..

공통적으로 사용되는 Bean 은

ContextLoaderListener를 Servlet Linstener 로 등록하여 공통으로 사용되는 설정파일들의 목록을 지정해주면 된다.

...

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/company-core-context.xml
classpath:/comp-control-context.xml
</param-value>
</context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

....

이런식으로.

그래서 종합해보면,

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/company-core-context.xml
classpath:/comp-control-context.xml
</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>goWebDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/webpage.xml</param-value>

</init-param>
</servlet>

<servlet>
<servlet-name>goRestDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/rest.xml</param-value>

</init-param>
</servlet>

이렇게 하게 되면, webpage.xml 과, rest.xml 에서는 서로 참조 될 필요가 없는 Bean 만 등록이 되고, 공통 설정은 classpath:/company-core-context.xml classpath:/comp-control-context.xml

에 등록하면 되는 것이다..

*classpth: 이게 있으면, 클래스패스에 위치한 설정파일, 이런 접두어가 없으면, 웹어플리케이션 루트 디렉토리에서 찾는다.

*contextConfigLocation 에서 param-value 의 경로를 지정하지 않으면, WEB-INF/applicationContext.xml 에서 값을 참조한다.


출처 : http://jejoong76.blog.me/70134608484

'Language > JSP' 카테고리의 다른 글

ServletContext와 RequestDispatcher  (0) 2012.11.02
스프링 MVC - 뷰 영역 구현  (0) 2012.10.31
Spring MVC  (0) 2012.10.31
MyBatis-Spring  (0) 2012.10.31
tiles :: 2.2.2 버전 web.xml 설정 세 가지 방법  (0) 2012.10.29
: