'iBatis'에 해당되는 글 2건

  1. 2009.06.02 ssimini 1.0 beta release (24)
  2. 2008.05.21 iBATIS2 에서 CacheModel 사용시 주의할 점
다운로드
http://code.google.com/p/ssimini/downloads/list

<목차>
Ⅰ. Introduction
ssimini?
동기
목적
특징

Ⅱ. 환경 구성하기

Ⅲ. 시작하기
1. @ActionConfig
2. @ActionMapping
  2-1. queryId
  2-2. queryType
  2-3. bindName
  2-4. handlerBean
  2-5. resultMapping
  2-6 scope
  2-7 chain
  2-8 expose
3. @Paging
4. @HandlerMethod
5. ScopeBinder
6. @RestfulMapping
7. 기타 팁
  7-1. 인설트된 레코드의 키 값 접근하기 
8. 앞으로 지원될 기능들


Introduction
ssimini?
(Struts2 + Spring + iBatis) + mini의 약자 입니다.
mini 는 최소, 간결의 의미를 가집니다.
OOP가 등장하고 OOP의 단점을 보완하기 위해 AOP가 등장 했습니다.
OOP, AOP 일지라도 지루한 자바코딩은 필수적입니다.
ssimini는 새로운  Specification Oriented Programming(명세지향프로그래밍)을 제시합니다.
이제는 명세만으로 애플리케이션을 만들 수 있습니다.

동기
지금까지 일을 해오면서 의미없는 자바코딩과 반복적이며 복잡한 설정이 싫었습니다.
예를들면, 각 레이어별로(Controller 또는 Action -> Service -> Dao) 의미없는 한줄의 코드가 위임으로 이루어져 있는 경우 입니다.

목적
SSi 기반 웹애플리케이션에서 단순한 CRUD 구현을 위한 자바 코드를 제거함으로써 진짜 비즈니스에만 집중할 수 있게 하고, 개발 생산성을 높이는 것이 목표 입니다.

특징
ssimini는 iBATIS의 쿼리와 Struts2에서 지원하는 랜더링 가능한 만으로 Web Application을 만들 수 있습니다. 복잡한 비즈니스 로직이 필요할 경우 ssimini의 라이프사이클에 끼어들어 비즈니스를 처리할 수 있습니다.
어떤 유즈케이스를 개발 할 때 개발자가 ssi 기반에서 기존 방식대로 개발을 하고자 한다면 그렇게 해도 됩니다. ssimini는 결코 기존 ssi 기반에 침략적이지 않습니다.
마지막으로 손쉬운 페이징 처리와 가장 진보된 rest style의 url 매핑을 경험할 수 있습니다.


환경 구성하기
이미 ssi 기반을 알고 있고 ssi 기반인 상태를 전재로한 구성 방법 입니다.
1. ssimini.jar 클래스패스에 추가하기.

2. spring 설정 파일(applicationcontext.xml)에 다음 코드를 삽입 하기.
<bean class="com.ssimini.config.ActionConfigurator" />

<context:component-scan base-package="*">
     <context:include-filter type="annotation" expression="com.ssimini.annotation.ActionConfig"/>
</context:component-scan>
component-scan 부분은 ActionConfig 애노테이션을 스캔하도록 하기만 하면 됩니다.

3. 기존에 사용하던 ibatis.jar 제거 하기.
ssimini는 페이징 처리를 위해 커스터마이징 된 ibatis를 포함하고 있습니다.
따라서 유저는 반드시 기존에 사용하던 ibatis.jar를 제거해야 합니다.

위 세가지 단계를 수행했다면 여러분은 바로 ssimini를 시작할 수 있습니다.

※ 만약 ssi 기반이 구축되어 있지 않은 경우 ※
모든 설정이 되어 있는 ssimini-blank.war 를 이용할 수 있습니다.
여러분이 할일은 config 패키지에 있는 jdbc.properties에 디비정보를 입력하는 것 뿐 입니다.
config 패키지에 있는 기타 설정 사항들을 참고 하시어 개발하는데 차질 없으시길 바랍니다.


시작하기
ssimini는 애노테이션 기반의 자바 설정파일로 작동 됩니다. 여러분이 ssimini를 사용한다는 것은 애노테이션 기반의 자바 설정파일을 만든다는 의미과 같습니다. 그럼 이제부터 ssimini를 사용하기 위해 자바설정 파일을 만드는 방법을 배워 보겠습니다.

1. @ActionConfig
이 애노테이션은 클래스 레벨에 선언하며, 해당 자바 파일을 ssimini 설정파일로 간주 하도록 하게 합니다.

가장 먼저 임의의 패키지를 만들고 그 안에 자바 파일을 만듭니다. 그리고 나서 그 클래스에 @ActionConfig 라는 애노테이션을 붙입니다. 그 클래스는 이제부터 ssimini를 위한 설정 파일이 됩니다.
아래 간단한 예제를 보겠습니다.
@ActionConfig(nameSpace="/bbs")
public class BoardConfig {

}

이 애노테이션에는 3가지의 속성이 존재 합니다.
nameSpace        - 네임스페이스
restfulExpose      - restful 로 매핑된 액션을 일반 url로 공개할지 여부
restfulMappings   - restful 매핑
이 시점에서는 nameSpace만 알고 넘어가면 되고 나머지 두개의 속성은 restful 매핑하기 에서 다루겠습니다.
nameSpace는 struts2에서 사용하는 네임스페이스와 같은 개념 입니다.


2. @ActionMapping
설정파일을 생성했으니 이제 액션(사용자 요청 or 요구사항 or url)을 매핑할 차례 입니다. 이 애노테이션은 필드에 선언하며, 액션(사용자 요청 or 요구사항 or url)을 매핑합니다. 또한, url 매핑을 뛰어넘어 해당 요청이 처리해야할 작업들을 설정 합니다.

먼저 설정파일에 아무 필드나 하나 추가 합니다. 필드명이 요청 url로 포함되기 때문에 의미 있는 네이밍을 추천 합니다. 그리고 그 필드 위에 @ActionMapping 애노테이션을 추가 합니다.
여기 간단한 예제가 있습니다.
@ActionConfig(nameSpace="/bbs")
public class BoardConfig {
    @ActionMapping(
             queryid           = "bbs.getList"
            ,queryType      = QueryType.SELECT_LIST
            ,bindName       = "list"
            ,resultMapping = @ResultMapping(
                                     viewFile           = "/WEB-INF/view/bbs/list.vm"
                                    ,resultType       = ResultType.Velocity
                             )
    )
    private String list;
}

여러분은 방금 다음 url을 매핑 했습니다.
http://localhost:8080/컨텍스트/bbs/list.action
url 매핑 규칙은 간단 합니다.
컨텍스트/@ActionConfig의 네임스페이스/ @ActionMapping 애노테이션이 선언된 필드.action

위 설정을 해석하면 이렇습니다.
- http://localhost:8080/컨텍스트/bbs/list.action 의 요청을 매핑한다.
- ibatis 설정파일에서 네임스페이스가 bbs이고 아이디가 getList인 쿼리를 실행한다.
- 쿼리의 종류는 리스트 조회이다.
- 조회된 리스트를 list 라는 이름으로 웹스코프에(디폴트로 request) 바인딩 한다.
- 마지막으로 /WEB-INF/view/bbs/list.vm 파일로 요청을 위임한다. 이때, 응답은 벨로시티가 맡는다.

이제 list.vm 파일에서는 다음과 같이 list 라는 이름으로 바인딩된 객체를 사용할 수 있습니다.
#foreach($board in $list)
    $board.id
#end

이처럼 @ActionMapping 이 ssimini의 가장 핵심적인 애노테이션이라고 할 수 있고, 설정하는 속성도 많습니다. 우리는 이제 그 속성들에 대해 하나하나 알아볼 것 입니다.

2-1. queryId
ibatis 설정파일의 쿼리 아이디를 지정 합니다. 만약 ibatis 환경설정에서 네임스페이스를 사용이 활성화 되었다면
네임스페이스.쿼리아이디 형태로 지정합니다.
(ssimini-blank.war 에서는 ibatis의 네임스페이스 사용이 활성화 되어 있습니다.)
위의 예제에서는 bbs라는 네임스페이스를 가진 XML 쿼리 파일의 getList 라는 아이디를 가진 쿼리를 수행합니다.

2-2. queryType
수행하는 쿼리가 어떤 종류의 쿼리인지 지정해야 합니다.
문자열로 직접 입력하는 것은 오타의 위험과 생산속도 저하등으로 인해 QueryType Enum 을 사용해 값을 지정합니다. 다음은 QueryType에 정의된 값들 입니다.
SELECT_SINGLE  - 단일 레코드 조회
SELECT_LIST      - 여러개의 레코드 조회
INSERT               - 데이터 입력시
UPDATE              - 데이터 수정시
DELETE              - 데이터 삭제시
NONE(디폴트 값)  - 쿼리를 수행하지 않을 때

여기서 중요한게 있습니다. 바로 NONE 인데 NONE은 쿼리를 수행하지 않습니다.
따라서 qeuryId 속성에 ibatis 쿼리 식별자를 설정했더라도 쿼리는 수행되지 않습니다.

2-3. bindName
쿼리를 수행하고 그 결과를 뷰에서 사용할 키워드를 설정 합니다. 또한 이 속성은 queryType이 NONE일 경우 좀 특별해 집니다. 쿼리 수행결과를 바인딩하는 것이 아니고 요청시 넘어온 파라미터맵을 바인딩 합니다. 하지만 이것은 의미가 없습니다. 왜냐하면 요청시 넘어온 파라미터맵에 접근하는 방법은 꼭 bindName 으로 접근하지 않고 다른 방법으로 접근할 수 있기 때문입니다.

2-4. handlerBean
@HandlerMethod 애노테이션 파트에서 다루겠습니다.

2-5. resultMapping
이 속성은 다른 속성들과 다르게 primitive 타입이 아니고 @ResultMapping 타입 입니다.
그렇다면 @ResultMapping 애노테이션의 속성들에 대해 알아보겠습니다.
viewFile
jsp, vm(벨로시티), ftl(프리마커) 같은 뷰 파일의 위치 입니다.

resultType
요청에 대한 응답형태를 지정 합니다. 이 속성의 타입은 ResultType enum 이며, 정의된 값들은 Jsp, FreeMarker, Velocity, PlainText, Redirect, XML, JSON, NONE 입니다.

actionUrl
resultType이 Redirect일 경우 리다이렉트 시킬 url을 지정 합니다.

postParams
resultType이 Redirect일 경우 리다이렉트 될 때 넘어갈 파라미터를 지정 합니다.

xmlExpression
이 속성은 resultType이 XML일 경우 XML 생성을 위한 소량의 표현식 입니다.

ResultType enum 에 정의된 값에서 알 수 있듯이 다양한 종류로 응답할 수 있습니다.
여러분이 어떤 ResultType을 결정하는지에 따라 함께 사용되는 속성들도 정해져 있습니다.
NONE
응답 하지 않습니다.
함께 사용되는 속성 : 없음

Jsp, FreeMarker, Velocity, PlainText
viewFile 속성에 지정된 뷰 파일로 요청을 포워딩 합니다.
함께 사용되는 속성 : viewFile

Redirect
actionUrl 속성에 지정된 url로 요청을 리다이렉트 합니다. 일반적인 리다이렉트와 달리 post 파라미터를 포함할 수 있습니다. post 파라미터는 postParams 속성에 지정 합니다. postParams는 배열이며 지정하는 방법이 따로 존재 합니다.

간단한 예제를 보면 쉽게 이해할 수 있습니다.
postParams = {"id:article.id", "page:#parameters.page"}
id는 파라미터 name 입니다. : 뒤에 오는 값은 ognl 표현식 입니다.
ognl 표현식은 jsp에서 사용하는 el표현식과 매우 흡사하며 오픈심포니에에서 배울 수 있습니다.
위 설정을 해석하면...
- id 라는 이름을 가진 파라미터에  현재 요청에 묶인 article 객체의 id 값을 세팅
- page 라는 이름을 가진 파라미터에  현재 요청시 넘어온 page 라는 파라미터의 값을 세팅
- 위에서 만들어진 파라미터를 리다이렉트시 post 데이타로 넘김
함께 사용되는 속성 : actionUrl, postParams

JSON
bindName으로 바인딩된 데이터를 내부에서 자동으로 JSON 형태의 문자열로 응답합니다.
(응답해더의 contentType 값은 text/text 입니다.)
함께 사용되는 속성 : 없음

XML
xmlExpression 속성에 지정된 표현식대로 XML 문자열을 생성하여 응답합니다.
(응답해더의 contentType 값은 text/xml 입니다.)
표현식 예제를 보면 쉽게 이해 할 수 있습니다.
바인딩 된 데이터가 List일 경우
xmlExpression = "root:articleList articleList:article"
루트 태그는 <articleList> 가 되고 루트 태그의 자식태그는 <article>이 됩니다.

바인딩 된 데이터가 List가 아닐 경우
xmlExpression = "root:article"
함께 사용되는 속성 : xmlExpression

2-6 scope
바인딩 되는 데이타의 생존범위를 지정 합니다.
Scope enum에 지정할 수 있는 REQUEST, SESSION, APPLICATION 3가지 값이 있으며,
디폴트는 REQUEST 입니다.

2-7 chain
이 속성을 이용해 여러개의 액션을 하나로 묶을 수 있습니다. 묶여진 액션들은 모두 하나의 트랜잭션안에서 수행 됩니다. 단, 스프링에서 트랜잭션 매니저가 설정되어 있어야 합니다. 트랜잭션 매니저가 설정되지 않았다면 트랜잭션 안에서 수행되지 않고 독립적으로 수행 됩니다.

예제를 보겠습니다.
@ActionConfig(nameSpace="/bbs")
public class BoardConfig {
    @ActionMapping(
             queryId             = "bbs.getList"
            ,queryType        = QueryType.SELECT_LIST
            ,bindName         = "list"
            ,chain           = {"list2", "this", "list3"}
            ,resultMapping    = @ResultMapping(
                                            viewFile          = "/WEB-INF/view/bbs/list.vm"
                                           ,resultType      = ResultType.Velocity
                               )
    )
    private String list;
  
    @ActionMapping( ... )
    private String list2;
   
    @ActionMapping( ... )
    private String list3;
}
예제에서 알 수 있듯이 chain 속성은 String 배열 입니다.
위 예제에서 액션이 수행되는 순서는 list2 , list, list3 입니다.
this 키워드는 자기 자신(list)를 가리키며 생략할 경우 가장먼저 수행 됩니다.

만약 다른 네임스페이스의 액션을 chain으로 설정하고 싶다면
네임스페이스를 포함한 액션명을 지정하면 됩니다.
chain = {"/bbs2/list", "this", "list2"}

2-8 expose
디폴트로 true 이며 false로 설정하면 url로 매핑되지 않습니다.
즉, http://localhost:8080/컨텍스트/네임스페이스/액션.action url로 접근할 수 없습니다.


3. @Paging
페이징을 위해 더 이상 각 디비에 맞는 페이징 쿼리를 따로 작성할 필요가 없습니다.
표시되어야 할 페이지 번호들, 다음|이전 존재유무 를 따로 계산할 필요가 없습니다.
한 마디로 여러분이 페이징을 위해 그 어떤것도 생각하지 않아도 됩니다.
단지 @Paging 애노테이션 하나면 됩니다.
예제를 보겠습니다.

모든걸 다 가져오는 쿼리지만... ssimini에서 각 디비에 맞는 페이징쿼리로 변환하여 수행 합니다.
<select id="getList" parameterClass="map" resultClass="hashmap">
    SELECT ID AS "id", CONTENT AS "content" FROM SAMPLE
</select>

@ActionConfig(nameSpace="/bbs")
public class BoardConfig {
    @ActionMapping(
             queryId           = "bbs.getList"
            ,queryType      = QueryType.SELECT_LIST
            ,bindName       = "list"
            ,resultMapping = @ResultMapping(
                                     viewFile          = "/WEB-INF/view/bbs/list.vm"
                                    ,resultType      = ResultType.Velocity
                             )
    )
    @Paging(
          dbType                                =  DBType.MYSQL
         ,pageProperty                        =  "page"
         ,listSize                                = 20
         ,pageSize                             = 10
         ,dynamicListSizeProperty         = "listSize"
         ,dynamicPageSizeProperty       = "pageSize"
    )
    private String list;
}

먼저 @Paging 의 속성에 대해 알아보겠습니다.
dbType
DBType enum을 통해 사용하는 디비의 종류를 지정 합니다.
현재 oracle, hsql, mysql 만 가능하며, 추후 릴리즈시 다른 디비도 추가 됩니다.

pageProperty
http요청시 페이지 번호를 담고 있는 파라미터의 이름 입니다.

listSize(옵션)
조회할 리스트의 갯수를 지정 합니다. 디폴트로 15 입니다.

pageSize(옵션)
하단에 표시될 페이지 네비게이션 번호의 갯수를 지정 합니다. 디폴트로 10 입니다.

dynamicListSizeProperty(옵션)
http요청시 조회할 리스트 갯수를 담고 있는 파라미터의 이름 입니다.
이 속성은 listSize 속성에 지정한 값 이외에 동적으로 리스트 조회 갯수를 결정할 수 있습니다.

dynamicPageSizeProperty(옵션)
http요청시 페이지 네이게이션 번호의 갯수를 담고 있는 파라미터의 이름 입니다.
이 속성은 pageSize 속성에 지정한 값 이외에 동적으로 페이지 네비게이션 번호의갯수를 결정할 수 있습니다.

이제 위 설정을 해석하면 이렇습니다.
- 디비는 MySql을 사용한다.
- 요청시 페이지번호를 담고 있는 파라미터의 이름은 "page" 이다.
- 조회할 레코드 수는 20개이고 하단에 표시할 번호의 갯수는 10 이다.

위 매핑 정보에는 조회한 결과를 "list" 라는 이름으로 REQUEST 범위에 바인딩 했습니다.
@Paging 애노테이션이 없다면 순수한 List 객체일 것이지만 지금은 @Paging 애노테이션이 있기 때문에
바인딩되는 객체는 PagingList 라는 객체 입니다. PagingList 클래스는 List 인터페이스를 구현하고 있으며
뷰에서 "list"에 접근할 때 평상시와 똑같이 사용할 수 있습니다.
다른게 있다면 페이징을 돕기 위한 몇개의 프로퍼티가 추가되었다는 점 입니다.
그리고 이 모든 프로퍼티는 자동으로 계산됩니다. 여러분이 계산을 위해 할 일은 없습니다.
public class PagingList<T> implements List<T>{   
    private List<Integer> pageNumber;
    private boolean prev;
    private boolean next;
    private int totalCount;
    private int page;
    private int prevPage;
    private int nextPage;

    .... 자바 빈 스타일의 메서드들 ....
}

pageNumber : 하단에 표시되어야할 페이지 번호들 입니다.
prev : 이전 페이지 여부
next : 다음 페이지 여부
totalCount : 현재 수행된 쿼리의 실제 전체 레코드 수
page : 현재 페이지 번호
prevPage : 이전 페이지 번호
nextPage : 다음 페이지 번호

만약 벨로시티를 사용한다면 이런식으로 사용할 수 있습니다.
#foreach($item in $list)
    $item.id, $item.content <br/>
#end

#if($list.prev)
    <a href="javascript:goPage($!{list.prevPage})">이전</a>
#end
#foreach($num in $list.pageNumber)
    #if($num == $list.page)
        $!{num}
    #else
        <a href="javascript:goPage($!{num})">$!{num}</a>
    #end
#end
#if($list.next)
    <a href="javascript:goPage($!{list.nextPage})">다음</a>
#end
페이징 처리부분은 매크로나 커스텀태그로 만들어 두면 편리 합니다.


4. @HandlerMethod
@ActionMapping 애노테이션의 속성에 handlerBean 이 있었는데 설명을 이곳으로 미뤘죠?
이제 handlerBean 에 대해서 설명할 때가 왔군요.
handlerBean 속성에 스프링에서 관리되는 빈 이름을 지정하여 ssimini의 라이프사이클에 끼어들어 비즈니스를 처리할 수 있습니다. 좀 더 정확하게 말하자면, 그 빈(스프링에서 관리되는 빈)의 어떤 메소드에 @HandlerMethod 애노테이션이 붙어 있다면 ssimini는 그 메소드를 적절한 타이밍에 호출할 것 입니다.

@HandlerMethod의 속성은 2개가 있습니다.
actionName
핸들러 메소드가 적용될 액션명 입니다.
이 속성의 존재 이유는 하나의 handlerBean에 여러개의 액션의 비즈니스를 처리할 수 있는 핸들러 메소드가 있을 수 있기 때문 입니다.

type
HandlerType enum 타입이며 After, Before, Around 3가지 값이 있습니다.
핸들러 메소드가 호출될 타이밍을 결정 합니다.

type 속성에 지정되는 값으로 알 수 있듯이 @HandlerMethod 애노테이션을 이용해 끼어들 수 있는
타이밍은 3개가 존재합니다. 경우에 따라 3가지 핸들러 메소드 모두가 하나의 액션에 적용될 수 있습니다.(Spring AOP와 비슷 합니다.)

그럼 3가지 타이밍에 대해서 알아 보겠습니다.
Before
쿼리 수행 전 파라미터를 가공할 수 있는 타이밍
메서드명은 제약이 없으며 매개변수 제약사항은 아래 예제와 같습니다.
@HandlerMethod(actionName="list", type=HandlerType.Before)
public Object before(Map params) {
     // 파라미터 가공
     return params;
}

After
쿼리 수행 후 결과를 가공할 수 있는 타이밍
메서드명은 제약이 없으며 매개변수 제약사항은 아래 예제와 같습니다.
@HandlerMethod(actionName="list", type=HandlerType.After)
public Object after(Object result) {
    // 결과 가공
    return result;
}

Around
쿼리 수행 타이밍을 둘러 싸서 쿼리 수행을 제어할 수 있는 타이밍
메서드명은 제약이 없으며 매개변수 제약사항은 아래 예제와 같습니다.
@HandlerMethod(actionName="list", type=HandlerType.Around)
public Object around(ActionTarget at, Object params) {
    // 현재 라인에서 파라미터 가공
    Object result = null;
    if( 조건 ) {
       result = at.process(params);                                  // 쿼리 수행
       at.getSqlMapClientTemplate().insert("insert");   // sqlMapClient에 접근하여 다른 쿼리 수행
    } else {
       // else 조건에서는 쿼리 수행 안함
    } 
    // 현재 라인에서 결과 가공
    return (result 또는 다른 값);
}
Around 타이밍은 특별합니다. Before, After 타이밍 모두를 포함하고 있습니다. 따라서 특정 조건에 따라 쿼리를 수행하지 않을 수도 있습니다. 쿼리 수행 제어는 위 예제에서 알 수 있듯이 ActionTarget 클래스의 process(Object params) 메소드를 이용합니다. 또한 getSqlMapClientTemplate() 메소드를 이용하여 다른 쿼리를 직접 수행 할 수도 있습니다.


5. ScopeBinder
만약 여러분이 chain 속성을 사용해 여러개의 액션을 수행할 때 이전 액션에서 수행되어 바인딩된 객체에 접근 하고 싶다면 ScopeBinder 를 이용할 수 있습니다.
예를들어, chain = {"a", "this", "b"} 이렇게 chain 속성을 설정한 후 a 액션이 수행된 후 그 결과가 "adata" 라는 이름으로 바인딩 되었다고 가정할 때 this나 b액션에서 "adata"에 접근하고 싶다면, handlerBean 속성으로 핸들러를 장착시킨 후 상황에 맞는 3가지 타이밍에서 ScopeBinder 클래스를 이용해 꺼내올 수 있습니다.

ScopeBinder 클래스에는 2개의 메소드가 존재 합니다.
public static void bind(Scope scope, String bindName, Object data)
- Scope enum에 해당하는 범위에 데이타를 바인딩 할 때 쓰입니다.

public static Object findObject(String bindName)
-모든 범위에 걸쳐 바인딩된 데이타를 검색하여 리턴 합니다.

따라서 여러분은 this나 b액션에 handlerBean을 설정한 뒤 3가지 타이밍중 원하는 타이밍에서 ScopeBinder.findObject("adata"); 코드로 a액션에서 바인딩 되었던 "adata" 객체에 접근할 수 있습니다.


6. @RestfulMapping
스프링3.0 에서 지원하는 rest style url 매핑보다 더 rest 다운 매핑을 할 수 있습니다.
저는 레스트풀 매핑을 구현하기에 앞서
이곳에서 레스트풀에 대한 학습을 했습니다.

요약하자면 레스트풀은 웹서비스에서 사용되는 개념이며 로이필딩이 창안 했습니다.
복잡한 wsdl 대신 url 로서 명시적인 요청정보를 표현하자는 개념입니다.
같은 url일 지라도 http method 에 따라 애플리케이션이 작업하는 내용이 달라지게 됩니다.
POST: Create
GET : Read
PUT : Update
DELETE : Delete
하지만 현재의 브라우저들은 모든 http method를 지원하지 않습니다.
따라서 별도의 파라미터(예를들면 __http_method) 로 http method 값을 지정합니다.
레스트풀에 대한 더 자세한 내용은 위 링크나 검색을 통해서 알아보도록 하세요.

그럼 ssimini에서 레스트풀 매핑을 어떻게 하는지 알아 보도록 하겠습니다.
브라우저가 http method를 모두 지원하지 않으므로 ssimini 또한 __http_method 라는 파라미터를 통해
http method 값을 지정해야 합니다. 여기 간단한 예제가 있습니다.
(restfulMappings 속성은 배열이기 때문에 1개 이상의 @RestfulMapping 를 지정할 수 있습니다.)
@ActionConfig(
         nameSpace         = "/bbs"
        ,restfulMappings    = {@RestfulMapping(pattern="/list/${page}", forGet="list")}
)
public class BoardConfig {
    @ActionMapping(
             queryId              = "bbs.getList"
            ,queryType         = QueryType.SELECT_LIST
            ,bindName          = "list"
            ,resultMapping    = @ResultMapping(
                                             viewFile         = "/WEB-INF/view/bbs/list.vm"
                                            ,resultType     = ResultType.Velocity
                                        )
    )
    @Paging(
            dbType           = DBType.MYSQL
           ,pageProperty   = "page"
           ,listSize           = 10
           ,pageSize        = 10
    )
    private String list;
}
지금 까지 사용해오던 예제를 레스트풀 매핑 했습니다.
이제부터 기존의 url (http://localhost:8080/컨텍스트/bbs/list.action) 대신
다음 처럼 rest 한 url로 접근할 수 있습니다
http://localhost:8080/컨텍스트/bbs/list/1

그럼 @RestfulMapping 애노테이션의 속성에 대해 알아보겠습니다.
pattern : url 패턴 입니다. 패턴의 일부분으로써 ${} 형태로 파라미터를 바인딩할 수 있습니다.
forGet : __http_method 파라미터 값이 "get"(또는 "GET") 일경우 수행되어야 할 액션명
forPost : __http_method 파라미터 값이 "post"(또는 "POST") 일경우 수행되어야 할 액션명
forPut : __http_method 파라미터 값이 "put"(또는 "PUT") 일경우 수행되어야 할 액션명
forDelete : __http_method 파라미터 값이 "delete"(또는 "DELETE") 일경우 수행되어야 할 액션명
이 파트의 처음에서 언급했듯이 같은 url 일지라도 http method 값에 따라 애플리케이션에서 수행하는 작업이 달라질 수 있습니다. 만약 __http_method 파라미터가 존재하지 않을 경우 "get" 으로 인식하게 됩니다.

레스트풀 매핑 대상이된 액션들은 더 이상 기존의 url로 접근할 수 없습니다.
만약 기존의 url로도 접근 시키고 싶다면 다음과 같이 설정할 수 있습니다.
@ActionConfig(
         nameSpace            = "/bbs"
       ,restfulExpose    = true
        ,restfulMappings      = {@RestfulMapping(pattern="/list/${page}", forGet="list")}
)
public class BoardConfig {
    ...
}
restfulExpose 속성을 true 설정하면 기존 방식의 url과 rest style의 url 모두 허용하게 됩니다.

또 다른 예로
@ActionConfig(
      nameSpace            = "/bbs"
     ,restfulExpose         = true
     ,restfulMappings      = {@RestfulMapping(pattern="/new", forGet="form", forPost="add")}
)
public class BoardConfig {
   
   @ActionMapping( ... 입력 폼 제공 ... )
   private String form;
  
   @ActionMapping( ... 인설트 작업 ... )
   private String add;
}
http://localhost:8080/컨텍스트/bbs/new url로 http method 값에 따라 두가지 작업을 할 수 있습니다.
단, post 일 경우 당연히 입력할 데이터도 함께 넘어가야 합니다.

7. 기타 팁
ssimini를 사용할 때 유용한 팁 입니다.

7-1. 인설트된 레코드의 키 값 접근하기 
여러분은 하나의 요청에서 데이터를 삽입하고, 삽입된 레코드의 키 값을 즉시 알아야할 경우가 많을 것 입니다.
키 값에 접근하는 것은 간단하지만 ssimini에 익숙치 않다면 어려울 수도 있습니다. 따라서 키 값에 접근하는 방법에 대해 알아보겠습니다.

여러분은 인설트된 레코드의 키 값을 알아내기 위해 반드시 ibatis 쿼리 파일에서 <selectKey> 엘리먼트를 사용해야 합니다. 다음은 <selectKey> 엘리먼트를 사용한 예제 입니다.
<insert id="insert" parameterClass="map">
   <selectKey keyProperty="id" resultClass="int">
      SELECT NEXT VALUE FOR SEQ_TEST_ID FROM DUMMY
   </selectKey>
      
   INSERT INTO TEST VALUES(#id#, #content#)
</insert>

이 문서는 ibatis 사용법을 나타내는 문서가 아닙니다. 하지만, selectKey 요소 만큼은 반드시 알아야할 사항이므로 복습차원에서 위 예제에 대해 해석을 해보겠습니다. INSERT 쿼리를 실행하기에 앞서 <selectKey> 엘리먼트에 의해서 시퀀스 객체로부터 자동증가된 키 값을 하나 얻어 옵니다. 이렇게 얻어온 키 값은 keyProperty 속성 값이 "id" 이므로, <insert>엘리먼트에 전달된 파라미터 객체에 "id" 라는 프로퍼티에 세팅 합니다.

이제 여러분은 <selectKey> 엘리먼트에 대해 확실히 알게 되었고, 이런 지식 가지고 있다면 다음의 두가지 방법으로 인설트된 레코드의 키 값에 접근할 수 있습니다.

키 값 바인드 시킨후 접근하기
만약 여러분이 @ActionMapping의 bindName을 설정 했다면, 해당 bindName으로 <selectKey> 엘리먼트에 의해 리턴된 키 값이 바인딩 됩니다. 따라서 다른 액션체인에서 전에 입력된 키 값을 필요로 한다면 ScopeBinder를 이용할 수고, 리다이렉트시 바로 아래에서 배우는 #parameters.id 표현식으로 파라미터를 전달할 수 있습니다.

#parameters 로 접근하기
#parameters는 ognl 표현식의 일부이며, 가리키는 객체는 <insert> 엘리먼트로 전달된 파라미터 객체 입니다.
따라서 위 예제의 경우 id라는 프로퍼티에 세팅된 상태 이므로, #parameters.id 표현식으로 접근할 수 있습니다.
이러한 표현식은 리다이렉트시 전달되는 파라미터를 지정할 때 쓰이거나,
핸들러에서ActionContext.getContext().getValueStack().find("#parameters.id"); 로 접근할 수 있습니다.


8. 앞으로 지원될 기능들
명세기반이기 때문에 모든 기능은 애노테이션 명세로 추가할 계획 입니다.
-  @FileUpload
-  @Validate
-  @Schedule
-  기존 ssi 기반 코드에서 ssimini 명세를 호출하기 위한 api
-  트랜잭션 제어
-  기타 등등...


ps
피드백은 jjaeko@naver.com 으로 보내주시면 감사 하겠습니다.
그리고 이 프레임워크를 사용하다가 잘 안되는 부분이 있으면 지원 해 드릴테니 망설임 없이 연락 주세요.
(단, ssi 기반을 충분히 이해하고 있어야 합니다.)

ps2
예제는 제작중 입니다.
신고

'Open Projects > ssimini' 카테고리의 다른 글

ssimini 1.0 beta release  (24) 2009.06.02
Posted by 째코
<CacheModel /> 요소를 선언할 때 cache-size는 SQL의 결과 인스턴스의 갯수인줄로 알고 있었는데
막상 테스트 해보니 그게 아니였습니다. 그래서 소스를 확인해보니 cache-size는 SQL의 갯수 였군요 -0-
public void putObject(CacheModel cacheModel, Object key, Object value) {
    cache.put(key, value);
    keyList.add(key);
    if (keyList.size() > cacheSize) {
      try {
        Object oldestKey = keyList.remove(0);
        cache.remove(oldestKey);
      } catch (IndexOutOfBoundsException e) {
        //ignore
      }
    }
  }

서로다른 SQL의 갯수가 cache-size 보다 크면 가장 처음 실행되었던 SQL을 삭제 합니다.

다시 생각해보니 당연한 것 같습니다.
Hibernate나 JPA는 주키를 매핑하기 때문에 지능적인 캐싱이 되지만
iBATIS는 SQL에만 의존하기 때문에 인스턴스를 식별할 방법이 없기 때문이죠...

신고

'iBATIS' 카테고리의 다른 글

iBATIS2 에서 CacheModel 사용시 주의할 점  (0) 2008.05.21
Posted by 째코


티스토리 툴바