레이블이 Struts인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Struts인 게시물을 표시합니다. 모든 게시물 표시

박재성의 스트럿츠 프레임워크 워크북 3년만에 다시 보기 charter1 - and db by Vins

최근 스트럿츠 감을 다시 찾을 필요가 있어 예제를 인스톨 하다가
에러를 만났다 ant 최신 버젼을 다운 받아 설치하고
charter1.zip을 다운 받아 압축을 풀고 ant db 실행

다음과 같은 에러 접함

java.sql.SQLException: Communication failure during handshake. Is there a server running on 127.0.0.1:3306?

3년이 지난 후 서버 환경은 apache 2.X, tomcat 5.5.X, mysql 5.X
가장 큰 변화는 mysql 5.0으로 4.0에서 버젼 업 되어 에러가 나는 것이다.

http://dev.mysql.com/downloads/connector/j/3.1.html
위 사이트에서 jdbc 드라이버를 새로 다운 받아야 함 (우선 회원 가입 해야 다운 가능)
위 페이지에서 원도우 환경일 경우 Source and Binaries (zip) 다운 로드 함
앞축 풀고 폴더 안에 jar 파일만 카피해서 프로젝트 lib에 복사

## 프로젝트 lib 란 ##
tomcat 내 common\lib 폴더가 아니다.
지금 charter1 폴더를 빌드 할려고 하는 것이니 해당 폴더에 가서 lib\application 폴더안에
mysql-connector-java-2.0.14-bin.jar를 지우고 복사 (다른 설정 필요 없음)
복사 후 남은 jdbc 커넥터 명 (mysql-connector-java-5.0.0-beta-bin.jar) 이 버젼 이상이면 OK

드라이버가 바뀌었음으로 dbpool.properties 파일에서 drivers 부분을 변경해야 함

원문 : drivers=org.gjt.mm.mysql.Driver 이걸 지우고
교체 : drivers=com.mysql.jdbc.Driver 이걸 넣는다

바꾸고 난 후 다시 ant db 실행

이번엔 아래와 같은 에러를 만났다

BUILD FAILED
com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual
to your MySQL server version for the right syntax to use near '&'?6;4E46G8E2F8G2E8FH?GF??!(' at line 1

MySQL 설치 환경은 언어를 기본 값을 하지 않고 utf8로 설정하였다 (utf-8 아님)
이럴 경우 dbpool.properties 파일의 mysql.url 부분

즉 mysql.url=jdbc:mysql://127.0.0.1:3306/struts?useUnicode=true&characterEncoding=KSC5601 이 부분에서
마지막 인코딩 부분을 KSC5601 을 전부 지우거나 utf8로 변경 하여 실행하면 빌드 OK


완료 화면
--------------------------------------------------------------------------

C:\websource\javajigi\chapter1>ant db
Buildfile: build.xml

init:

db:
[sql] Executing resource: C:\websource\javajigi\chapter1\resources\chapter1.sql
[sql] 3 of 3 SQL statements executed successfully

BUILD SUCCESSFUL
Total time: 1 second
C:\websource\javajigi\chapter1>

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

GEF(Graphical Editing Framework), htmleditor, strutsIDE


오늘은 이클립스에서 스트럿츠를 사용하기 위한 세팅에 대해서 다루어 보겠다.


취직을 위해서는 개발 내용들을 올리는 것이 더 나을것 같다는 생각도 들지만,


누가 머래도 개발을 하려면 세팅이 우선시 되어야 하기에, 이런 저런 세팅법들을


먼저 올리게 되었다.


각종 소스들도 조만간 하나씩 하나씩 업데이트 할것이다.


각설하고, 이클립스에서 스트럿츠를 편리하게 사용하기 위해서는,


GEF(Graphical Editing Framework), htmleditor, strutsIDE가 필요하다.


 



위 사진의 사이트는 http://www.eclipse.org/gef/ 이다.


이곳에서 먼저 gef를 다운 받는다.


gef에 대해 알고 싶다면, 해당 홈페이지의 overview를 보면 이해할 수 있을 것이다.


 



두번째로 http://amateras.sourceforge.jp/cgi-bin/fswiki_en/wiki.cgi?page=EclipseHTMLEditor 에서 htmleditor를 다운 받는다.


 



strutsIDE는 https://sourceforge.jp/projects/amateras/files/?release_id=24130#24130 에서 다운 받으면 된다.


 


 



다운 받은 GEF파일의 압축을 풀면 위와 같이 eclipse폴더 밑에 여러개의 파일들이 존재하는 것을 알 수 있다. 이 파일들을 복사한다.


 



복사한 파일들을 eclipse가 설치된 폴더에 붙여 넣는다.


 



htmleditor파일의 압축을 풀면 plugin폴더 밑에 위와 같은 폴더가 보일것이다.


이것을 복사한다.


 



복사한 폴더를 eclipse설치폴더/plugins에 붙여 넣는다.


 


 



strutsIDE파일의 압축을 풀면 plugins 폴더 밑에 위 그림과 같은 폴더가 존재한다.


이 폴더를 복사한다.


 



복사한 폴더를 eclipse설치폴더/plugins에 붙여 넣는다.


 


이제 이클립스를 실행해 프로젝트를 생성해 보자.


 



이클립스를 실행하고, 톰켓프로젝트를 하나 생성해 보자.


필자는 strutsTest라고 이름 지었다.


 



프로젝트를 생성했다면, 이제 struts를 추가해 보자.


프로젝트를 클릭후 마우스 오른쪽 - new - ohter... 를 선택한다.


 



위와 같이 Amateras/struts 폴더 밑에 add struts support를 선택한다.


next를 클릭한다.


 



설정에 필요한 모든것들이 체크되어 있는 것을 확인할 수 있다.


next를 누른다.


 



tiles와 validator plugin을 사용하겠다는 것!!!


finish를 클릭한다.


 



WEB-INF아래에 위와 같이 파일들이 생성되었다면 성공한 것이다.


이제 struts를 사용하면된다.


 


이 포스팅이 스트럿츠를 시작하는 사람들에게 도움이 되었으면 한다.


자바 개발자로서 나날이 발전해 나가길 빈다.


Eclipse & Struts 환경설정

Eclipse & Struts 환경설정



jdk : http://java.sun.com/javase/downloads/index.jsp

eclipse : http://www.eclipse.org/downloads/ , http://amateras.sourceforge.jp/cgi-bin/fswiki/wiki.cgi

amateraIDE : http://amateras.sourceforge.jp/cgi-bin/fswiki_en/wiki.cgi

dbedit : http://sourceforge.net/projects/dbedit/

tomcat : http://tomcat.apache.org/



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


1. 이클립스 다운로드, 설치



test version : Eclipse SDK 3.2.1

설치 - c:\Program Files\eclipse

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

2. 톰캣 다운로드, 설치



test version : Apache Tomcat 5.5.20

설치 - c:\tomcat

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

3. Eclipse tomcat plug-in 다운로드, 설치



test verstion : tomcatPluginV31

설치 - c:\Program Files\eclipse\plugins

<상태 변화>

* 이클립스 툴바(toolbar)에 톰캣 호랑이 아이콘이 3개 생긴다.

* Tomcat Project 를 생성 할 수 있다.

// windows - preferences... 에서 tomcat 에 관한 설정을 해줘야 한다.

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

4. Tomcat administration tool 다운로드, 설치



test version : jakarta-tomcat-5.5.9-admin

설치 - c:\tomcat 에 덮어서 설치

4.1 C:\tomcat\conf\tomcat-users.xml 수정





<상태 변화>

* http://127.0.0.1:8080/admin 접근 가능해진다.

* Tomcat project - reload this context 가능 해진다.

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

5. Eclipse GEF, htmleditor plug-in 다운로드, 설치



test version : GEF-ALL-3.2.1, htmleditor_2.0.3

GEF 설치 - c:\Program Files\eclipse\ 에 덮어서 설치

htmleditor 설치 - C:\Program Files\eclipse\plugins

<상태 변화>

* new - Amateras 생김, windows - preferences.. - Amateras 생김

* html, jsp, xml 파일 생성 가능

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

6. Eclipse struts IDE plug-in 다운로드, 설치



test version : struts_IDE_2.0.3

설치 - C:\Program Files\eclipse\plugins

* new - Amateras - Struts 생김

* Add struts Support, Struts Action, Struts ActionForm, Struts JSP file 생성 가능

* struts-config.xml IDE 방식으로 작성 가능

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

혹시 여기까지 해서 위와 같이 설정이 안 보이거나 안 나올 경우
이클립스의 다른 플러그인과 충돌 가능성이 있기 때문에
예를 들어 lomboz등의 플러그인이 깔리지 않은 순수 이클립스 버젼 위에 테스트 해 보시기 바랍니다.

그렇게 하면 반듯이 나옵니다.

스트럿츠 설치 최종판 by VINS


스트럿츠 설치와 스트럿츠를 이용한 작업은 보통 어려운 일이 아니다.


모델2까지 작업을 해 봤다면 조금 접근하기가 쉬울지도 모르겠다.


하지만 자바 입문과 동시에 스트럿츠를 경험하려고 한다거나


오랫동안 자바를 떠났다가 스트럿츠를 해 보려고 한다면


그 설치에서 부터가 상당히 난관이다.


 


여기서는 원도우 베이스로 설치 및 개발 테스트를 하려고 하는


개발자들을 위해 간단 설치 버젼으로 소개하려 한다


아래는 JDK1.5, TOMCAT5.5.7, 스트럿츠1.2.8 버젼으로 진행하려 한다.


 


스트럿츠 1.2.8 다운로드 --> http://archive.apache.org/dist/struts/binaries/ 에서 struts-1.2.8-bin.zip 이걸 클릭하면 된다


 


스트럿츠 설치에 많은 에러를 만나보았고 나 역시 자바를 거의 5년만에


다시 만져보다 보니 상당히 스트럿츠 설치가 어려웠다


지난 5년동안 닷넷 진영에서 C#과 ASP.NET 개발을 해 왔으며 이전버젼의 ASP


MS-SQL, MYSQL 등 수 많은 프로젝트 개발에 참여 해 오면서


자바 진영의 설치 인터페이스 무료소스 진영에서 배포되고 있는 막대한 양의


정보를 Sun등의 기업이 책임 있게 관리와 지원을 해 나가는 모습이 있다면 어떨까 하는


생각이 들었다


위의 각 파일 중 JDK와 톰캣이 이미 설치되었다는 가정하에서 진행된다


JDK나 톰캣은 여러분이 가지고 있는 서적 또는 웹사이트에서 흔히 접할 수 있다.


그리고 스트럿츠 보다는 배경 지식이 덜 필요하기 때문에 생략하도록 하겠다.


 


우선 스트럿츠 어떤 버젼을 다운 받으셨던 압축을 풀면 %Struts%lib 폴더에 jar, tlb, dtd등


여러 파일들이 보여질 것이다.


 


첫째, jar 파일을 전부 %JAVA_HOME%\jre\lib\ext  (자바를 설치한 후 JRE를 말하는 것이 아님)


JKD가 설치 된 폴더 아래 JRE 폴더가 따로 있다 거기 복사를 해야 한다 (흔히 하는 실수1)


첫째를 완료 하면 %JAVA_HOME%\jre\lib\ext 폴더 아래는 다음과 같은 파일들이 있을 것이다.



antlr.jar
commons-beanutils.jar
commons-digester.jar
commons-fileupload.jar
commons-logging.jar
commons-validator.jar
dnsns.jar
jakarta-oro.jar
localedata.jar
servlet-api.jar
struts.jar
sunjce_provider.jar
sunpkcs11.jar


 


둘째, 첫째에서 했던 것과 같이 jar 폴더를 전부 작업하려는 프로젝트 폴더의 WEB-INF/lib 폴더로 복사를 해야 한다.


예) 설치하려는 D:\JSPSample 이라고 한다면 d:\JSPSample\WEB-INF\lib가 되는 것이다.


둘째를 완료 하면 d:\JSPSample\WEB-INF\lib 폴더 아래는 다음과 같은 jar 파일이 있다



antlr.jar
commons-beanutils.jar
commons-collections.jar
commons-digester.jar
commons-fileupload.jar
commons-logging.jar
commons-validator.jar
jakarta-oro.jar
struts.jar


이 과정에서 이렇게 똑같은 파일을 두번 카피하지 않고 classpath 설정해서 쓰는 방법이 있긴 하다


하지만 그렇게 되면 classpath를 환경설정 파일에 등록 해야 하고 상당히 지저분해 진다.


두번 카피하는 것도 오히려 프레임워크 의존적이지 않게 되기 때문에 배포가 더 쉬울것이다.


 


 


셋째, tlb, dtd 등 jar파일을 제외한 모든 파일을 개발하려고 하는 프로젝트에 /WEB-INF/tlb 폴더로 복사를 한다 (tlb 폴더가 아니라 WEB-INF에 놓아두어도 상관 없다 web.xml 파일에서 그 설정을 바꿀수 있다)


tlb 폴더를 만들었다라고 가정하면 web.xml의 태그라이브러리 부분을 다음 처럼 설정해야 한다.


아래 web.xml 파일의 파란색 부분을 참조 하자


 


<?xml version="1.0" encoding="ISO-8859-1"?>


<!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">


<web-app>
  <display-name>Struts Blank Application</display-name>
 
  <!-- Standard Action Servlet Configuration (with debugging) -->
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/config/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
    <init-param>
      <param-name>detail</param-name>
      <param-value>2</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>



  <!-- Standard Action Servlet Mapping -->
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>



  <!-- The Usual Welcome File List -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>



  <!-- Struts Tag Library Descriptors -->
  <taglib>
    <taglib-uri>/tags/struts-bean</taglib-uri>
    <taglib-location>/WEB-INF/tld/struts-bean.tld</taglib-location>
  </taglib>


  <taglib>
    <taglib-uri>/tags/struts-html</taglib-uri>
    <taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location>
  </taglib>


  <taglib>
    <taglib-uri>/tags/struts-logic</taglib-uri>
    <taglib-location>/WEB-INF/tld/struts-logic.tld</taglib-location>
  </taglib>


  <taglib>
    <taglib-uri>/tags/struts-nested</taglib-uri>
    <taglib-location>/WEB-INF/tld/struts-nested.tld</taglib-location>
  </taglib>


  <taglib>
    <taglib-uri>/tags/struts-tiles</taglib-uri>
    <taglib-location>/WEB-INF/tld/struts-tiles.tld</taglib-location>
  </taglib>


</web-app>


 


물론 위 파란 부분은 없을 수도 있다.


그것은 프로젝트를 처음 시작할 때 그럴수도 있으며 모르고 있을 수도 있다


즉 위 태그라이브러리를 설정하지 않으면 스트럿츠 정의 태그를 사용하지 않을꺼라는


것과 똑같다. 즉 스트럿츠의 반쪽만 이용하는 샘이 된다.


 


자 위와 같이 설정 했다면 뭐 어떻게 쓰는지 jsp 파일의 예문을 보도록 하자


(아래 예문은 스트럿츠 1.2.8 버젼의 예제를 그대로 이용한 것이다.


 


-----index.jsp


<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<logic:redirect forward="welcome"/>


<%--


Redirect default requests to Welcome global ActionForward.
By using a redirect, the user-agent will change address to match the path of our Welcome ActionForward.


--%>


위 index.jsp 파일의 taglib의 uri 부분을 보면 파란색으로 보이는 부분과 web.xml 파일의


파란색 부분의 세번째 <taglib-uri> 부분을 확인 해 보면 동일하다는 것을 알게된다.


이런 식으로 xml 파일에서 설저된 부분은 각 JSP 서블릿 빈즈 등 스트럿츠를 사용하는 모든 부분에서


참조하게 컨테이너(여기서는 톰캣5.5.7)가 시작될 때 읽어 들이게 된다.


J2EE완경이 이미 설치된 분들은 여기까지만 따라와도 무리가 없다


하지만 다음을 계속 보자.


 


넷째, 고작 파일 몇개 복사하고 xml 설정 해 주고 경로 맞춰주고 한 것 뿐이다.


하지만 여기서 그 다음 내용을 보지 않는 다면 다음과 같은 에러를 만날 수도 있다.


 


HTTP Status 500 -





type Exception report


message


description The server encountered an internal error () that prevented it from fulfilling this request.


exception

javax.servlet.ServletException: javax/servlet/jsp/tagext/TagSupport
..... 이하 지면상 생락~~~
이 에러난 계속 새로 고침 하다 보면 났다 안 났다 한다
그래서 완료 된 건가 뭔가 다른 설정 문제가 있는 건가 하고 착각하기도 한다.
위에서 문제점은 어떤 것이 나올지 정확하지는 않다.
하지만 위 에러의 경우는 tagext 같은 부분은 J2EE에서 지원하는 서블릿 에러이다.
 
이런 에러를 만났다면 걱정하지 말고 구글 검색창에 j2ee.jar 파일을 검색해서 다운 받도록 하자
그리고 첫번째에서 했던것 처럼 %JAVA_HOME%/jre/lib/ext 폴더에 저장(카피)한다 
그리고 톰캣 재시작~
 
 
그럼 정상적인 Welcome 화면을 만나게 될 것이다. 
여기까지 완료 했다면 당신은 Jakarta Struts를 개발할 모든 준비가 다 된 것이다.
 
다섯째, 이런 환경을 만들면서 스트럿츠에 대한 환경변수엔 특별한 것이 없다
본인의 환경 설정 관련 정보를 오픈 하면 다음과 같다.
 
CATALINA_HOME -> D:\ATJO\TOMCAT5.5.7
JAVA_HOME -> D:\ATJO\JDK
Path ->d:\ATJO\JDK\bin;%Path%
 
CATALINA_HOME 같은 경우는 톰캣이 설치된 root 폴더를 지정해 주면 된다.
JAVA_HOME 역시 JDK 설치 폴더를 지정해 두었다.
Path 경우는 좀 특별한다. 필자의 경우는 ATJO라는 폴더 밑에 전부 존재한다.
ATJO는 아파치, 톰캣, 자바(JSP) 오라클의 앞자만을 딴 것이다. 
C: 드라이브에 이런 저런 것들을 놔 두기 싫어하는 관계로 D드라이브를 이용하였다.
이 설정은 여러분 마음대로 해도 좋지만 한곳에 모아놓길 권장한다.
다시 Path가 왜 특별한지 얘기를 하면 필자의 경우는 ORACLE이 설치되어 있고 
스트럿츠를 이용하기 위해서 ORACLE이 가지고 있는 JAVA JAR 파일 보다 먼저 읽혀질 필요가 있었다
그래서 PATH환경 변수의 제일 첫 부분에 JDK의 bin 폴더의 경로를 설정한 것이다.
 
이 블로그엔 스트럿츠의 많은 자료들이 담겨 있다 
앞서간 많은 개발자들의 피땀어린 노력과 그 오픈 정신에 감사한다.

이글 서두에서도 말했지만 알고 나면 간단한 부분을 가지고
개발의 문제가 아니라. 고작 프레임워크 설치 문제에 이런 시간과 정열을 소비하게 
만드는 것 또한 오픈 진영의 문제점이다.
잘 아는 사람이 아니면(잘 모르면) 쓰지 말라는 것 처럼 보인다.
개념을 더 파악하라든지, 책을 좀 보셔야 할 것 같네요등 게시판에 조롱하듯 
게시물에 대한 답변을 달아 놓은 몇몇을 보았다
이는 MS의 소스 공개 거부나 오픈 소스라도 접근하는 경로가 찾기 어렵다는 거나 
뭐가 다른건지 모르겠다. 소스를 오픈하는 큰 개발자의 마인드처럼 그 오픈 소스를 이용하는
개발자들 역시 그런 정신을 좀 본받을 필요가 있는 것 같다.

[확장 예제] 스트럿츠를 위한 모델2 개념잡기 by VINS - Build in Concept MVC2(Model2) for Struts

앞서 스트럿츠를 위한 mvc2 개념 잡기에 있어서 MessageProcess서블릿에 고정값이 었던 부분을


값을 받아오고 처리하고 또 그 값을 request객체에 담아 화면으로 출력하는 확장 예제에 대한 확장 소스 공개 부분입니다.


 


1. 생략


2. Command.properties (한줄 더 추가)


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


/message.do=mvc2.controller.MessageProcess
/parameter.do=mvc2.controller.MessageProcessEx


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


이제 호출은  http://localhost/mvc2/parameter.do?message=vins 으로 하자


 



3. Contoller 서블릿 구현 (이하 소스, 이놈에 네이버 에디터가 깨먹지 않아야 하는데 ㅋㅋ)



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


앞선 강의에서와 소스 동일


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




설명도 생략


 


4. CommandProcess.java 구현 (이 놈도 동일)


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


 


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



 


5. MessageProcess.java -> 이 놈과 같은 역활의 새로운 파일 생성


5. MessageProcessEx.java  (EX 확장이르는 의미로 붙였음)- 소스 역시 새로운 소스임


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


package mvc2.controller;


import javax.servlet.http.*;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class MessageProcessEx implements CommandProcess {
    public String requestPro(HttpServletRequest request,HttpServletResponse response) throws Throwable {
  String message = request.getParameter("message"); //파라미터 확인
  
  Object result = null;
  if (message == null) {
   result="저는 아무개 입니다";
  } else if (message.equals("vins")) {
   result="저는 by Vins 입니다";
  } else {
   result="이런 파라미터는 무시";
  }
  
  request.setAttribute("result", result);
  request.setAttribute("myname", message);
    return "/view/messageview.jsp";
 }
}


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


리턴 되는 디스플레이 JSP하나 새로 생성


 



6. 디스플레이


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


<%@ page contentType="text/html;charset=euc-kr" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
 <title>간단한 컨트롤러(Controller)의 사용 예제</title>
</head>
<body>
 <c:set var="result" value="${requestScope.result}"/>
 <c:set var="myname" value="${requestScope.myname}"/>
 
 결과 : <c:out value="${result}"/><br/>
 이름 : <c:out value="${myname}"/>
</body>
</html>


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


 


화면 출력


 http://localhost/mvc2/parameter.do?message=vins


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


결과 : 저는 by Vins 입니다
이름 : vins


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


나머지 값들은 적절히 바꿔 보세요..


 


[1편 강의를 보시려면]


http://mcpicdtl.blogspot.com/2009/02/2-by-vins-build-in-concept-mvc2model2.html

스트럿츠를 위한 모델2 개념잡기 by VINS - Build in Concept MVC2(Model2) for Struts

모델1, 모델2 또는 MVC1, MVC2라고 표현하기도 한다.

최근 스트럿츠, 스프링 등 프레임워크가 주류를 이루고 있으며 빠르게 스트럿츠를 학습하기 위한

접근법으로 급히 스트럿츠를 익히다 보니 스트럿츠를 쓰면 모델1, 2에 비해서 어떤 이점들이 있는지

모델2의 재조명으로 바라보고자 한다

모델1,2 이런것은 자바에만 국한된 것이 아니다.

자바로 인해 유명해진 모델링 기법이긴 하지만 어떤 언어를 막론하고 모델2의 구현을 위한 불가능한 언어는 없다

다만 자바 & JSP로 구현하기가 좀 쉽다

WAS서버(여기선 톰캣)가 이를 위한 준비를 이미 마친 상태고 개발자는 web.xml 정보만 컨트롤 함으로서

쉽게 구현할 수 있게 되어 있는 것이마

NT계열의 웹 서버인 IIS에선 ISAPI 필터의 구현으로 이 기능을 구현할 수 있다,

ASP, PHP WITH WINDOW 경우 어떤 언어로 구현하더라도 우선 요청을 받아 해당하는 파일을 호출하도록

되어진 부분이 IIS이기 때문이다.

LAMP를 사용하는 경우 아파치만으로 구현을 하는 것은 조금 암울하게 보일지 모르나

아파치의 설정등 어느정도의 전체 요청의 컨트롤이 가능한 요소를 구현할 수가 있다.

하지만 위 두가지 경우 전부 "구현을 해야 한다는 것이다".

그리고 "검증되지 않았다는 것이다".

가장 부담스러운 것은 모든 요청에 대한 필터를 가해야 한다는 것이다.

해서 JSP, JAVA 만큼 활성화 되고 있진 않은 듯 하다.

물론 닷넷 진행은 2.0 이후 점차, 모델 개념을 도입하고 있는 모습이 보이기도 한다.

실제 2003년까지만 해도 모델2등의 굉장히 생소하게만 느껴졌다

이미 자기가 경험해 본 패턴임에도 용어부터가 낮설게 느껴졌었다 .

그때까지 개발의 대부분은 당시 유행하던 3-TIRE, 코바, 프록시 객체 기법등 다양한 방법으로

1.디자인-2.비지니스로직-3.데이터로직에 이르는 3-tire를 구현하기 위해 노력은 했지만

결국 1.디자인이라는 이 부분에 대한 극복이 불가능했었다

이때 이 1.디자인 이라고 하는 부분은 asp, php, jsp를 뜻한다. 요청을 받고 해당하는 비지니스 객체를 통해

데이터를 처리한 후 최요 요청 받았던 해당 파일로 output이 일어난다는 얘기다

요청과 출력 그리고 이동에 대한 모든 부분을 해당 jsp파일(나머지 언어 생략)이 담당했다는 얘기다.

뭐가 불편한지 우리는 알지 못한다.

익숙하고 간편하다고 생각했기 때문이다. 그리고 무엇보다 배우기 쉽다.

하지만 그 가장 프론트에 위치한 그 파일을 디자인 개편, 로직 변경, 유지보수를 위해 HTML코딩에 CSS까지

덕지덕지 붙은 페이지를 열어 바꾸어 주어야 하는 부분을 수정하고 재배로를 하곤 했다.

자 어떤 것들을 들어낼 수 있는지 보자

우선 프론트쪽에 위치한 jsp파일의 역활중 2가지를 들어내어 디자인 소스과 구분 할 수 있다.

1. 디자인(프론트) 역활 (<- 기존의 모델1)

1) 요청 처리

2) 프로세스 처리(페이지 이동)

3) 디스플레이 처리(DB에서 가져온 내용을 출력한 다는 등의..)

2. 모델 2 적용

1) 요청 처리를 위한 컨트롤러 (서블릿 형태의..)

2) 프로세스 처리를 위한 디스페쳐 컨트롤러 (비지니스 로직에 포함 가능)

3) 디스플레이 (디자인 페이지)

자 위를 보면 3가지 역활은 그대로 있고 하나의 통합적인 페이지에서 더이상 이루어 지지 않는다

전체 요청을 처리하는 컨트롤러를 하나 생성하고 web.xml의 설정과 연동 되도록 하기 위해

init 또는 요청 처리 메소드에서 적절한 구현을 한 컨트롤러가 이제 부터 모든 요청을 관장한다.

이전 모델) 클라이언트 -> JSP <-> 비지니스로직 <-> 데이터로직

적용 모델) 클라이언트 -> 컨트롤러(서블릿) <->비지니스로직(프로세스처리) <-> 데이터로직

디스플레이

자 컨트롤러가 요청을 처리 한 후 비지니스로직 결과와 함께 디스플레이로 포워드 시켜 버린다.

그럼 데이터로직을 처리하고 받아온 데이터 값에 대한 공유는 하고 궁금해 하실텐데

이는 Request객체에 저장을 하여 공유해야할 범위내에서 공유를 하게 된다.

이 범위라는 것이 page, request, session, application 범위가 있는데 아주 간단히만 설명하면

request에 저장을 하면 페이지가 이어지는 요청 정보를 가져갈 경우 그 Request는 계속 공유된다.

예를 들어 로그인 화면에서 아이디/패스를 입력 후 로그인 과정을 거지는 전 페이지 구성에서 그 데이터를

공유하게 된다는 것이다. page객체에 저장하면 그 페이지를 벗어남과 동시에 값은 사라지고 만다.

이해가 되었을 것이다.

그럼 디스플레이는 결국 jsp코딩이 남게 되나요?

결과 부터 말하자면 그렇다.

하지만 <%...%>로 시작되는 부분이 아닌 EL, JSTL등의 기법으로 디자이너가 코드자체에 대한 컨트롤은 불가능 하도록

다양한 기법들이 제공 되고 있다.

즉 게시판 리스트 화면을 출력하기 위해 for 또는 while문 등이 HTML소스 위를 기어다는 일이 없도록

커스텀 태그도 제공하고 있어 실제 디자인과 개발소스간의 경함(?)이 많이 줄어들었다 할수 있겠다.

그럼 구체적인 부분에 대해서 언급 하겠다. (프로젝트명 mvc2 빌드패스 WEB-INF/classes)

1. web.xml 컨트롤러 및 요청에 대한 필터링

WEB-INF 바로 아래 위치하며 아래 부분이 추가 되어 있어야 함

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

생략...

<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>mvc2.controller.Controller</servlet-class>
<init-param>
<param-name>propertyConfig</param-name>
<param-value>C:/websource/jakarta-tomcat-5.5.7/webapps/mvc2/WEB-INF/Command.properties</param-value>
</init-param>
</servlet>

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

생략...

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

해석하면 서블릿 이름은 Controller이머 mvc2.controller라는 패키지에 속한다.

컨트롤러에서 사용할 명령파라미터의 세팅 정보는 WEB-INF바로 아래 Command.properties 파일에 저정 (메모장으로 열고 저장하면 됨)

위와 같이 서블릿 정보에 대한 매칭을 시키지 위해서 페어로 존재하는

servlet-mapping의 설정은 모든 .do형식의 요청은 서블릿 Controller를 호출하도록 매칭 시킴.

(참고 여기서 do는 하다라는 의미의 실행하라는 명령으로 생각하면 됨 관용적임, 네이버 경우는 NHN을 사용하는 것 같음)

2. Command.properties (어떤 이름이라도 관계 없음, web.xml의 해당 부분과 일치만 한다면)

여기엔 단순하게 하나의 명령만 실행되도록 해 보겠다.

물론 게시판 간은 경우 list.do, write.do, modify.do 등등 처리할 파일들이 많겠지만.

여기선 아주 단순하게 파라미터로 전달 되어온 메세지하나 처리하도록 구현할 예정이므로 아래 한줄만 넣어주기 바란다.

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

/message.do=mvc2.controller.MessageProcess

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

프로젝트 메인 루트에서 message.do형식의 URL패턴이면 mvc2.controller.MessageProcess 서블릿을 호출하라는 뜻이다.

예를 들면 http://localhost/mvc2/message.do .....

3. Contoller 서블릿 구현 (이하 소스, 이놈에 네이버 에디터가 깨먹지 않아야 하는데 ㅋㅋ)

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

package mvc2.controller;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Controller extends HttpServlet
{
private java.util.Map commandMap = new java.util.HashMap();

public void init(ServletConfig config) throws ServletException {

String propertyConfig = config.getInitParameter("propertyConfig");
Properties prop = new Properties();

FileInputStream fs = null;

try
{
fs = new FileInputStream(propertyConfig);
prop.load(fs);
}
catch (IOException e)
{
throw new ServletException(e);
} finally {
if(fs!=null) try{fs.close();} catch (IOException ex){}
}

Iterator keyIter = prop.keySet().iterator();

while(keyIter.hasNext()) {
String command = (String)keyIter.next();
String className = prop.getProperty(command);

try
{
Class commandClass = Class.forName(className);
Object commandInstance = commandClass.newInstance();
commandMap.put(command, commandInstance);
}
catch (ClassNotFoundException e)
{
throw new ServletException(e);
}
catch (InstantiationException e)
{
throw new ServletException(e);
}
catch (IllegalAccessException e)
{
throw new ServletException(e);
}
}
}

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
requestPro(request, response);
}

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

private void requestPro(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
CommandProcess comp = null;
String view = null;
try
{
String command = request.getRequestURI();

if(command.indexOf(request.getContextPath())==0) {
command = command.substring(request.getContextPath().length());
}
comp = (CommandProcess)commandMap.get(command);
view = comp.requestPro(request, response);
}
catch (Throwable e)
{
throw new ServletException(e);
}

RequestDispatcher disp = request.getRequestDispatcher(view);
disp.forward(request,response);
}
}

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

초기화 메소드 init()에서 web.xml에서 필요한 정보를 읽어와 해쉬맵(HashMap)객체에 저장하는 것을 볼수 있다.

doGet, doPost는 requestPro(request, response)형식의 메소드를 내부적으로 호출하게 되어 있다.

doGet, doPost에 아무것도 없는 것은 간략하게 하기 위합니다.

필요한 보안적 처리 및 필터는 보완을 해가면 사용하길 바란다.

requestPro 메소드를 보면 첫줄에 CommandProcess comp = null; 형식을 선언하고

그 보다 몇줄 더 아해 부분에 comp = (CommandProcess)commandMap.get(command); 이 부분에서 암시적 형변환을 한다.

이 부분은 web.xml 파일에서 propertyConfig라는 파라미터의 값의 Command.properties 파일 내용일 읽고

그 서블릿의 키 값과 객체의 인스턴스 형태를 해쉬맵에 저장한다.

그럼 키에 /message.do 가 남고 값에는 mvc2.controller.MessageProcess의 인스턴스가 지정된다.

이때 주목할 것은 난데 없는 CommandProcess 와 MessageProcess 두 오브젝트이다.

어찌됐든 디스페쳐가 포워드 해야 할 경로 정보를 받아 request값과 함께 디스플레이 화면으로 보낸다 (자세한 설명은 뒤..)

4. CommandProcess.java 구현 (인터페이스)

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

package mvc2.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//요청 파라미터로 명령어를 전달하는 방식의 슈퍼 인터페이스
public interface CommandProcess {
public String requestPro(HttpServletRequest request, HttpServletResponse response)
throws Throwable;
}

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

인터페이스를 사용해 타입을 통일한다. (OOP상속의 단점을 피하고 횡적설계 인터페이스의 확장성을 고려한...)

사실 이 부분은 형변화 하지 않고 쓰면 없어도 그만이다.

뭐 전체적으로 컨트롤러의 해당 부분을 없에 버리면 그만이지만. 이 예제에서는 CommandProcess 서블릿(인터페이스)을

단 하나의 서블릿이 구현하고 있어 의미를 모를수 있으나 같은 형식의 구현체들이 여럿 있을 경우

if else / switch를 해 가며 해당 형마다 호출해 줄수는 없는 일이다. 지금 이해가 가지 않더라도 우선 따라가자 .

5. MessageProcess.java (구현체)

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

package mvc2.controller;

import javax.servlet.http.*;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class MessageProcess implements CommandProcess {
public String requestPro(HttpServletRequest request,HttpServletResponse response) throws Throwable {
request.setAttribute("message", "지금은 고정값으로 정합니다.");
return "/view/process.jsp";
}
}

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

request객체에 값을 세팅하고 난 후 3번 Contoller서블릿에서 전달해야 할 디스플레이 소스에 값을 전달하도록 리턴

그럼 컨트롤러의 requestPro메소드의 마지막 2줄이었던 RequestDispatcher가 값view를 받아 request, resposne함께

프로세스를 전힝시킨다.

RequestDispatcher disp = request.getRequestDispatcher(view);
disp.forward(request,response);

6. 디스플레이

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

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>요청 파라미터를 명령어로 전달하는 예제</title>
</head>
<body>
처리결과 : <c:set var="message" value="${requestScope.message}"/>
<c:out value="${message}"/>
</body>
</html>

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

<c:out value="${message}"/> 여기서 ${message}은 5번 MessageProcess서블릿에서

세팅했던 값이다.

자 간단하게 MVC2에 대한 개념 및 구성에 대해 알아보았다.

자바를 잘 모르고 스트럿츠로 가자라고 하면 곧 포기하게 될 것이다.

자바를 안다고 하더라도 모델을 개념 없이 스트럿츠로 가더라도 힘든 길을 가게 될 것이다.

모델2까지 이해하고 가면 돌아가는 것 같지만 결국 스트럿츠에 모델2가 어느정도 개념이 녹아 있기 때문에

결국 돌아가는 길이 빨리 학습할 수 있는 길이라는 것을 명심했으면 한다.

"계단은 한번에 한계단씩 오르듯이...." 쩝

이건 거짓말이다.

"계단은 한번에 여러 계단을 뛰어 오를수 있지만 나이는 한번에 여러살 못 먹듯이 ..."

단계를 거치는 것이 곧 지름길이다.



[확장 예제를 보고 싶은 경우]
http://mcpicdtl.blogspot.com/2009/02/2-by-vins-build-in-concept-mvc2model2_03.html

Spring, Struts 통합

다른 웹 프레임워크들과의 통합





14.1. 소개



Spring은 어떠한 자바 기반의 웹 프레임워크와도 쉽게 통합된다. 당신은 ContextLoaderListener 를 당신의 web.xml에 선언하고, 어떤 컨텍스트 파일을 로드할 것인지를 세팅하기 위해 contextConfigLocation <context-param>를 사용하기만 하면 된다.


The <context-param>:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>

The <listener>:

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

노트: Listeners는 Servlet API 2.3 버전에 추가되었다. 만약 당신이 Servlet 2.2 컨테이너를 사용한다면, 당신은 동일한 기능을 얻기 위해 ContextLoaderServlet 를 사용할 수 있다.


만약 당신이 contextConfigLocation 컨텍스트 파라미터를 명시하지 않는다면, ContextLoaderListener는 로드할 /WEB-INF/applicationContext.xml 파일을 찾을 것이다. 일단 컨텍스트 파일이 로드되면, Spring은 빈 정의에 기반하여 WebApplicationContext 객체를 생성하고 이것을 ServletContext에 담아둔다.


모든 자바 웹 프레임워크들은 Servlet API에 기반하여 만들어졌기 때문에, 당신은 Spring이 생성한 ApplicationContext를 얻기 위해 다음의 코드를 사용할 수 있다.

WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);

WebApplicationContextUtils 클래스는 편의를 위해 만들어진 것인데, 때문에 당신은 ServletContext 속성의 이름을 기억할 필요가 없다. 그것의 getWebApplicationContext() 메써드는 만약 (ApplicationContext) 객체가 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 키로 존재하지 않는다면 null을 반환할 것이다. 어플리케이션에서 NullPointerExceptions를 받는 위험을 감수하는 것보다는 getRequiredWebApplicationContext() 메써드를 사용하는 것이 낫다. 이 메써드는 ApplicationContext를 찾지 못할 경우, 예외를 던진다.


일단 당신이 WebApplicationContext를 참조하게 되면, 당신은 이름 혹은 타입으로 빈들을 가져올 수 있다. 대부분의 개발자들은 빈들을 이름으로 가져와서 그것의 구현된 인터페이스들 중 하나로 캐스팅한다.


운 좋게도 이번 장에서 다루는 대부분의 프레임워크들은 빈들을 룩업하는 방식이 매우 간단하다. 빈들을 BeanFactory로부터 가져오는 것이 쉬울 뿐만 아니라, 컨트롤러에 의존성 삽입(dependency injection)을 사용할 수 있도록 해준다. 각각의 프레임워크 섹션에서 그것의 특화된 통합 전략들에 기반하여 보다 세부적인 사항들을 설명할 것이다.






14.2. JavaServer Faces



JavaServer Faces (JSF) 는 컴포넌트 기반, 이벤트 드리븐 웹 프레임워크이다. Sun Microsystem의 JSF Overview 에 따르면, JSF 기술은 다음을 포함하고 있다.





  • UI 컴포넌트들을 표현하고 그것들의 상태를 관리하며, 이벤트들을 핸들링하고, 밸리데이션을 삽입하고, 페이지 네비게이션을 정의하고, 국제화와 접근성을 지원하는 API들의 세트



  • JSP 페이지 내에서 JavaServer Faces 인터페이스를 표현하기 위한 JavaServer Pages (JSP) 커스텀 태크 라이브러리






14.2.1. DelegatingVariableResolver



당신의 Spring 미들티어를 JSF 웹 레이어와 통합하는 가장 쉬운 방법은 DelegatingVariableResolver 클래스를 사용하는 것이다. 이 변수 처리자(variable resolver)를 당신의 어플리케이션에 설정하려면, faces-context.xml를 수정해야 한다. <faces-config> 요소를 연 이후에, <application> 요소를 추가하고 그 안에 <variable-resolver> 요소를 추가하면 된다. 변수 처리자의 값은 Spring의 DelegatingVariableResolver를 참조해야만 한다.

<faces-config>
<application>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>en</supported-locale>
<supported-locale>es</supported-locale>
</locale-config>
<message-bundle>messages</message-bundle>
</application>

Spring의 변수 처리자를 명시함으로써, 당신은 Spring 빈들을 당신이 관리하는 빈들의 프라퍼티들로 설정할 수 있다. DelegatingVariableResolver는 처음엔 값을 룩업하는 것을 기반하는 JSF 구현의 디폴트 처리자에 위임한다. 그리고나서 Spring의 루트 WebApplicationContext에 위임한다. 이것은 당신이 JSF에 의해 관리되는 빈들에 의존성을 쉽게 주입할 수 있도록 해준다.


관리되는 빈들은 faces-config.xml 파일에 정의된다. 아래는 Spring의 BeanFactory로부터 가져온 #{userManager} 빈을 어디에 정의하느냐를 보여준다.

<managed-bean>
<managed-bean-name>userList</managed-bean-name>
<managed-bean-class>com.whatever.jsf.UserList</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>userManager</property-name>
<value>#{userManager}</value>
</managed-property>
</managed-bean>

DelegatingVariableResolver는 JSF와 Spring을 통합할 때 적극 추천되는 전략이다. 만약 당신이 보다 튼튼한 통합 예시를 찾는다면, JSF-Spring 프로젝트를 볼 수 있다.






14.2.2. FacesContextUtils



커스텀 VariableResolver는 당신의 프라퍼티들을 faces-config.xml 내의 빈들과 매핑할 때 매우 잘 동작한다. 그러나, 종종 당신은 빈을 명시적으로 가로챌 필요가 있을 것이다. FacesContextUtils 클래스는 그것을 쉽게 해준다. 이 클래스는 WebApplicationContextUtils과 비슷한데, ServletContext 파라미터가 아니라 FacesContext 파라미터를 가진다는 점만 다르다.

ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());





14.3. Struts



Struts 는 자바 어플리케이션을 위한 사실상의 웹 프레임워크 그 자체이다. 이것은 주되게 Struts이 가장 먼저 릴리즈된(2001년 6월) 것 중 하나라는 사실에 기인한다. Craig McClanahan에 의해 창안된 Struts은 아파치 소프트웨어 재단에 의해 후원되는 오픈 소스 프로젝트이다. 처음부터, Struts는 JSP/Servlet 프로그래밍 패러다임을 획기적으로 간소화했고 개인 프레임워크들을 사용하던 많은 개발자들을 끌어들였다. 이것은 프로그래밍 모델을 간단하게 했으며 오픈소스였다. 그리고 이것은 이 프로젝트가 성장하고 자바 웹 개발자들 사이에 대중화될 수 있도록 하는 거대한 커뮤니티를 가졌다.


Struts 어플리케이션을 Spring과 통합하는 데는 두 가지 방법이 있다.





  • ContextLoaderPlugin를 사용하여 Spring이 Action들을 빈들로 관리하도록 설정하고 Action들의 의존성을 Spring 컨텍스트 파일에 세팅하는 방법



  • Spring의 ActionSupport 클래스를 상속해서 getWebApplicationContext() 메써드를 사용하여 Spring 관리되는 빈들을 명시적으로 가로채는 방법






14.3.1. ContextLoaderPlugin



ContextLoaderPlugin 은 Struts ActionServlet을 위해 Spring 컨텍스트 파일을 로드하는 Struts 1.1 이상 버전의 플러그인이다. 이 컨텍스트는 ContextLoaderListener에 의해 로드된 WebApplicationContext를 그것의 부모 클래스로 참조한다. 컨텍스트 파일의 디폴트 이름은 매핑된 서블릿의 이름에 -servlet.xml을 더한 것이다. 만약 web.xml에서 ActionServlet이 <servlet-name>action</servlet-name>라고 정의되었다면, 디폴트는 /WEB-INF/action-servlet.xml이 될 것이다.


이 플러그인을 설정하기 위해서는 다음의 XML을 struts-config.xml 파일의 아래쪽의 plug-ins 섹션에 추가해야 한다.

<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/>

컨텍스트 설정 파일의 위치는 "contextConfigLocation" 프라퍼티를 사용하여 임의대로 정할 수 있다.

<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/action-servlet.xml.xml,
/WEB-INF/applicationContext.xml"/>
</plug-in>

모든 컨텍스트 파일들을 로드하기 위해 이 플러그인을 사용할 수 있는데, 이것은 StrutsTestCase와 같은 테스팅 툴들을 사용할 때 유용할 것이다. StrutsTestCase의 MockStrutsTestCase는 Listeners를 초기화하지 않을 것이기 때문에, 당신의 컨텍스트 파일들을 플러그인에 담는 것이 필요하다. 이 이슈에 대해서는 버그 정리 를 참조하도록 하라.


이 플러그인을 struts-config.xml에 설정한 이후에야 Action을 Spring에 의해 관리되도록 설정할 수 있다. Spring 1.1.3은 이를 위해 두 가지 방법을 제공한다.





  • Struts의 디폴트 RequestProcessor를 Spring의 DelegatingRequestProcessor로 오버라이드한다.



  • <action-mapping>의 타입 속성에 DelegatingActionProxy 클래스를 사용한다.


이 메써드들 모두 당신이 Action들과 action-context.xml 파일에 있는 그것들의 의존성들을 관리할 수 있게 해준다. struts-config.xml과 action-servlet.xml 내의 Action들은 action-mapping의 "path"와 빈의 "name"으로 연결된다. 당신이 struts-config.xml 파일에 다음과 같은 설정을 가진다고 가정하자.

<action path="/users" .../>

그러면, 당신은 Action 빈을 "/users"라는 이름으로 action-servlet.xml 내에 정의해야만 한다.

<bean name="/users" .../>





14.3.1.1. DelegatingRequestProcessor



DelegatingRequestProcessor 를 struts-config.xml 파일에 설정하기 위해서는, <controller> 요소 내의 "processorClass"를 오버라이드해야 한다. 이 줄들은 <action-mapping> 요소에 뒤따른다.

<controller>
<set-property property="processorClass"
value="org.springframework.web.struts.DelegatingRequestProcessor"/>
</controller>

이 세팅을 추가한 후에, 당신의 Action은 그것이 무슨 타입이건 간에 자동적으로 Spring의 컨텍스트 파일에서 룩업될 것이다. 사실, 당신은 타입을 명시할 필요조차 없다. 다음의 조각코드들은 둘 다 모두 잘 동작할 것이다.

<action path="/user" type="com.whatever.struts.UserAction"/>  
<action path="/user"/>

만약 당신이 Struts의 modules 특징을 사용한다면, 당신의 빈 이름들은 반드시 모듈 접두어를 포함해야만 한다. 예를 들어, 모듈 접두어 "admin"을 가진 <action path="/user"/>로 정의된 action은 <bean name="/admin/user"/>라는 빈 이름을 가져야 한다.


노트: 만약 당신이 Struts 어플리케이션에서 Tiles를 사용한다면, 당신은 DelegatingTilesRequestProcessor 로 <controller>를 설정해야만 한다.






14.3.1.2. DelegatingActionProxy



만약 직접 작성한 RequestProcessor를 가지고 있어서 DelegatingTilesRequestProcessor를 사용할 수 없는 상황이라면, DelegatingActionProxy 를 action-mapping에서 사용할 수 있다.

<action path="/user" type="org.springframework.web.struts.DelegatingActionProxy"
name="userForm" scope="request" validate="false" parameter="method">
<forward name="list" path="/userList.jsp"/>
<forward name="edit" path="/userForm.jsp"/>
</action>

action-servlet.xml에서의 빈 정의는 직접 작성한 RequestProcessor를 쓰건, DelegatingActionProxy를 쓰건 동일하다.


만약 당신이 컨텍스트 파일에 Action을 정의한다면, 그 Action에 대해 Spring 빈 컨테이너의 모든 특징들을 사용할 수 있을 것이다. 각각의 request에 대한 새로운 Action 인스턴스를 초기화하기 위한 옵션으로 의존성 주입을 사용하는 것 등. 후자를 사용하려면 당신의 Action 빈 정의에 singleton="false"를 추가해주어야 한다.

<bean name="/user" singleton="false" autowire="byName"
class="org.example.web.UserAction"/>





14.3.2. ActionSupport 클래스들



앞에서 언급한 것처럼, WebApplicationContextUtils 클래스를 사용해서 ServletContext로부터 WebApplicationContext를 가져올 수 있다. 더 쉬운 방법은 Struts를 위한 Spring의 Action 클래스들을 상속받는 것이다. 예를 들어, Struts의 Action 클래스를 상속하는 것 대신에 Spring의 ActionSupport 클래스를 상속할 수 있다.


ActionSupport 클래스는 getWebApplicationContext()처럼 부가적인 편의 메써드들을 제공해준다. 아래의 예제는 Action에서 어떻게 이것을 사용할 수 있는지를 보여준다.

public class UserAction extends DispatchActionSupport {

public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("entering 'delete' method...");
}

WebApplicationContext ctx = getWebApplicationContext();
UserManager mgr = (UserManager) ctx.getBean("userManager");

// talk to manager for business logic

return mapping.findForward("success");
}
}

Spring은 모든 표준 Struts Action의 하위 클래스들을 포함한다. - Spring 버전은 단지 이름에 Support만을 붙혀놓았을 뿐이다.




추천하는 전략은 당신의 프로젝트에 가장 잘 맞는 접근방법을 사용하는 것이다. 하위클래스 방법은 당신의 코드를 보다 읽기 쉽게 만들어주며 어떻게 의존성들을 해결할 것인지 명확하게 알게 해준다. 반면, ContextLoaderPlugin을 사용하는 것은 컨텍스트 XML 파일에 새로운 의존성을 추가하는 것을 쉽게 해준다. 어느 쪽이던지, Spring은 두 개의 프레임워크들을 통합하기 위해 몇몇 멋진 옵션들을 제공한다.






14.4. Tapestry



Tapestry는 아파치의 자카르타 프로젝트로부터 나온 강력한 컴포넌트 지향 웹 어플리케이션 프레임워크이다.(http://jakarta.apache.org/tapestry) Spring이 그 자체의 강력한 ui 계층을 가지고 있지만, 웹 ui를 위해 Tapestry와의 조합을 사용하여 J2EE 어플리케이션을 개발하는 것은 독특한 몇가지 장점들이 있다. 이 문서는 이 두 개의 프레임워크들을 조합하기 위한 몇 가지 최선의 실습예제들을 상세시 설명할 것이다. 당신이 Tapestry와 Spring 프레임워크 기본 둘 다에 대해 어느정도 친숙하다고 전제하에서 그것들에 대해서 여기서 설명하지는 않을 것이다. Tapestry와 Spring에 대한 일반적인 소개글은 각각의 웹 사이트들에서 접할 수 있다.






14.4.1. 아키텍쳐



Tapestry와 Spring으로 개발된 전형적인 계층적 J2EE 어플리케이션은 최상위 UI 계층은 Tapestry로, 많은 하위 계층들은 하나 혹은 그 이상의 Spring 어플리케이션 컨텍스트에 의해 관리되는 형태로 구성될 것이다.





  • 사용자 인터페이스 계층


    - 사용자 인터페이스와 관련있다.


    - 몇몇 어플리케이션 로직을 포함한다.


    - Tapestry에 의해 제공된다.


    - Tapestry를 경유하여 UI를 제공하는 것은 별개로 하고, 이 계층에서의 코드는 Service 계층의 인터페이스들을 구현한 객체들을 경유하여 작동한다. 이러한 서비스 계층 인터페이스들을 구현한 실질적인 객체들은 Spring 어플리케이션 컨텍스트로부터 가져온다.



  • 서비스 계층


    - 어플리케이션 특화된 '서비스' 코드


    - 도메인 객체들과 작동하며 그 도메인 객체들을 몇몇가지 저장소(database)에 넣고 빼기 위해 Mapper API를 사용한다.


    - 하나 혹은 그 이상의 Spring 컨텍스트에 의해 관리된다.


    - 이 계층에서의 코드는 도메인 모델 내에서 어플리케이션 특화의 형태로 객체들을 조작한다. 그것은 이 계층 내의 다른 코드와 Mapper API를 통해 작동한다. 이 계층의 객체는 그것이 작동할 때 필요한 특정한 매퍼 구현체들을 Spring 컨텍스트를 통해 받는다.


    - 이 계층의 코드는 Spring 컨텍스트에서 관리되기 때문에, 그 자신의 트랜잭션들을 관리하는 것 대신에, Spring 컨텍스트에 의해 트랜잭션화될 것이다.



  • 도메인 모델


    - 이 도메인에 특화된 데이터와 로직을 다루는 도메인 특화된 객체 계층구조이다.


    - 비록 도메인 객체 계층이 어딘가 저장되고 이것에 대한 몇가지 일반적인 특권(예를 들어, 양방향적인 관계들) 가진다는 개념에 기반하여 만들어진 것이지만, 이것은 일반적으로 다른 계층들에 대해 전혀 알 필요가 없다. 그렇기 때문에, 이것은 독자적으로 테스트될 수 있고, 생산과 테스팅을 위해 서로 다른 매핑 구현체들과 함께 사용될 수 있다.


    - 이 객체들은 독립형이거나 Spring 어플리케이션과 결합되어 사용된다. 이것은 고립성, IoC, 다른 전략적 구현체 등등의 컨텍스트의 몇가지 이점들을 가지게 한다.



  • 데이터 소스 계층


    - (데이터 접근 객체 -DAO 라고도 불리는) Mapper API : 도메인 모델을 몇 가지 (일반적으로 DB이지만 파일시스템, 메모리 등도 될 수 있는) 저장소에 저장할 때 사용되는 API


    - Mapper API 구현체들 : 하나 혹은 그 이상의 Mapper API의 특화된 구현체들, 예를 들어, Hibernate 특화 매퍼, JDO 특화 매퍼, JDBC 특화 매퍼 또는 메모리 메퍼 등


    - 매퍼 구현체들은 하나 혹은 그 이상의 Spring 어플리케이션 컨텍스트에 존재한다. 서비스 계층 객체는 컨텍스트를 통해 작동시 필요한 매퍼 객체들을 받는다.



  • 데이터베이스, 파일시스템 혹은 다른 저장소들


    - 도메인 모델 내의 객체들은 하나 혹은 그 이상의 매퍼 구현체들에 의해 하나 이상의 저장소에 저장된다.


    - 저장소는 파일시스템처럼 매우 간단할 수도 있고, DB에서의 스키마처럼 도메인 모델로부터 자신만의 데이터 표현을 가질 수도 있다. 그러나, 다른 계층들에 대해서는 알지 못한다.






14.4.2. 구현체



유일한 실질적인 질문(이 문서에 의해 설명될 필요가 있는)은 어떻게 Tapestry pages가 Spring 어플리케이션 컨텍스트의 인스턴스로 정의된 빈들인 서비스 구현체들에 접근할 수 있느냐이다.






14.4.2.1. 샘플 어플리케이션 컨텍스트



우리가 xml 형태로 다음과 같은 간단한 어플리케이션 컨텍스트 정의를 가지고 있다고 가정하자.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>

<!-- ========================= GENERAL DEFINITIONS ========================= -->

<!-- ========================= PERSISTENCE DEFINITIONS ========================= -->

<!-- the DataSource -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:DefaultDS</value></property>
<property name="resourceRef"><value>false</value></property>
</bean>

<!-- define a Hibernate Session factory via a Spring LocalSessionFactoryBean -->
<bean id="hibSessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>

<!--
- Defines a transaction manager for usage in business or data access objects.
- No special treatment by the context, just a bean instance available as reference
- for business objects that want to handle transactions, e.g. via TransactionTemplate.
-->
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
</bean>

<bean id="mapper"
class="com.whatever.dataaccess.mapper.hibernate.MapperImpl">
<property name="sessionFactory"><ref bean="hibSessionFactory"/></property>
</bean>

<!-- ========================= BUSINESS DEFINITIONS ========================= -->

<!-- AuthenticationService, including tx interceptor -->
<bean id="authenticationServiceTarget"
class="com.whatever.services.service.user.AuthenticationServiceImpl">
<property name="mapper"><ref bean="mapper"/></property>
</bean>
<bean id="authenticationService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="authenticationServiceTarget"/></property>
<property name="proxyInterfacesOnly"><value>true</value></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<!-- UserService, including tx interceptor -->
<bean id="userServiceTarget"
class="com.whatever.services.service.user.UserServiceImpl">
<property name="mapper"><ref bean="mapper"/></property>
</bean>
<bean id="userService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref bean="userServiceTarget"/></property>
<property name="proxyInterfacesOnly"><value>true</value></property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

</beans>

Tapestry 어플리케이션 내에서, 우리는 이 어플리케이션 컨텍스트를 로드할 필요가 있고, Tapestry pages로 하여금 AuthenticationService와 UserService 인터페이스를 각각 구현하고 있는 authenticationService와 userService 빈들을 가지게끔 해야 한다.






14.4.2.2. Tapestry pages에서 빈들을 얻어오기



이 지점에서, Spring의 정적 유틸리티 함수인 WebApplicationContextUtils.getApplicationContext(servletContext)를 호출함으로써 웹 어플리케이션은 어플리케이션 컨텍스트를 사용할 수 있다. 여기에서의 servletContext는 J2EE 명세의 표준 ServletContext를 의미한다. 그렇기 때문에, 페이지가 예를 들어 UserService 인스턴스를 얻기 위한 한가지 간단한 방법은 다음과 같은 코드를 가지는 것이다.

    WebApplicationContext appContext = WebApplicationContextUtils.getApplicationContext(
getRequestCycle().getRequestContext().getServlet().getServletContext());
UserService userService = (UserService) appContext.getBean("userService");
... some code which uses UserService

이러면 된다. 이런 방식은 page 혹은 component를 위한 베이스 클래스의 메써드에 있는 대부분의 기능들을 캡슐화함으로써 난잡함을 최소화할 수 있다. 그러나, 몇 가지 관점에서 이 방식은 Spring이 장려하고, 이 어플리케이션의 다른 계층에서 사용되고 있는 IoC 접근방식에 반대된다. IoC 접근방식 내에서는 이상적으로는 page가 컨텍스트에 이름으로 특정한 빈을 요청하지 않아야 하며, 사실 이상적으로는 page는 컨텍스트에 대해 전혀 몰라야 한다.


운 좋게도, 이것을 가능하게 하는 방식이 있다. 우리는 Tapestry가 이미 page에 프라퍼티를 선언적으로 추가하는 메커니즘을 가지고 있다는 사실에 의존한다. 그리고 이러한 선언적 형태로 page에 대한 모든 프라퍼티들을 관리하는 앞서 언급한 접근방식이 사실상 사용되기 때문에, Tapestry는 그것들의 생명주기를 page와 component 생명주기의 일부로 잘 관리할 수 있다.






14.4.2.3. 어플리케이션 컨텍스트를 Tapestry에 드러내기



먼저 Tapestry page 혹은 Component가 ServletContext를 가지지 않은 채로 ApplicationContext를 사용할 수 있도록 만들 필요가 있다. 이것은 page 혹은 component의 생명주기 내에서 ApplicationContext에 접근할 필요가 생기는 단계에서, ServletContext는 page에서 쉽게 사용하기 힘들기 때문에 우리는 WebApplicationContextUtils.getApplicationContext(servletContext) 를 직접 사용할 수 없다. 하나의 방법은 이것을 드러내주는 Tapestry IEngine의 커스텀 버전을 정의하는 것이다.

package com.whatever.web.xportal;
...
import ...
...
public class MyEngine extends org.apache.tapestry.engine.BaseEngine {

public static final String APPLICATION_CONTEXT_KEY = "appContext";

/**
* @see org.apache.tapestry.engine.AbstractEngine#setupForRequest(org.apache.tapestry.request.RequestContext)
*/
protected void setupForRequest(RequestContext context) {
super.setupForRequest(context);

// insert ApplicationContext in global, if not there
Map global = (Map) getGlobal();
ApplicationContext ac = (ApplicationContext) global.get(APPLICATION_CONTEXT_KEY);
if (ac == null) {
ac = WebApplicationContextUtils.getWebApplicationContext(
context.getServlet().getServletContext()
);
global.put(APPLICATION_CONTEXT_KEY, ac);
}
}
}

이 엔진 클래스는 Spring 어플리케이션 컨텍스트를 Tapestry 어플리케이션의 '글로벌' 객체에 "appContext"라는 속성으로 위치시킬 것이다. 이 특별한 IEngine 인스턴스가 Tapestry 어플리케이션을 위해 사용된되었다는 사실을 Tapestry 어플리케이션 정의 파일에 시작지점으로 등록하는 것을 확실히 해야 한다. 예를 들어보자.

file: xportal.application:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<application
name="Whatever xPortal"
engine-class="com.whatever.web.xportal.MyEngine">
</application>






14.4.2.4. Component 정의 파일들



이제 ApplicationContext로부터 우리가 필요한 빈들을 가져오기 위해 page 혹은 component 정의 파일(*.page 혹은 *.jwc)에서 우리는 단순히 property-specification 요소들을 추가하고 그것들을 위한 page 혹은 component 프라퍼티들을 생성하면 된다. 예시를 보자.

    <property-specification name="userService"
type="com.whatever.services.service.user.UserService">
global.appContext.getBean("userService")
</property-specification>
<property-specification name="authenticationService"
type="com.whatever.services.service.user.AuthenticationService">
global.appContext.getBean("authenticationService")
</property-specification>

property-specification 내의 OGNL(Original?) 표현은 프라퍼티를 위한 초기값을 컨텍스트로부터 얻어온 빈으로 나타낸다. 전체 page 정의는 아마 다음과 같을 것이다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<page-specification class="com.whatever.web.xportal.pages.Login">

<property-specification name="username" type="java.lang.String"/>
<property-specification name="password" type="java.lang.String"/>
<property-specification name="error" type="java.lang.String"/>
<property-specification name="callback" type="org.apache.tapestry.callback.ICallback" persistent="yes"/>
<property-specification name="userService"
type="com.whatever.services.service.user.UserService">
global.appContext.getBean("userService")
</property-specification>
<property-specification name="authenticationService"
type="com.whatever.services.service.user.AuthenticationService">
global.appContext.getBean("authenticationService")
</property-specification>

<bean name="delegate" class="com.whatever.web.xportal.PortalValidationDelegate"/>

<bean name="validator" class="org.apache.tapestry.valid.StringValidator" lifecycle="page">
<set-property name="required" expression="true"/>
<set-property name="clientScriptingEnabled" expression="true"/>
</bean>

<component id="inputUsername" type="ValidField">
<static-binding name="displayName" value="Username"/>
<binding name="value" expression="username"/>
<binding name="validator" expression="beans.validator"/>
</component>

<component id="inputPassword" type="ValidField">
<binding name="value" expression="password"/>
<binding name="validator" expression="beans.validator"/>
<static-binding name="displayName" value="Password"/>
<binding name="hidden" expression="true"/>
</component>

</page-specification>






14.4.2.5. abstract accessors 추가하기



이제 page 혹은 component 자체를 위한 Java 클래스 정의를 위해, 우리가 정의한 프라퍼티들에 접근하기 위한 추상 getter 메써드를 추가해야 한다. page 또는 component가 실제로 Tapestry에 의해 로드될 때, 그것은 정의된 프라퍼티들을 추가하기 위해 클래스파일에 런타임 코드 방식을 실행하고, 새롭게 생성된 필드들에 대한 추상 getter 메써드들을 연결한다. 다음의 예를 보자.

    // our UserService implementation; will come from page definition
public abstract UserService getUserService();
// our AuthenticationService implementation; will come from page definition
public abstract AuthenticationService getAuthenticationService();

For completeness, the entire Java class, for a login page in this example, might look like this:

package com.whatever.web.xportal.pages;

/**
* Allows the user to login, by providing username and password.
* After successfully logging in, a cookie is placed on the client browser
* that provides the default username for future logins (the cookie
* persists for a week).
*/
public abstract class Login extends BasePage implements ErrorProperty, PageRenderListener {

/** the key under which the authenticated user object is stored in the visit as */
public static final String USER_KEY = "user";

/**
* The name of a cookie to store on the user's machine that will identify
* them next time they log in.
**/
private static final String COOKIE_NAME = Login.class.getName() + ".username";
private final static int ONE_WEEK = 7 * 24 * 60 * 60;

// --- attributes

public abstract String getUsername();
public abstract void setUsername(String username);

public abstract String getPassword();
public abstract void setPassword(String password);

public abstract ICallback getCallback();
public abstract void setCallback(ICallback value);

public abstract UserService getUserService();

public abstract AuthenticationService getAuthenticationService();

// --- methods

protected IValidationDelegate getValidationDelegate() {
return (IValidationDelegate) getBeans().getBean("delegate");
}

protected void setErrorField(String componentId, String message) {
IFormComponent field = (IFormComponent) getComponent(componentId);
IValidationDelegate delegate = getValidationDelegate();
delegate.setFormComponent(field);
delegate.record(new ValidatorException(message));
}

/**
* Attempts to login.
*
* <p>If the user name is not known, or the password is invalid, then an error
* message is displayed.
*
**/
public void attemptLogin(IRequestCycle cycle) {

String password = getPassword();

// Do a little extra work to clear out the password.

setPassword(null);
IValidationDelegate delegate = getValidationDelegate();

delegate.setFormComponent((IFormComponent) getComponent("inputPassword"));
delegate.recordFieldInputValue(null);

// An error, from a validation field, may already have occurred.

if (delegate.getHasErrors())
return;

try {
User user = getAuthenticationService().login(getUsername(), getPassword());
loginUser(user, cycle);
}
catch (FailedLoginException ex) {
this.setError("Login failed: " + ex.getMessage());
return;
}
}

/**
* Sets up the {@link User} as the logged in user, creates
* a cookie for their username (for subsequent logins),
* and redirects to the appropriate page, or
* a specified page).
*
**/
public void loginUser(User user, IRequestCycle cycle) {

String username = user.getUsername();

// Get the visit object; this will likely force the
// creation of the visit object and an HttpSession.

Map visit = (Map) getVisit();
visit.put(USER_KEY, user);

// After logging in, go to the MyLibrary page, unless otherwise
// specified.

ICallback callback = getCallback();

if (callback == null)
cycle.activate("Home");
else
callback.performCallback(cycle);

// I've found that failing to set a maximum age and a path means that
// the browser (IE 5.0 anyway) quietly drops the cookie.

IEngine engine = getEngine();
Cookie cookie = new Cookie(COOKIE_NAME, username);
cookie.setPath(engine.getServletPath());
cookie.setMaxAge(ONE_WEEK);

// Record the user's username in a cookie

cycle.getRequestContext().addCookie(cookie);

engine.forgetPage(getPageName());
}

public void pageBeginRender(PageEvent event) {
if (getUsername() == null)
setUsername(getRequestCycle().getRequestContext().getCookieValue(COOKIE_NAME));
}
}






14.4.3. 개요



이 예제에서, 우리는 Spring ApplicationContext에 정의된 서비스 빈들을 선언적 형태로 page에 제공되도록 처리해왔다. page 클래스는 서비스 구현체들이 어디에서 왔는지 알지 못하며, 때문에 예를 들어, 테스트동안 다른 구현체로 살짝 빠져나가기 쉽다. 이러한 IoC는 Spring 프레임워크의 주요한 목적과 이점의 하나이며, 우리는 이 Tapestry 어플리케이션에서의 J2EE 스택을 향한 모든 지점에서 이것을 확장하도록 있도록 처리해왔다.






14.5. WebWork



WebWork 는 간단함을 염두에 두고 설계된 웹 프레임워크이다. 이것은 일반적인 command 프레임워크인 XWork 를 기반으로 개발되었다. XWork 역시 IoC 컨테이너를 가지고 있지만, 이것은 Spring만큼 총체적인 특징을 가지고 있지 못하며, 이 부분에서 그것을 다루지는 않을 것이다. WebWork 컨트롤러들은 Actions로 불리는데, 그것은 Action 인터페이스를 구현해야만 하기 때문일 것이다. ActionSupport 클래스는 이 인터페이스를 구현하는데, WebWork action들을 위한 일반적인 부모 클래스이다.


WebWork는 xwork-optional 프로젝트의 java.net에 위치한 자체 Spring 통합 프로젝트를 유지한다. 현재적으로, WebWork와 Spring을 통합하는데는 세 가지의 선택이 가능하다.





  • SpringObjectFactory: XWork의 디폴트 ObjectFactory 를 오버라이드 해서 XWork는 Spring 빈들을 루트 WebApplicationContext에서 찾을 것이다.



  • ActionAutowiringInterceptor: Action의 의존성들을 그것들이 생성될 때 인터셉터를 사용하여 자동으로 묶는다.



  • SpringExternalReferenceResolver: <action> 요소의 <external-ref> 요소에서 정의된 이름에 기반하여 Spring 빈들을 룩업한다.


이 모든 전략들은 WebWork's Documentation 에서 보다 상세하게 설명되어 있다.


Struts 빠른 시작






Struts 빠른 시작


Last Revision: January 21, 2005  번역 : 김종대





차 례



  1. 서두
  2. 개요
  3. 사양
  4. 새로운 프로젝트 설정 & 구조
  5. Struts 프로젝트 시작하기 

    1. Struts 프로젝트의 컴포넌트들
    2. 우리의 어플리케이션 빌드하기 

  6. 데모 실행
  7. 요약
  8. 사용자 피드백  




1. 서두


이 문서는  Sun JDK 1.4.2, Eclipse 3.0.1, MyEclipse 3.8.2를 사용하여 작성되었다. 모든 스크린 샷은 Eclipse,  MyEclipse Enterprise Workbench, Windows XP의 디폴트 유저 인터페이스 설정들에 기초한다. 이 문서의 지침에 어려움을 겪을 경우 MyEclipse 문서화 팀에 피드백을 제공하는 방법으로 사용자 피드백 절을 보길 바란다.





2. 개요


이 튜토리얼에서 우리가 MyEclipse Enterprise Workbench를 사용하여 간단한 Struts 예제 어플리케이션의 개발과 테스트를 경험하게 될 것이다. Struts 와/또는 MyEclipse에 관한 사전 지식은 필요하지 않다.

프레임웍의 유사성과 특징 적용 범위 때문에, 이 튜토리얼은 JSF 튜토리얼과 매우 유사할 것이다. 일단 당신이 MyEclipse에서 Struts 도구들을 사용하는 방법을 더 잘 이해했다면 우리는 동일한 프로젝트 목적을 가지고 당신이 나중에 Struts에 대해 JSF와 비교할 수 있도록 윤곽 지울 것이다.





3. 사양


아래는 이 가이드에 사용된 소프트웨어의 목록이다: 







  • 이 데모를 위한 사용자 이름은 "myeclipse"이고 패스워드는 "myeclipse"이다.

노트:  JDK 설치 후에 당신의 컴퓨터를 재시작하고, Eclipse, MyEclipse, Tomcat을 설치하라. MyEclilpse를 설치하는 지침들은 설치 빠른 시작을 보길 바란다. 모든 소프트웨어가 설치된 후에, 당신이 예제 어플리케이션을 배치하고 실행할 수 있기 전에 어플리케이션 빠른 시작에 상세 설명된 대로 MyEclipse 내에서 Tomcat5 커넥터를 설정할 필요가 있을 것이다.





4. 새로운 프로젝트 설정 & 구조



우리의 개발 산출물들을 구성하기 위해, 우리는 MyEclipse에서 Struts Capabilities 를 추가한 새로운 Web Module Project 를 생성시킬 필요가 있을 것이다. 당신은 아래 그림1에 보이듯이 File > New > Other > Project > J2EE > Web Project에서 마법사를 사용하여 웹 프로젝트를 생성시킬 수 있다.


그림 1 - Web Project Wizard Dialog

이제 그림2에 보이듯이 모든 새로운 프로젝트 정보를 입력하라:


그림 2 - Web Project 설정들

일단 웹 프로젝트가 생성되면, 우리는 그것에 Struts Capabilities를 추가할 필요가 있다. 이것은 그림3에 보이듯이  패키지 탐색기 뷰에서 우리의 프로젝트를 마우스 오른쪽 버튼 클릭하고 MyEclipse > Add Struts Capabilities를 선택함으로써 행해진다.


그림 3 - Adding Struts Capabilities to a Web Project

Struts 대화상자에 대한 디폴틀 값들은 이 튜토리얼의 용으로 세밀하다. 하지만 당신은 당신이 원하는 위치를 반영하도록 Base package for new classes 를 변경하고자 원할 수 있다. 아래 그림4에서 우리는 단순히 디폴트 값들을 남겨둘 것이다.


그림 4 - Struts Capabilities 구성하기

마법사가 완료된 후, 프로젝트 구조는 그림5에 보이는 것과 같을 것이다.


그림 5 - 구성 후의 프로젝트 레이아웃

이제 우리는 우리의 프로젝트 생성을 행하고 우리는 다음 절에 설명된 대로 우리의 웹 컨텐트  빌드를 시작할 수 있다.





5. Struts 프로젝트 시작하기


이 절에서 우리는 간단한 웹 사이트 로그인 화면을 모사하게 될 우리의 예제 Struts 어플리케이션을 생성시키는데 집중할 것이다. 결과적으로 우리는 오직 두 개의 JSP 페이지들 만을 필요로 할 것이다. 즉 로그인을 위해 사용자에게 프롬프트하는 페이지와 로그인이 성공적이었음을 나타내는 페이지. 우리는  이들 페이지들을 각각 oginUser.jsp 와 loginUserSuccess.jsp로 명명할 것이다. 단순함을 위해 만일 로그인 시도 동안에 인가 오류가 존재할 경우, 우리는 사용자에게 페이지로 다시 되돌아가도록 리다이렉트 시킬 것이고 오류 메시지를 디스플레이 할 것이다. 

Struts 프로젝트를 시작할 때, 모든 조각들이 함께 적합하게 될 방법에 관한 더 나은 아이디어를 개발팀이 가질 수 있도록 당신의 어플리케이션의 플로우를 레이아웃 하는 것이 일반적으로 유용하다. 플로우를 파내는 가장 쉬운 방법은 Struts Editor의 그래픽 모드를 사용하여 그것을 그래픽으로 생성시키는 것이다.  Struts Editor는 당신의 설계 실물 모형들로부터 페이지 플로우를 빠르게 모사함으로써 당신의 어플리케이션을 빌드를 시작할 수 있도록 해주는 드래그-앤-드롭 판넬을 제공한다.  예제 로그인 어플리케이션의 플로우는 아래 그림 6과 같다.

노트: 어플리케이션 플로우의 스크린 샷은 Struts Designer를 사용하여 빌드되었다. 우리는 다음 절에서 그것에 액세스하는 방법과 그것을 사용하는 방법에 고나한 상세를 다를 것이다.


그림 6 - 예제 어플리케이션 플로우

이 디자인 레이아웃에서, 우리는 어플리케이션이 userLogin.jsp 페이지를 디스플레이 함으로써 시작될 것임을 알 수 있다.로그인 페이지는 로그인 오퍼레이션을 수행하기 위해 userLogin 액션을 호출할 것이다.  만일 우리가 어떤 유효성 검사 오류 또는 문제점들을 가질 경우, userLogin 액션은 userLogin.jsp 페이지로 사용자 피드백을 포워드 할 게 될 것이다. 하지만 만일 로그인이 성공적이었다면, 어플리케이션은 userLoginSuccess.jsp 페이지로 포워드 될 것이다.  





5.1 Struts 프로젝트의 컴포넌트들


일반적인 Struts 프로젝트는 개발 산출물들의 다음 카테고리들로 구성될 것이다:



  • JSP들


  • Action들


  • ActionForward들*


  • ActionForm들**


  • Struts 배치 디스크립터: struts-config.xml

* ActionForward들은 그것이 컴파일 되었을 때 path와 Action이 무엇을 취할 것인지를 정의하는 struts-config.xml 파일 내의 <forward> 엔트리들이다.  우리는 ActionForward 클래스의 맞춤형 구현들을 참조하지 않는다. 비록 개선된 사용자들을 위해 이것들이 가능하고 도구들에 의해 지원될 지라도.

** ActionForm들은 사용자가 페이지 값들을 포장하기 위해 concrete ActionForm 구현들을 생성시키고자 원하지 않을 경우에 DynaForms의 사용으로 대체될 수 있다. 

MyEclipse에서 (struts-config.xml 파일을 제외하고) 어떤 또는 이들 모든 컴포넌트들을 생성시키는 것은  3가지 다른 방법들을 통해 행해질 수 있다: 

방법 #1: 아래 그림7에 보이듯이 Struts 마법사를 선택하여 File > New > Other... > J2EE > Struts 1.1 (or 1.0) 메뉴를 선택한다.:


그림 7 - 모든 이용 가능한 Struts 마법사들

마법사들은 직관적이고 주어진 Struts 컴포넌트가 지원하는 모든 값들을 당신에게 프롬프트 하게 될 것이다.  이들 마법사들 중 몇몇은 다른 것들 보다 훨씬 더 복잡하다. 예를 들어 아래 그림8에 보이는 Struts Action 마법사에서, 당신은 Struts Action에 의해 지원되는 모든 특징들의 넓은 적용 범위를 볼 수 있다:


그림 8 - New Struts Action 마법사

방법 #2: Struts Editor의 소스 뷰 판넬을 디스플레이 하는 것이 활성화된 편집 컨텍스트일 때 맞춤형 Outline View를 사용한다. Outline View에서, 당신은 그 타입의 새로운 컴포넌트들을 생성시키기 위한 마법사를 활성화 시키기 위해 또는 마법사들을 사용하여 기존의 어떤 것들을 편집하기 위해 루트 레벨 노드들의 어떤 것을 마우스 오른쪽 클릭 할 수 있다.  그림 9는 이들 컨텍스트 마법사들을 사용하는 예제를 디스플레이 한다. 


그림 9 - Outline View로부터 Struts 마법사들을 띄우기

액션들중 몇몇은 실제로 전체 플로우를 생성시키는 것을 당신에게 허용해주는 일련의 논리적으로 관련된 마법사들임을 노트하는 것이 가치가 있다. 마법사들은 공통 값들을 이음매 없이 재사용 가능하도록 그리고 수작업 재입력을 최소화 하도록 함께 연결되어 있다.

방법 #3: 그림10에 보이는 Struts Editor는 또한 Struts 산출물들을 생성시키는 매우 편리한 방법이다. 디자이너는 struts-config.xml 파일을 열어서 액세스 된다.  편집기의 하단에서 당신은 디자이너를 띄우기 위해 Design 탭을 클릭할 수 있다. 


그림 10 - Accessing the Struts Designer

디자이너로 전환한 후에 당신은 아래 그림 11과 같이 보이는 뷰를 보게 될 것이다:


그림 11 - Struts Designer의 개요

이제 우리는 Struts 프로젝트의 다른 컴포넌트들을 생성시키는 방법을 다루었고, 우리는 이제 다음 절에 보이듯이 우리의 로그인 데모 어플리케이션의 다른 부분들을 생성시킬 수 있다.





5.2 어플리케이션 빌드하기


우리는 JSP 페이지들을 생성시키는 것에 먼저 집중함으로써 데모 어플리케이션의 생성을 시작할 것이다. 우리의 데모 어플리케이션이 웹 사이트 상의 로그인 화면을 모사할 예정이므로, 우리는 오직 2개의 JSP 페이지들 만을 필요로 한다:  loginUser.jsp 과 loginUserSuccess.jsp. 대부분의 Struts 어플리케이션들의 경우처럼, 만일 로그인 동안에 어떤 것이 잘못될 경우, 우리는 사용자에게 loginUser.jsp 페이지를 다시 전송할 것이고 오류를 디스플레이 할 것이다(이것은 왜 우리가 loginUserFailure.jsp 페이지를 만들 필요가 없는가 하는 이유이다).

우리는 먼저 우리의 loginUserSuccess.jsp 페이지를 생성시킴으로써 시작할 것이다. 이것은 역으로 마지막 페이지를 먼저 생성시키는 것으로 보이겠지만 우리는 우리가 관련된 Action과 ActionForm을 가진 첫번째 JSP 페이지를생성시키기 위한 New Form, Action and JSP 마법사를 사용할 수 있도록 이 방법으로 그것을 행할 것이다.

먼저 그것을 클릭하여 "JSP" 판넬을 사용하고 우리의 캔버스를 클릭하여 우리의 디자이너 뷰로부터 loginUserSuccess.jsp JSP 페이지를 생성시키자. 아래 그림12의 안내를 따르라:


그림 12 - Designer를 사용하여 JSP 생성하기

캔버스를 클릭한 후에, 우리는 그림 13에 보이는 것처럼 익숙한 New JSP 대화상자를 프롬프트 받을 것이다.

노트: "Standard JSP using Struts 1.1" 템플릿을 사용하여 을 선택하도록 하라.


그림 13 - JSP 마법사 구성하기

After clicking Finish, the design view, in 그림 14, will show the newly created page.

노트: 새로운 JSP 페이지를 추가한 후에, MyEclipse는 JSP 편집기 내에 새로운 JSP 페이지를 열게 될 것이고, 아래 스크린 샷에서 우리는 당신의 어플리케이션이 시작되는 것이 무엇인지를 당신에게 보여주기 위해 디자이너를 클릭했다. 만일 당신이 JSP 페이지를 생성할 때 이것이 그 플로우가 아닐 경우에 혼동하지 말라. 


그림 14 -  우리의 JSP 페이지를 보여주는 Struts Designer

JSP 페이지를 완료하기 위해 남은 모든 것은 로그인이 성공적이었음을 사용자에게 통보하는 메시지를 출력하는 것이다. 페이지에 대한 전체 소스는 아래 그림 14a에 보여져 있다.

노트: 안내를 더 쉽게 만들기 위한 용도로 아래 JSP 페이지는 당신이 처음 그 파일을 열었을 때 당신이 갖게 될 디폴틀 JSP와 닮지 않고, 당신이 새로운 JSP 파일을 생성시킨 후에 코드를 정확하게 복사하거나 디폴트 JSP 템플릿 코드를 채택하는 것이 자유롭다. 







userLoginSuccess.jsp

<%@ page language= "java"%>

<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-template" prefix="template" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-nested" prefix="nested" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html:html locale="true">
  <head>
    <title>My Struts 'userLoginSuccess.jsp' ending page</title>
  </head>
 
  <body>
    Hello <bean:write name="userName" scope="request" />, you successfully logged in!
  </body>
</html:html>
그림 14a - userLoginSuccess.jsp 코드

이 페이지가 매우 간단함을 주목하라. 우리가 여기서 행해야 할 유일하게 중요한 것은 우리의 어플리케이션의 요청 scope 내에 저장되는 userName 변수의 값을 프린트하는 <body> 태그의 내용이다. 따라서 우리가 나중에 생성시키게 될 우리의 액션에서, 우리는 요청 scope 내에 userName 이름의 속성을 위치지울 필요가 있다.

이제 우리는 여전히 userLogin.jsp 페이지, ActionForm 그리고 Action을 생성시킬 필요가 있다. 이것은 많은 작업인양 보이겠지만, MyEclipse는 New Form 마법사와 New Form, Action and JSP 마법사를 통해 이것을 현격하게 능률화 시킬 수 있다.

userLogin.jsp 페이지를 생성시킬 때 우리는 우리가 이 페이지 내에 디스플레이 하고 그들 필드들을 각각의 ActionForm으로 매핑시키고자 원하는 필드들을 검토할 필요가 있다. ActionForm은 값들을 저장하게 될 것이고 그것들(값들)이 적당한 Action에 전달되도록 보증하게 될 것이다. Form을 생성시킬 때 위에 언급된 MyEclipse 마법사들 양자는 그 폼에 부속된 JSP 페이지를 생성시키는 능력을 제공한다. 이것은 Form의 모든 프로퍼티들을 취하게 될 것이고 그것 속에 이미 모든 form 필드들을 가진 JSP 페이지들을 생성시키고 사용할 준비가 되어 있을 것이다. 이 경우에 우리가 또한 로그인을 처리하기 위한 Action을 생성시키고자 원하므로 우리는 단지  New Form 마법사 대신에 New Form, Action and JSP 마법사를 사용할 것이다.

어플리케이션 빌드를 계속 진행하기 위해서, 그림 15처럼 Struts 디자이너의 흰 색 캔버스 상을 마우스 오른쪽 버튼 클릭하고, New를 선택한 후 New Form, Action and JSP 마법사를 선택하라:


그림 15 - Designer에서 New Form, Action, JSP 마법사 띄우기

우리는 먼저 New Form  마법사를 설명할 것이다. 왜냐하면 그것은 세 단계 마법사의 첫 번째이기 때문이다. 마법사가 당신을 위해 좋은 디폴트 값을 채울 수 있도록 user case 이름을 입력하도록 하라. 그림 16은 당신이 use case를 입력할 때, 값들이 어떻게 당신을 위해 채워질 것인지를 보여준다:


그림 16 - New Form 마법사

이제 우리는 다음 두 개의 프로퍼티들을 추가할 필요가 있다: userName 과 password.  password 필드를 추가할 때, 그림17에 보이는 것처럼 JSP input type 필드로 password를 선택하라.


그림 17 - Form에 프로퍼티 추가


그림 18 - Form 프로퍼티들

Next를 선택하기 전에, 당신이 그것 속에 이들 값들을 가진 form을 갖는 스켈레톤 JSP 페이지를 생성시키는 것을 MyEcilpse에게 원한다고 마법사에게 아라려주기 위해 JSP 탭을 클릭하여야 한다. 아래 그림 19는 이것을 보여준다: 

노트: 마법사의 디폴트 특징은 우리가 webroot 속에 모든 JSP들을 위치 지울 예정인 이 데모 어플리케이션의 용도로 생성되는 JSP들을 "/form"에 위치지울 것이라는 점이다.


그림 19 - Form을 위한 JSP 페이지 생성을 가능하게 하기

마지막으로 우리는 Methods 탭을 클릭하여 마법사가 새로운 Form 속에 당신을 위해 자동 생성시킬 수 있는 모든 메소드들을 체크되지 않도록 할 필요가 있다. 그림 20은 이 구성을 보여준다.

노트: 이 데모를 간단하게 유지하기 위해 우리는 맞춤형 reset이나 validate 메소드들을 생성시키지 않을 것이지만, 당신 자신의 어플리케이션을 코딩할 때 이들 메소드들을 사용하는 것은 일반적으로 좋은 생각이다.


그림 20 - 사용 불가능이 된 메소드 생성

Next를 클릭한 후에 당신은 당신이 값들의 모두가 당신을 위해 이미 채워져 있음을 알게 되는 New Action 마법사로  갈 것이다. 이곳은 새로운 ACtion을 가지고 방금 생성시켰던 Form과 함께 시도하여 MyEclipse가 당신의 시간을 절약해주는 또 다른 곳이다.  당신이 원하는 어떤 변경들을 행하는 게 자유롭지만, 당신이 현재 (이 데모 어플리케이션 내에서) 걱정할 필요가 있는 유일한  것은이 Action이 그것에 대해 이용가능한 Forwards 를 채우는 것이다.  그림 21은 이 마법사의 스크린 샷을 보여준다.


그림 21 - Struts Action 마법사

당신은 그림 22에 보이는 것처럼 Forwards 탭을 클릭하여 ActionForwards를 지정할 수 있다.  


그림 22 - Action Forwards 설정

이 액션에 대해 Forwards를 추가한 후에 finish를 클릭하는 것은 MyEclipse에게 모든 리소스들을 생성시키고 모든 새로운 정보를 가지고 struts-config.xml 파일(과 디자이너)을 업데이트할 수 있도록 허용해준다. 그림 23은 어플리케이션의 업데이트 된 레이아웃과 구조를 보여준다. 

노트: 몇몇 수작업 레이아웃은 작은 스크린 샷 속에 모든 다이어그램 요소들을 선명하게 보여줄 수 없다. 당신이 수작업으로 다이어그램을 레이아웃 할 때, 당신의 변경들은 장래의 편집들에 대해 예약될 것이다.


그림 23 - Struts Designer와 우리의 어플리케이션에 대한 개요

이제 어플리케이션 플로우가 정의되었고 우리는 이 데모 어플리케이션을 위한 "로그인" 절차를 처리하기 위한 로직을 Action에 추가할 필요가 있다.  우리는 그림 24에 보이듯이 리소스를 더블 클릭하여 디자인으로부터 임의의 리소스들로 빠르게 점프할 수 있다:


그림 24 - 편집기를 열기 위한 Designer Resource 더블 클릭

처음으로 UserLoginAction.java 파일을 열 때, 우리의 execute 메소드에 대해 생성된 코드는 그림24a와 같을 것이다.







UserLoginAction.java
public ActionForward execute(
  ActionMapping mapping,
  ActionForm form,
  HttpServletRequest request,
  HttpServletResponse response) {
  UserLoginForm userLoginForm = (UserLoginForm) form;
  throw new UnsupportedOperationException(
   "Generated method 'execute(...)' not implemented.");
 }
그림 24a - 생성된 execute 메소드

우리는 예외상황을 간단하게 던지는 디폴틀 구현을 삭제하고, 그것을 그림 24b에 보이는 것과 같은 어떤 간단한 로그인 로직으로 대체시키고자 원한다.







UserLoginAction.java
 public ActionForward execute(
  ActionMapping mapping,
  ActionForm form,
  HttpServletRequest request,
  HttpServletResponse response) {
  UserLoginForm userLoginForm = (UserLoginForm) form;
  
  if(userLoginForm.getUserName().equals("myeclipse") && userLoginForm.getPassword().equals("myeclipse"))
  {
   request.setAttribute("userName", userLoginForm.getUserName());
   return mapping.findForward("success");
  }
  
  return mapping.findForward("failure");
 }
그림 24a - 교정된 execute 메소드


여기서 우리는 만일 userName 값과 password 값이 둘 다 "myeclipse"일 경우에 어떤 것,, 매우 간단한 체킹을 행하고 있다. 그럴 경우 우리는 request scope에 userName을 저장하고  success 포워드를 반환하여서, 우리의 userLoginSuccess.jsp 페이지는 개인화 된 메시지를 디스플레이 할 수 있다.  그 밖의 경우, 어떤 것이 잘못 되었고 우리는 failure 포워드를 반환한다. 실제 어플리케이션에서 우리는 무엇이 발생했는지를 설명하기 위해 failure  포워드를 반환하기 전에 request scope에 ActionMessages 또는 ActionErrors 콜렉션을 일반적으로 추가할 것이다. 





6. 데모 실행하기


이제 어플리케이션이 완성되고, 우리는 Manage Deployments 대화상자를 열고 이 프로젝트를 위한 새로운 배치를 설정하여 그것을 Tomcat5에 배치할 수 있다. 당신은  그림25에 보이듯이 패키지 탐색기 뷰에서 당신의 프로젝트를 오른쪽 마우스 버튼 클릭하고 MyEclipse > Add and Remove Project Deployments를 선택하거나 툴바에서 Deploy J2EE Project to Server 버튼을 클릭하여 관리 대화상자를 열 수 있다:

노트: 당신이 이미 MyEcilpse 내에 Tomcat 5 (또는 다른) 어플리케이션 서버 커넥터를 이미 설정했다고 가정한다. 만일 당신이 그것을 갖고 있지 않거나 도움이 필요하다면, 어플리케이션 서버 빠른 시작 가이드를 보라.


그림 25 - 새로운 배치 생성

배치가 완료된 후에 오류가 발생하지 않았음을 확인하기 위해 배치 상태를 체크하는 것은 항상 좋은 생각이다. 당신은 그림 26에서 다음 지침을 따라 이것을 행할 수 있다:


그림 26 - 배치 성공 확인

마지막으로 우리는 그림27에 보이듯이 어플리케이션 서버 적재 버튼을 사용하여 Tomcat을 시작할 것이다.


그림 27 - 어플리케이션 서버 시작

서버를 시작할 때 그것의 출력은 Eclipse 내의 Console View로 나타날 것이다. 그림 28은 우리가 다음 두 가지를 체크할 수 있는 일반적인 Tomcat 시작을 보여준다:
 1) Tomcat은 우리의 웹 어플리케이션을 적절하게 배치했다
 2) Tomcat은 적절하게 시작되었다


그림 28 - 어플리케이션과 서버가 정확하게 시작되었음을 확인한다

일단 Tomcat 5가 실행 중이고, 우리는 MyEclipse Browser View를 열어 그것을 테스트할 수 있다. 이것은 그림 29에 보이듯이 Window > Show View > Other...를 선택하고 MyEclipse Web Browser View를 선택하여  액세스되는 Open View 대화상자에서 행해질 수 있다.


그림 29 - Opening the Web Browser View

브라우저의 주소창에, 그림30에 보이듯이 예제 어플리케이션을 활성화 시키기 위해 http://localhost:8080/StrutsLoginDemo/userLogin.jsp 를 입력하라.


그림 30 - 데모 어플리케이션에 로그인한다

여기서 우리는 어플리케이션이 이용 가능한지를 본다. 우리는 그림 31에 보이듯이 이제 우리의 userName과 password를 입력할 수  있다.

노트: 이 데모를 위한 userName 과 password 는 각각 'myeclipse' 과 'myeclipse' 이다.


그림 31 - Note About Validation

로그인 후에, 폼은 유효성 검사 되었고 어플리케이션은 성공적인 로그인 페이지로 우리는 포워드 시켰다. 그림 32는 우리의 Action이 정확하게 우리에게 포워드 된 userLoginSuccess.jsp 페이지를 보여준다.


그림 31 - 성공적인 로그인





7. 요약


이 데모에서 우리는  MyEclipse Enterprise Workbench에서 이용 가능한 Struts 도구들을 사용하여 간단한 Struts 여플리케이션을 개발했다.

이것은 Struts에 대한 소개를 결론짓는다. 추가적인 빠른 시작 문서들은 웹 프로젝트들, 편집, 어플리케이션 서버 구성, 엔터프라이즈 어플리케이션 프로젝트들, 그리고 데이터베이스 배치에 대해 작업하는 것을 소개하는 것에서 이용 가능하다. 추가 정보는 MyEclipse 빠른 시작 라이브러리를 방문하라.





8. 사용자 피드백


만일 당신이 이 문서를 개선하는 방법에 대한 코멘트나 아이디어를 갖고 있다면 그것들을 MyEclipse Documentation Forum에 제출하길 바란다.