1.5.3이 나온지 일년이 지나서야 1.5.4 가 릴리즈 되었다.
그동안 사이트에 어떤 글도 올라오지 않아 스트라이프 망한줄 알았다.
 사실 2010년 11월에 릴리즈 되었으니, 내 포스팅도 굉장히 늦은 감은 있긴 하지만 여튼 변경된게 무언지 알아보자

Change Log

체인지 로그에 보면 추가된 사항은 다음과 같다. [체인지 로그 확인]

  • STS-678 : DynamicMappingFilter fails with "Could not get a reference to StripesFilter" on WAS 6.1
  • STS-762 : Eliminate UrlBindingFactory.getInstance() from 1.5.x branch
  • STS-767 : Showing an error message when the parameter name is _eventName

몇가지 변경된 사항은 없어보이나 막상 적용해보면 에러가 쏟아진다.
실제로 적용해서 무엇이 변경되었는지 알아보자.

동적 속성용 태그 라이브러리 추가

기존에는 태그 라이브러리 하나로 동적 속성을 지원하였다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="http://stripes.sourceforge.net/stripes.tld" %>
<s:useActionBean beanclass="com.vine.admin_webapp.cm.action.ContentsEditActionBean" var="actionBean" event="read" />
<s:layout-render name="/layout/default.jsp" section="${actionBean.section}">
    <s:layout-component name="pageTitle">컨텐츠 조회</s:layout-component>
    <s:layout-component name="pageDesc"><p>컨텐츠 정보를 조회합니다.</p></s:layout-component>
<s:layout-render/>

1.5.3 까지는 태그에 동적 속성을 사용할 수 있었으나, 1.5.4에는 동적속성용 태그와 표준 태그를 분리하여 사용해야한다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="http://stripes.sourceforge.net/stripes.tld" %>
<%@ taglib prefix="d" uri="http://stripes.sourceforge.net/stripes-dynattr.tld" %>
<s:useActionBean beanclass="com.vine.admin_webapp.cm.action.ContentsEditActionBean" var="actionBean" event="read" />
<d:layout-render name="/layout/default.jsp" section="${actionBean.section}">
    <s:layout-component name="pageTitle">컨텐츠 조회</s:layout-component>
    <s:layout-component name="pageDesc"><p>컨텐츠 정보를 조회합니다.</p></s:layout-component>
<d:layout-render/>


더 자세한 것은 다음 링크 참조
http://www.stripesframework.org/display/stripes/Tag+Library+Doc

Layout-component 재사용 변경

레이아웃 페이지에서 중첩된 레이아웃을 사용할 경우 동일 페이지내에 동일 컴포넌트가 존재할 경우 중복 되어 처리된다. 즉,

/layout/default.jsp

<s:layout-render name="/layout/blank.jsp">
  <s:layout-component name="script">
   <script type="text/javascript">
    ${script}
   </script>
  </s:layout-component>
 </s:layout-render>
 <s:layout-render name="/layout/blank.jsp">
  <s:layout-component name="style">
   <style type="text/css">
    ${style}
   </style>
  </s:layout-component>
 </s:layout-render>

 

/layout/blank.jsp

<%@ taglib prefix="s" uri="http://stripes.sourceforge.net/stripes.tld" %>
<s:layout-definition>
 <s:layout-component name="script"></s:layout-component>
 <s:layout-component name="style"></s:layout-component>
</s:layout-definition> 


와 같이 사용할 경우 스크립트 내용이 두번, 스타일부분이 2번 출력이 된다.
따라서 중첩된 컴포넌트를 사용할 경우 컴포넌트의 이름이 중복되지 않도록 다음과 같이 처리해야한다.

변경된 /layout/default.jsp

<s:layout-render name="/layout/blank.jsp">
  <s:layout-component name="script">
   <script type="text/javascript">
    ${script}
   </script>
  </s:layout-component>
<s:layout-component name="style">
   <style type="text/css">
    ${style}
   </style>
  </s:layout-component>
 </s:layout-render>
스트라이프 버젼이 릴리즈되었다. 사실 나온지 오래되었지만, 그냥 1.5.2 쓰고 있었는데..
웹로직에 업로드햇더니만 에러!-_-;;

찾아보니 패치되었다고 새로 받으란다. 1.5.3에서 변경된 사항은 다음과 같다.
  • 웹로직 10.0 에서 액션빈을 못 찾는 오류
  • 위자드 관련하여서, 첫 페이지에 Submit 버튼이 여러개일 경우 두번재에서 다중 이벤트가 발생하는 현상

빨리 올리자. 
늦게 올리면 또 오픈소스가 그렇지 머라고 하는 사람들 있다. =_=;;
조용히 업글..

실제로 ajax 클라이언트 호출은 사용자가 작성해야한다.

스트라이프가 해주는 일은 ajax 호출에 대하여 적절히 데이타를 넘겨줄 수 있을 뿐이다.

 

Ajax 호출 및 스트라이프로 응답하기

우선 완전한 jsp 페이지를 보자.

<%@page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
  <head>
      <title>My First Ajax Stripe</title>
      <script type="text/javascript"
              src="${pageContext.request.contextPath}/ajax/prototype.js"></script>
      <script type="text/javascript" xml:space="preserve">
          /*
           * Function that uses Prototype to invoke an action of a form. Slurps the values
           * from the form using prototype's 'Form.serialize()' method, and then submits
           * them to the server using prototype's 'Ajax.Updater' which transmits the request
           * and then renders the response text into the named container.
           *
           * NOTE: Requires Prototype version 1.6 or above.
           *
           * @param form reference to the form object being submitted
           * @param event the name of the event to be triggered, or null
           * @param container the name of the HTML container to insert the result into
           */
          function invoke(form, event, container) {
              if (!form.onsubmit) { form.onsubmit = function() { return false } };
              var params = Form.serialize(form, {submit:event});
              new Ajax.Updater(container, form.action, {method:'post', parameters:params});
          }
      </script>
  </head>
  <body>
    <h1>Stripes Ajax Calculator</h1>

    <p>Hi, I'm the Stripes Calculator. I can only do addition. Maybe, some day, a nice programmer
    will come along and teach me how to do other things?</p>

    <stripes:form action="/examples/ajax/Calculator.action">
        <table>
            <tr>
                <td>Number 1:</td>
                <td><stripes:text name="numberOne"/></td>
            </tr>
            <tr>
                <td>Number 2:</td>
                <td><stripes:text name="numberTwo"/></td>
            </tr>
            <tr>
                <td colspan="2">
                    <stripes:submit name="add" value="Add"
                        onclick="invoke(this.form, this.name, 'result');"/>
                    <stripes:submit name="divide" value="Divide"
                        onclick="invoke(this.form, this.name, 'result');"/>
                </td>
            </tr>
            <tr>
                <td>Result:</td>
                <td id="result"></td>
            </tr>
        </table>
    </stripes:form>
  </body>
</html>

이 예제는 숫자 2개를 입력받은 후 ajax 호출로 더한결과와 나눈 결과를 받아서 result 안에 출력하는 소스다. 여기선 프로토타입을 사용했지만, jQuery 를 써도 당연히 무방하다.
중요한 부분은 다음 구문이다.

var params = Form.serialize(form, {submit:event});

액션빈 클래스에 받아들일 메서드명을 submit 파라미터로 집어넣고 있다.
다음은 액션빈 클래스를 보자.

 

public class CalculatorActionBean implements ActionBean, ValidationErrorHandler {
    private ActionBeanContext context;
    @Validate(required=true) private double numberOne;
    @Validate(required=true) private double numberTwo;

    public ActionBeanContext getContext() { return context; }
    public void setContext(ActionBeanContext context) { this.context = context; }

    @DefaultHandler public Resolution add() {
        String result = String.valueOf(numberOne + numberTwo);
        return new StreamingResolution("text", new StringReader(result));
    }

    public Resolution divide() {
        String result = String.valueOf(numberOne / numberTwo);
        return new StreamingResolution("text", new StringReader(result));
    }

    // Standard getter and setter methods
    public double getNumberOne() { return numberOne; }
    public void setNumberOne(double numberOne) { this.numberOne = numberOne; }

    public double getNumberTwo() { return numberTwo; }
    public void setNumberTwo(double numberTwo) { this.numberTwo = numberTwo; }
}


add 메서드와 devide 메서드를 구현한걸 확인할 수 있다. ajax 통신이라고 조금 다르게 사용한것은 Resolution 을 StreamingResolution 을 반환한다는것 뿐이다.
StreamingResolution 을 3개의 생성자를 가지며, 첫 인자는 contentType 을 받아들이는것에 주의한다.

다음은 주로 사용하는 Resolution 에 대한 Java doc 이다.

Constructor Summary
StreamingResolution(String contentType)
Constructor only to be used when subclassing the StreamingResolution (usually using an anonymous inner class.
StreamingResolution(String contentType, InputStream inputStream)
Constructor that builds a StreamingResolution that will stream binary data back to the client and identify the data as being of the specified content type.
StreamingResolution(String contentType, Reader reader)
Constructor that builds a StreamingResolution that will stream character data back to the client and identify the data as being of the specified content type.

StreamingResolution 외에 흥미로운 ajax 관련 Resolution 들이 더 있다.

Constructor Summary
JavaScriptResolution(Object rootObject, Object... objectsToExclude)
Constructs a new JavaScriptResolution that will convert the supplied object to JavaScript.

JavaScriptResolution 은 주어진 인자를 javascript 에서 평가(eval)할 수 있는 형태로 문자열을 만들어 출력한다. 즉 json 형태로 출력한다.

물론 ForwardResolution 을 이용하여 jsp 로 렌더링해도 상관 없다.

Constructor Summary
ForwardResolution(Class<? extends ActionBean> beanType)
Constructs a ForwardResolution that will forward to the URL appropriate for the ActionBean supplied.
ForwardResolution(Class<? extends ActionBean> beanType, String event)
Constructs a ForwardResolution that will forward to the URL appropriate for the ActionBean supplied.
ForwardResolution(String path)
Simple constructor that takes in the path to forward the user to.

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

스트라이프는 파일 업로드 라이브러리로 cos 라이브러리를 사용한다. 이것은 다른 라이브러리에 의존하지 않는다.

반면 apache 의 file-upload 는 commons-io 패키지에 의존한다. 또한 라이센스 문제로 인해 cos 를 사용한다.

 

web.xml 의 스트라이프 필터에 멀티파트 인터셉터를 추가한다.

[web.xml]

<init-param>
<param-name>MultipartWrapper.Class</param-name> <param-value>
net.sourceforge.stripes.controller.multipart.CommonsMultipartWrapper </param-value> </init-param>

파일찾기 버튼이 추가될 jsp 파일엔 다음의 태그핸덜를 사용한다.

 

[upload.jsp]

<stripes:form>
...
<stripes:file name="newAttachment"/>
...
</stripes:form>

그리고 파일을 받을 액션 클래스에는 FileBean 클래스를 받는다.
[uploadActionBean.java]
private FileBean newAttachment;
public FileBean getNewAttachment() return newAttachment;
}
public void setNewAttachment(FileBean newAttachment) {
this.newAttachment = newAttachment; }

파일 업로드 크기 제한

파일 업로드시에 업로드 크기를 제한할 수 있다. 기본값으로 10메가바이트로 지정되어있다.
web.xml 의 StripesFiler 부분에 FileUpload.MaximumPostSize 설정을 추가한다.
설정값은 1kb,10mb,10gb 등으로 사용할 수 있다. 다음은 2기가로 제한한 예이다.

[web.xml]
<filter>
<display-name>Stripes Filter</display-name> <filter-name>StripesFilter</filter-name>
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
<init-param>
<param-name>ActionResolver.Packages</param-name> <param-value>kr.or.ehc.kg.web</param-value> </init-param>
<init-param>
<param-name>Interceptor.Classes</param-name> <param-value>net.sourceforge.stripes.integration.spring.SpringInterceptor</param-value>
</init-param>
<init-param> <param-name>MultipartWrapper.Class</param-name> <param-value>net.sourceforge.stripes.controller.multipart.CommonsMultipartWrapper</param-value>
</init-param>
<init-param> <param-name>FileUpload.MaximumPostSize</param-name> <param-value>2gb</param-value>  </init-param>
</filter>


참조 : http://www.stripesframework.org/display/stripes/File+Uploads

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

스트라이프에서 스프링의 빈을 가져오려면 다음의 설정을 추가해야한다.

web.xml 의 스프링 필터에 스프링 인터셉터를 추가한다.

[web.xml]

<filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
       <param-name>ActionResolver.Packages</param-name>
       <param-value>kr.or.ehc.kg.web</param-value>
    </init-param>
    <init-param>
            <param-name>Interceptor.Classes</param-name>
            <param-value>
                        net.sourceforge.stripes.integration.spring.SpringInterceptor
            </param-value>
     </init-param>
</filter>

 

그리고 액션 리졸버 클래스에서는 @SpringBean 어노테이션으로 스프링 빈을 가져올 수 있다.

public class ArticleAction implements ActionBean {
    ...
    @SpringBean("articleService")
    private ArticleService articleService;
    ...
}

 

물론, 메서드에 어노테이션을 지정할 수 있다. 스프링빈을 가져올 때, 관례적으로 'set스프링빈' 의 형태의 메서드를 취하나, 메서드 보안 관계상 다른 이름으로 주입하는 것도 좋은 방법이다

public class ArticleAction implments ActionBean {
     ...
    private BoardService boardService;
    @SpringBean("boardService")
    public void injectBoardService(BoardService boardService) {
        this.boardService = boardService;
    }

    ...
}


라이브러리 추가

stripes 는 대다수의 다른 웹프레임워크와는 달리 의존 라이브러리가 존재하지 않는다. 이것은 굉장히 큰 장점이다. 따라서 부담없이 프로젝트에 적용할 수 있다.

maven 을 사용한다면 스트라이프를 추가한다. maven 을 사용하지 않는다면 사이트에서 jar 를 받아 클래스패스에 추가한다.

[pom.xml]

<dependency>
    <groupId>net.sourceforge.stripes</groupId>
    <artifactId>stripes</artifactId>
    <version>1.5.2</version>
</dependency>

 

web.xml 설정

다음의 설정을 추가한다.

이것은 *.jsp 패턴에는 스프라이프 필터를, 그리고 *.action 패턴에는 스트라이프 서블릿(서블릿 디스패쳐)을 설정한다.

즉, 뷰에 대해서 접근은 jsp 로 접근하고, 행위에 대해선 *.action 으로 접근한다. (관례임)

 

StripeFilter 를 설정할 때, ActionResolver.Packages 는 매우 중요하다.

이것은 바로 액션 클래스가 위치할 패키지다. 이 위치를 보고, 액션 클래스를 찾으므로, 정확히 지정한다.


[web.xml]

<filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
        <param-name>ActionResolver.Packages</param-name>
        <param-value>com.slothink.web</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

<filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <servlet-name>StripesDispatcher</servlet-name>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

<servlet>
    <servlet-name>StripesDispatcher</servlet-name>
    <servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>StripesDispatcher</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

 

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

최근의 스트라이프를 알게 되었다.

MVC 는 좋지만, 설정 관리에 귀찮음에 학을 때던 수많은 개바자들로부터 탈출할 수 있는 그야말로 심플한 웹프레임웍이었다.

이것은 기본적으로 xml 설정을 사용하지 않는다.

뷰는 직접 jsp 로 접근하며, 행위에 대해서 컨트롤러에 대해서는 액션클래스를 구현해서 시현한다. 따라서 막코딩 좋아하는 jsp 개발자들도 부담없이 받아들일 수 있을것이다. 
스프라이프를 마스터한 사람이 교육을 한다면 교육 1시간이면 충분하지 않으려나?

또한 근본적으로 PRG(Post Redirect Get) 패턴이 적용되어있어, 올바른 웹페이지를 만들 수 있을 것이다.

진작,, 이걸 알았어야 하는데..-_-

하지만, 스트라이프가 스트러츠나 스프링 mvc 보다도 강력함에도 인지도가 낮은것은 사실이다. 따라서 한글책도 없고, 레퍼런스도 많은 것은 아니다. 그러나 일부 스트라이프 원서와 스트라이프 사이트에서 제공하는 레퍼런스만으로도 충분히 만족할만한 컨텐츠를 구할 수 있다.

여하튼, 이제 스트라이프를 엄청 사용해보는 김에 스트라이프 사용에 대한 문서화를 진행하려고 한다.

다함께 차차차~

+ Recent posts