Framework/Spring2010. 6. 1. 18:04

Spring과 Struts2 둘다 사용해봤으면 Request Parameter를 Command에 매핑하는 과정에서의 차이점을 알 수 있다.
당연 중첩 파라미터에 대해 기본적으로 처리해주는 Struts2가 사용하기 편리하다.
그러나 Spring에서도 기본적으로 처리가 되지 않는 것은 아니다. 단, 중첩 파라미터의 인스턴스가 미리 생성이 되어 있지 않으면 NullPointException을 발생시킨다는 부분만 없으면 말이다.
그래서 MultiActionController의 소스를 열어서 중첩 파라미터의 인스턴스를 필요에 따라 미리 생성해주는 부분을 추가하여 해결해 보았다. (현재 잘 사용중이다.)

소스를 조금만 파악하면 의외로 아주 간단하다. 단점은 Spring이 버전업이 되면 다시 확인을 해야한다는 것 ...

중첩 파라미터를 의미하는 값은 주로 사용하는 "."을 사용했다.

private final String OVERLAP_PROPERTY_PREFIX = ".";

그리고 실질 적으로 중첩 파라미터를 확인하고 해당 Command의 중첩 인스턴스를 생성해 주는 함수를 추가한다.

protected void preOverlapProperty(HttpServletRequest request, Object command) {
    String paramName = null;
    Enumeration<String> enumer = request.getParameterNames();
    while (enumer.hasMoreElements()) {
        paramName = enumer.nextElement();

        if (paramName.indexOf(OVERLAP_PROPERTY_PREFIX) != -1) {
            initOverlapProperty(paramName, command);
        }
    }
}


protected Object initOverlapProperty(String propertyName, Object bean) {
    StringTokenizer st = new StringTokenizer(propertyName, OVERLAP_PROPERTY_PREFIX);
    String subPropertyName = null;
    Object beanObject = bean;
    BeanMap beanMap = new BeanMap(beanObject);

    while (st.hasMoreTokens() && st.countTokens() > 1) {
        subPropertyName = st.nextToken();
        beanObject = beanMap.get(subPropertyName);

        if (beanObject == null) {
            Class subBeanClass = beanMap.getType(subPropertyName);
            if (subBeanClass != null) {
                try {
                    beanObject = subBeanClass.newInstance();
                    beanMap.put(subPropertyName, beanObject);
                    beanMap = new BeanMap(beanObject);
                } catch (Exception e) { }
            }
        }
    }
    return bean;
}


그럼 이렇게 중첩 파라미터에 대한 처리를 해주는 추가 된 함수를 어디에서 호출을 해야되나 ...
원래 MultiActionController 클래스의 invokeNamedMethod 함수 내에서 bind 하기 전에 preOverlapProperty 함수를 호출하면 된다.
수정 된 소스는 아래와 같다.

protected final ModelAndView invokeNamedMethod(String methodName,
        HttpServletRequest request, HttpServletResponse response)
        throws Exception {

    Method method = (Method) this.handlerMethodMap.get(methodName);
        ...

    params.add(command);
    
    preOverlapProperty(request, command); // 중첩 파라미터 처리를 위해 추가 된 부분

    bind(request, command);
        ...
}

위와 같이 하면 Struts2에서 사용하는 중첩 파라미터에 대해서 동일하게 처리가 된다.
뭐 수정 된 부분은 원 Spring의 파라미터 binding을 하는 로직은 그대로 사용을 하며, 단지 Command 내에 선언 된 객체에 대해 Request Parameter에서 필요하다면 미리 Instance를 생성해주어 NullPointException의 발생을 피한 것 밖에 없다.

첨부한 소스는 현재 서비스에서 사용하고 있는 Base Control이며 위의 중첩 파라미터의 처리 외에 Request와 Response 그리고 Model에 대한 선/후 간단한 Logging이 추가되어 있는 소스이다.

Posted by as.wind.914
Framework/Struts 22008. 3. 27. 16:28

Struts2는 자바기반의 Web Application을 개발하는데 사용되는 Framework 이다. 자바 개발자라면 거의 모든이가 알고 있는 Apache Struts의 업그레이드 된 Framework 이다.

많은 자바 개발자들이 알고있듯이 Apache Struts는 자바 Web Application Framework의 대표로 자리 매김을 하였고, 다른 개발 Framework의 사용을 확산시키는데도 아주 큰 기여를 했다. 그러나 시간이 지나면서 개발자들은 웹 환경에 익숙한 사용자들의 다양하고 복잡한 요구사항을 접하게 되었고, Apache Struts가 그에 맞게 업그레이드를 거듭했지만 따라가기는 힘이 들었다. 또, 뒤이어 WebWork, Spring 등 많은 쉽고 간결하며 풍부한 사용자 인터페이서를 구축할 수 있는 Web Application Framework들이 등장하고, 개발자들은 Struts를 벗어나 다양한 Framework을 도입하여 개발하게 된다. (저 또한 Struts를 사용하다 어느정도 한계를 느껴 WebWork2을 Framework으로 사용을 하고 있었다.)
이에 Apache Struts는 Struts와 WebWork2를 결합하는 것으로 Struts2를 내놓게 되었다.

Struts2는 WebWork2와 결합하면서 MVC(Model View Controller)패턴을 구현한 내부 아키텍쳐가 WebWork2의 아키텍쳐로 완전히 달라졌다. Struts는 Servlet과 요청프로세스 기반으로 액션을 처리하는 구조대신 Filter와 인터셉터 기반의 WebWork2 아키텍쳐를 적용해 매우 유연하게 구축되었다. 나 또한 Struts에서 WebWork2로 갈아탄 이유가 Framework이 매우 유연하며, 설정이 작았기 때문이였다. 이 밖에도 Struts2는 아직도 다른 뛰어난 오픈 소스의 좋은 기능들을 쉽게 접목할여 활용할 수 있는 방향으로 계속해서 업그레이드가 되고 있다.

-- Struts2의 새롭게 달라진 모습
WebWork2 MVC 아키텍쳐 도입 : Struts와는 전혀 다른 WebWork2의 아키텍쳐를 적용 함.
인터셉터(Interceptor) : 인터셉터는 HTTP Request를 중간에 가로채서 처리된다는 것은 Filter와 비슷한 개념이나 액션의 실행 전과 후에 처리 할 코드를 다르게 구성할 수 있다. 인터셉터는 AOP 개념으로 Struts2 자체 내에 많은 인터셉터가 구현이 되어 있으며, 개발자가 필요에 따라 새로운 인터셉터를 등록하여 사용할 수도 있다. 복수의 인터셉트를 순서에 따라 실행도 가능하다.
간단한 설정과 빠른 리로딩 : Struts2에서는 환경 설정 또한 WebWork2를 거의 그대로 적용하여 기본값으로 사용하여도 많은 설정을 생략할 수 있게 되어 있다. 그리고, 설정도 extends와 include가 가능해 더욱 편리해졌으며, 변경 된 설정은 웹 컨테이너를 재시작하지 않고 빠르게 리로딩된다.
Framework에 종속적이지 않은 액션 : Struts1과는 다르게 Struts2의 액션은 Struts2 Framework에 종속되지 않게 디자인하여 프레임워크를 사용하면서도 프레임워크에 종속적이지 않으며, 쉽게 테스트가 가능하다.
의존성주입(Dependency Injection)의 지원 : 객체 간의 의존성 결합도를 낮추기 위해 구글 Guice Framework를 내부적으로 사용하고 있으며, Spring과 같은 뛰어난 Framework를 함께 사용할 수 있다.
Plugin 기능 : Struts2는 유용한 많은 오픈 소스 프레임워크를 쉽게 사용할 수 있도록 Plugin을 제공한다.
(JFreeChart, JasperReport, Sitemesh .. 등)
다양한 Result 타입 : Struts2는 가장 많이 사용하는 JSP외에도 많은 뷰 기술(Freemark, Velocity 등)을 지원하는 Result 타입을 사용할 수 있으며, 개발자 스스로 만들어 사용도 가능하다.
강력하고 많은 커스텀태그 지원 : 재사용성이 높은 많은 사용자 인터페이스 태그(커스텀태그)를 제공하며, 테마와 템플릿도 사용이 가능하다.

위에서 보는 것과 같이 Struts2에 새롭게 달라진 모습들이 많다. 그러나 여기서 가장 크게 달라지고 중요한 부분은 MVC 아키텍쳐가 변경되었다는 것이다. 그럼 Struts2에서 도입 된 WebWork2 MVC 아키텍쳐는 어떠한지 간략한지, Struts의 MVC 아키텍쳐와 어떻게 다른지를 알아보자. 다음에 Struts2 아키텍쳐 부분에서 자세히 다룰것이니 간단히 이해하고 넘어가도 무방할듯 하다.

-- Struts MVC 아키텍쳐 vs WebWork2 MVC 아키텍쳐
여기서 MVC 모델이 무엇이며, 왜 필요한지와 같은 문제는 다루지 않겠다. 궁금한이는 따로 알아보길 바란다. 여기서 이야기하는 Struts는 Struts2가 아닌 Struts1을 의미합니다.

먼저 Struts MVC와 WebWork2 MVC의 공통점은 초기 MVC 모델에서 업그레이드 된 Front Controller 방식을 이용한 MVC 모델이라는 것이다.

사용자 삽입 이미지

[ Front Controller MVC 패턴 ]

위 그림과 같이 Dispatcher가 Controller를 핸들링하는 구조입니다. Struts와 WebWork2에 모두 Dispatcher가 있습니다. 초기 대부분의 Web Application은 Page Controller 방식으로 개발이 되었습니다. Page Controller 방식은 요청이 많을 경우 부하가 걸린다는 것이고 하나의 페이지마다 하나씩의 Page Controllger가 만들어져야 한다는 문제점을 가지고 있었습니다. 그래서 이 문제점을 해결한 방식이 바로 Front Controller 방식 입니다. Front Controller 방식에서는 Controller는 핸들러와 복수개의 커맨드로 구현하여 이 문제점을 해결하였습니다.

Struts MVC와 WebWork2 MVC의 차이점은 Push Model & Pull Model 방식으로 나누어 집니다. Struts는 Push Model이고, WebWork2는 Pull Model 입니다.
그 차이점은 그림으로 보면 명확하게 알 수 있습니다.

사용자 삽입 이미지

[ Push Model ]

사용자 삽입 이미지

[ Pull Model ]

먼저 Struts가 해당하는 Push Model은 초기 MVC Model에 주로 채택되었던 방법으로 사용자의 요청에 의해 처리 된 결과를 View에 밀어넣는(Push) 방식이고, WebWork2가 속하는 Pull Model은 View에서 다수의 Controller로 부터 필요에 따라 처리 결과를 땡겨(Pull)오는 방식이다. Pull Model은 필요한 컴포넌트를 호출하여 처리하는 방식이 가능하기 때문에 이를 Component MVC라고도 부르며, 최근에 발표되는 많은 Web Application Framework이 이 방식을 사용한다.

Push Model : Struts, Spring ...
Pull Model : Struts2(WebWork2), Tapestry, JBossSeam, ...

이상 Struts2가 대체 무엇인가에 대해 간략하게 알아보왔다. 위 내용을 보고는 Struts2는 Java Web Application Framework 이며, 대략적으로 이러한 특징이 있구나 하는 것 정보만 이해하면 될 것이다. 이후 Struts2의 세부아키텍쳐 및 그 내용과 부가적인 부분들에 대해 알아본다.

Posted by as.wind.914
Framework/iBatis2008. 3. 25. 20:53

Java로 진행되는 많은 프로젝트들이 iBatis를 이용하여 진행될 것이다. iBatis를 사용해본 사용자라면 iBatis를 사용하기 위해 생성해야 하는 DTO, DAO, SqlMap들을 만드는 것이 귀찮은 작업이라는 것은 다들 느낄 것이다. 나또한 DTO를 생성하는 것이 제일 귀찮으니 ...
그래서 iBatis 관련 소스 Generator를 만들까 고민하다 iBatis에서 제공하는 Abator이란 것을 발견하고 간단히 사용해 보니 쓸만해서 소개를 한다. 물론 생성 된 소스를 조금은 수정을 해야 한다.

그럼 Abator를 간략하게 들여다 보자.

-- Abator 다운로드 
Abator 공식 사이트에서 소스와 Documents, Binaries를 다운받아 적당한 위치에 압축을 푼다.

-- iBatis 소스 생성하기
Abator Plugin은 간단하게 말하면 DataBase에 접속하여 지정 된 Table에 대한 DTO, Key, DAO, SqlMap 소스를 자동으로 생성해 준다. Abator을 이용하여 iBatis 소스를 생성하는 방법은 간단하다. abator의 config xml file을 생성하고 ant를 이용하여 실행하면 된다.

abator_config.xml file
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE abatorConfiguration PUBLIC "-//Apache Software Foundation//DTD Abator for iBATIS Configuration 1.0//EN" "http://ibatis.apache.org/dtd/abator-config_1_0.dtd">
   <abatorConfiguration>
      <abatorContext id="Oracle">
         <!-- DataBase 연결정보 정의 -->
         <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver"
               connectionURL="jdbc:oracle:thin:@192.160.100.204:1521:devora"
               userId="backend" password="!Qprdpsem@">
               <classPathEntry location="xxx/ojdbc14.jar" />
         </jdbcConnection>
         <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
         </javaTypeResolver>

         <!-- 모델빈의 위치및 생성옵션 (DTO 생성) -->
         <javaModelGenerator targetPackage="com.junducki.blog.ibatis.dto" targetProject="../blog/src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
         </javaModelGenerator>
         <!-- SqlMap 생성 -->
         <sqlMapGenerator targetPackage="pay" targetProject="../blog/src/main/java/sql">
            <property name="enableSubPackages" value="true" />
         </sqlMapGenerator>
         <!-- DAO 생성옵션 -->
         <daoGenerator type="SPRING" targetPackage="com.junducki.blog.ibatis.dao"
               targetProject="../blog/src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="useActualColumnNames" value="false" />
         </daoGenerator>
 
         <!-- 소스생성에 필요한 테이블들을 지정한다. -->
         <table tableName="TB_PAY_INFO" domainObjectName="PayInfo" enableDeleteByExample="false"
               enableSelectByExample="false" enableUpdateByPrimaryKey="false" enableInsert="false">
            <property name="useActualColumnNames" value="false" />
            <property name="createDynamicSQL" value="true" />
         </table>
      </abatorContext>
   </abatorConfiguration>

build.xml file
   <?xml version="1.0" encoding="UTF-8" ?>
   <project name="blog" default="geniBatis" basedir=".">
      <property name="generated.source.dir" value="${basedir}" />
      <target name="geniBatis" description="Generate iBatis">
         <taskdef name="abator" classname="org.apache.ibatis.abator.ant.AbatorAntTask"
               classpath="lib/abator.jar" />
            <abator overwrite="true" configfile="../ibatis_plugin/abator_config.xml" verbose="false">
            <propertyset>
               <propertyref name="generated.source.dir" />
            </propertyset>
         </abator>
      </target>
   </project>

위의 abator_config.xml과 build.xml 파일을 이용하여 생성한 iBatis 소스들은 아래와 같다.

사용자 삽입 이미지

[ Abator을 이용해 iBatis 소스 생성 결과 ]

DAO : PayInfoDAO, PayInfoDAOImpl
DTO : PayInfo, PayInfoKey
SqlMap : TB_PAY_INFO_SqlMap.xml

이와 같이 Abator를 이용하면 아주 간단히 iBatis 관련 소스를 실제 DataBase와 매핑하여 생성할 수 있다. 그리고, 여기서는 아주 간단하게 Abator을 다루어 보았는데, Abator Document를 보면 configuration 설정에 더욱 많으니 꼭 보기 바란다.

Posted by as.wind.914