View 는 클라이언트가 모델의 상태를 보기 위해 사용하는 창(window)이다.
하나의 모델은 여러 창 즉 뷰를 포함할 수 있으며 클라이언트가 어떤 view를 통해 모델을 보느냐에 따라 화면이 달라진다.
표시 방법으로 XML, XSLT, SOAP, HTML 등 다양한 방법을 택할 수 있으며
Anyframe Web 에서는 주로 자바 코드와 태그로 View를 구성하며
JSP를 기반으로 클라이언트에 동적인 컨텐츠를 제공하게 된다.
ActionForm
웹 어플리케이션에서 사용자의 입력을 받을 때 페이지에는 텍스트 박스, 버튼 등과 같은 컴포넌트들이 HTML 의 폼 요소 내에 포함되어 있고
사용자가 버튼을 누르게 되면 필드 내에 있는 값들이 HTTP 요청과 함께 서버로 submit 된다.
서버 어플리케이션은 request에서 이 입력 값들을 꺼내어 올바른 데이터를 입력했는지 validation을 수행하고 나서 실제 비지니스를 수행하기 위해 Action으로 데이터를 넘기게 된다.
만일 입력 데이터가 validation rule을 통과하지 못한 경우 에러 메시지를 설정하여 입력 페이지로 돌아가게끔 처리해야 한다.
이처럼 요청에서 입력 값을 꺼내어 검증을 수행하고 실패에 대한 에러 메시지를 출력하는 등의 기능을 직접 구현하는 것은 쉬운 일이 아니다.
또한 이런 작업은 전체 어플리케이션 내에서 반복해서 일어나므로 재사용하는 것이 좋다.
이러한 작업들을 해주는 것이 org.apache.struts.action.ActionForm 클래스 이다.
ActionForm의 역할
- 요청에서 입력 값을 꺼내어 검증 수행, 실패에 대한 에러 메시지를 출력하는 등의 일련의 처리 과정을 재사용
- ActionForm은 클라이언트의 입력 값을 Action으로 전달하고, 결과를 되돌려줄 수 있음
- 입력 데이터들을 검증하는 동안 상태를 보관하는 버퍼로 동작
- 확실하지 않은 입력 값들을 검증 룰을 통해 세밀하게 조사하기 전까지 비즈니스 계층 밖에 위치하도록 해주는 firewall 역할
- ActionForm을 화면 표시 데이터로 설정하여 HTML 폼의 입력 필드를 쉽게 표시할 수 있음
Html 입력 Form으로 부터 받은 parameter 들은 자동으로 ActionForm 객체에 채워진다.
검증을 위한 validate() 메소드와 parameter가 ActionForm에 채워지기 전에 초기화 하는 reset() 메소드를 구현해야 한다.
struts-config.xml 에 ActionForm 에 대한 form-beans element 정의가 필요하다.
ActionForm은 Model의 부분이 아니다. 비즈니스 처리를 수행하기 위한 Model 영역은 Controller / View 와 완전히 분리하여야 하며
직접 비즈니스 계층으로 전달해서는 안되고 ValueObject 나 Parameters 같은 형태의 Data Transfer Object를 생성하여 전달토록 해야 한다.
ActionForm의 단점
- 어플리케이션 개발자가 ActionForm의 서브클래스를 직접 구현해야 함
- 많은 수의 클래스가 생겨날 수 있어서 유지보수 관리 어려움
- HTML Form 의 변경 시 ActionForm Class 수정 필요
DynaActionForm
- 실제 구현 클래스들을 만들 필요가 없음
- Property는 configuration파일에서 설정
- validate() 메소드를 구현하려면 DynaActionForm을 상속받아 직접 구현해야함. Validator 프레임워크를 이용하는 것이 좋음.
DynaActionForm 관련 form-beans 설정 및 한계는 이미 Configuration 파트에서 기술하였다.
ActionForm의 scope
- ActionForm 객체가 저장되어 유지되는 context의 scope를 나타낸다.
- session, request 2가지 레벨이 있다.
- struts-config.xml 의 action element 의 scope 속성으로 설정한다. default는 session이다. session scope일 경우 ActionForm의 제거에 유의해야 한다.
ActionForm의 LifeCycle
- 1. 액션의 매핑 정보를 확인하고 ActionForm이 설정되어 있는지 검사한다.
- 2. 액션에 ActionForm이 설정되어 있다면, 폼 빈의 설정 정보에서 action요소의 name속성을 찾는데 사용한다.
- 3. 이미 만든 ActionForm 인스턴스가 있는지 검사한다.
- 4. ActionForm 인스턴스가 적합한 scope에 있고 요청에 필요한 타입과 같다면 재사용한다.
- 5. ActionForm 인스턴스가 적합한 scope내에 없다면 새로운 인스턴스를 만들고 action 요소의 scope 속성에 따른 scope에 저장한다.
- 6. ActionForm 인스턴스의 reset() 메소드를 호출한다.
- 7. 요청 파라미터의 이름에 따른 ActionForm의 settter를 통해서 요청 파라미터의 값을 ActionForm에 입력한다. (populate 라고 한다.)
- 8.마지막으로 validate 속성이 "true"로 설정되어 있다면 ActionfForm 인스턴스의 validate()메소드를 수행하고 검증과정에 에러가 있다면 에러들을 반환한다.
다음은 ActionForm 작성의 예이다.
final class LogonForm extends ActionForm {
/**
* The password.
*/
private String password = null;
/**
* The username.
*/
private String username = null;
public String getPassword() {
return (this.password);
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return (this.username);
}
public void setUsername(String username) {
this.username = username;
}
/**
* Reset all properties to their default values.
*
public void reset(ActionMapping mapping, HttpServletRequest request) {
this.password = null;
this.username = null;
}
/**
* Validate.
*
*/
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if ((username == null) || (username.length() < 1))
errors.add("username", new ActionError("error.username.required"));
if ((password == null) || (password.length() < 1))
errors.add("password", new ActionError("error.password.required"));
return errors;
}
}
화면에서 입력받을 요소에 대한 attribute를 정의하고 해당 getter/setter 메소드를 작성한다.
또한 검증을 위한 validate 메소드와 초기화를 위한 reset 메소드를 구현해야 한다.
ActionErrors 사용하기
위 ActionForm 소스의 validate 메소드에서 ActionErrors 객체를 반환하는 것을 보았다. 여기서는 ActionError의 사용에 대해 알아본다.
- ActionErrors는 어플리케이션에서 발견한 에러를 하나 이상 캡슐화한다.
- 발견된 에러는 각각 org.apache.struts.action.ActionError 인스턴스가 된다.
- request에 저장된 ActionErrors는 이후 JSP 에서 custom tag를 통해 사용자들에게 에러 메시지로 보여진다.
다음은 ActionError 생성의 예이다.
ActionError error = new ActionError("global.error.login.requiredfield", "email");
위의 첫번째 인자는 리소스 번들 내의 키 중 하나와 일치하는 문자열이고, 두번째 인자는 메시지를 위한 parameter이다.
아래는 리소스 번들 내의 관련 메시지 정의이다.
global.error.login.requiredfield=The {0} field is required.
{0} 부분에는 email 이란 글자가 찍혀 표시된다.
위의 형태 외에도 복수개의 메시지 파라메터를 처리할 수 있는 몇가지 생성자 유형이 더 있다.
ActionError는 ActionForm의 validate 에서만 생성할 수 있는 것은 아니다.
예를 들어, Action에서 호출한 비즈니스 처리에서 예외가 발생했고 이를 사용자에게 알리기 위한 에러 메시지를 추가하려고
할 때도 ActionErrror를 사용할 수 있다.
ActionError 의 SuperClass 로 ActionMessage라는 클래스가 있으며 메시지나 INFO 를 출력할 때 사용하면 유용하다.
JSP 에서 Taglib를 이용해 메시지로 ActionError를 보여주는 예는 다음과 같다.
<%@ page contentType="text/html; charset=euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:html>
<head>
<title>Error Page</title>
</head>
<body>
<html:errors/>
</body>
</html:html>
Custom Tags
JSP Tag 확장 API를 이용하여 고유한 기능의 custom actions를 구현한 Custom Tag 구성이 가능하다.
Custom Tags
- Tag interface를 구현한 클래스를 만들고 XML 형식의 tag library descriptor (TLD) 파일을 제공해야 함
- 보통 관련된 Custom Tag의 묶음인 Tag Library 형태로 제공됨
Tag library의 필요성
- GUI 제작시 재사용을 통해 생산성 향상
- scripting 요소의 제거로 개발자와 디자이너간 역할 분담에 도움을 줌
- 전체 업무 영역에서 많이 사용되는 공통 기능을 커스텀 태그로 구현하면 생산성 향상에 도움이 될 수 있음
Tag library의 구성요소
- Tag Handler : Tag 가 어떤 식으로 동작하는지 정의하는 클래스, javax.servlet.jsp.tagext.Tag 인터페이스를 구현한 javax.servlet.jsp.tagext.TagSupport 나 BodyTagSupport를 상속(extends)하여 구현함
- Tag Library Descriptor (TLD) : Tag Handler 클래스로 구현한 Custom Tag 들에 대한 XML 형식의 기술문서
- taglib 지시자 (JSP 페이지 내에서) : JSP 페이지에서 해당 Tag Library를 사용하기 위한 지시자
Struts Framework 는 몇몇 종류의 태그들을 포함하고 있으며 이 Tag Library 기능을 이용하면
프리젠테이션 계층을 더 쉽게 제어할 수 있고 재사용이 용이하다. 제공하는 Tag library를 사용하여
JSP 페이지에서 자바 코드를 일체 사용하지 않고도 개발이 가능하다.
Tag library의 종류
- Struts Tag Library : html, logic, bean, template, nested
- JSTL : core, fmt, xml, sql
- Jakarata taglibs
- 해당 프로젝트에 맞게 작성한 Custom Tag Library
Struts Tag library
- HTML tag : HTML 입력 폼을 작성하거나 HTML 기반 사용자 인터페이스를 작성하는데 일반적으로 쓰이는 태그
- Logic tag : 조건 처리, Collection 객체를 loop을 돌면서 출력, 흐름 제어 등에 쓰이는 태그
- Bean tag : 자바 빈과 관련 프로파티들에 접근하는 데 이용되는 태그. 변수의 기술을 통해 쉽게 접근할 수 있는 새 빈을 정의할 수 있음
- Template tag : layout를 공유하는 동적인 JSP 템플릿을 작성할 때 유용하게 쓸 수 있는 태그
- Nested tag : Struts 태그들을 중첩해서 사용할 수 있게 해줌
사용법은 다른 Tag Library와 같다. 아래는 JSP의 taglib 선언과 web.xml에 taglib 요소를 정의한 예이다.
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</tglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
많은 경우 Tag Library 들은 자바빈즈와 함께 사용된다. 자바 빈은 HTML 폼의 입력 필드에 대응하는
프로퍼티들을 포함하는 ActionForm일 수도 있지만 Value Object들도 사용할 수 있다.
HTML tag
다음은 HTML Tag Library의 태그들에 대한 설명이다.
| name |
Description |
| base |
HTML의 base요소를 표시한다. |
| button |
button 입력 필드를 표시한다. |
| cancel |
cancel 버튼을 표시한다. |
| checkbox |
checkbox 입력필드를 표시한다. |
| errors |
모든 일련의 에러 메시지를 조건적으로 표시한다. |
| file |
file 선택 입력 필드를 표시한다. |
| form |
html 선택 입력 필드를 표시한다. |
| frame |
HTML의 form 요소를 정의한다. |
| hidden |
hidden 필드를 표시한다. |
| html |
HTML의 html요소를 표시한다. |
| img |
HTML의 img태그를 표시한다. |
| javascript |
validator 플러그 인이 로딩한 validation-rule에 기반한 javascript를 표시한다. |
| link |
HTML의 앵커나 하이퍼링크를 표시한다. |
| messages |
모여진 일련의 메시지들을 조건적으로 표시한다. |
| multibox |
멀티플 checkbox 입력 필드를 표시한다. |
| option |
select의 option을 표시한다. |
| options |
select의 option들의 집합을 표시한다. |
| optionsCollection |
select의 option들의 집합을 표시한다. |
| password |
password 입력필드를 표시한다. |
| radio |
radio 버튼 입력 필드를 표시한다. |
| reset |
reset 버튼 입력 필드를 표시한다. |
| rewrite |
URI를 표시한다. |
| select |
select 요소를 표시한다. |
| submit |
submit 버튼을 표시한다. |
| text |
"text" 타입의 입력 필드를 표시한다. |
| textarea |
textarea 입력 필드를 표시한다 |
다음은 html 의 link, password 태그의 예이다.
<tr>
<td colspan="4" align="center">
<html:link page="/html-link.do?doubleProperty=321.321&longProperty=321321">
Double and long via hard coded changes
</html:link>
</td>
</tr>
<html:password property="password" size="15" maxlength="16" redisplay="false" />
Logic tag
다음은 Logic Tag Library의 태그들에 대한 설명이다.
| empty |
요청한 변수가 null 또는 빈 문자열인 경우 이 태그의 바디 컨텐츠를 수행한다. |
| equal |
요청한 변수가 지정한 값과 같을 경우 이 태그의 바디 컨텐츠를 수행한다. |
| forward |
ActionForward 엔트리를 통해 지정한 페이지로 포워드를 수행한다. |
| greaterEqual |
요청한 변수가 지정한 값보다 크거나 동일한 경우 이 태그의 바디 컨텐츠를 수행한다. |
| greaterThan |
요청한 변수가 지정한 값보다 큰 경우… |
| iterate |
지정한 컬렉션으로 이 태그 내의 바디 컨텐츠를 반복한다 |
| lessEqual |
요청한 변수가 지정한 값보다 작거나 동일한 경우 |
| lessThan |
요청한 변수가 지정한 값보다 작을 경우 |
| match |
지정한 값이 요청한 변수의 부분 문자열에 일치하는 경우 |
| messagesNotPresent |
지정한 메시지가 이 요청에 없는 경우 |
| messagesPresent |
지정한 메시지가 이 요청에 있는 경우 |
| notEmpty |
요청한 변수가 null도, 빈 문자열도 아닌 경우 |
| notEqual |
요청한 변수가 지정한 값과 동일하지 않은 경우 |
| notMatch |
지정한 값이 요청한 변수의 부분 문자열에 일치하지 않는 경우 이 태그의 바디 컨텐츠를 수행한다. |
| notPresent |
지정한 값이 이 Request에 없는 경우 |
| present |
지정한 값이 이 Request에 있는 경우 |
| redirect |
HTTP Redirect를 표시한다. |
다음은 notEmpty, iterate 태그의 예이다.
<logic:notEmpty name="userSummary" property="addresses">
<!—이 부분은 address Collection 의 모든 객체들을 돌며 반복 출력하는 logic 태그로 구성하면 됨 -->
</logic:notEmpty>
<logic:iterate id="address" name="usersSummary" property="addresses">
<!—address 객체를 테이블 형태로 출력한다. -->
</logic:iterate>
Bean tag
다음은 Bean Tag Library의 태그들에 대한 설명이다.
| cookie |
지정한 요청 쿠키의 값에 근거해 변수를 정의한다 |
| define |
지정한 빈 프로파티의 값에 근거해 변수를 정의한다 |
| header |
지정한 요청 헤더의 값에 근거해 변수를 정의한다 |
| include |
동적인 어플리케이션 요청의 응답을 로드 해 빈으로 이용할 수 있도록 한다 |
| message |
응답이 되는 국제화된 메시지 문자열을 표시한다 |
| page |
지정한 아이템을 빈으로써 페이지 문맥에서 꺼낸다 |
| parameter |
지정한 요청 파라미터의 값에 근거해 변수를 정의한다 |
| resource |
웹 어플리케이션의 자원을 로드 해 빈으로 이용할 수 있도록 한다 |
| size |
Collection 또는 Map의 요소의 개수를 포함한 빈을 정의한다. |
| struts |
지정한 Struts 내부 설정 객체를 빈으로 꺼낸다. |
| write |
지정한 빈 프로파티의 값을 표시한다. |
다음은 message, write 태그의 예이다.
<td><bean:message key="global.user.firstName"/>:</td>
위와 같이 사용하면 global.user.firstName 에 해당하는 메시지를 가져와 보여준다.
<td>Hello <bean:write name="user" property="firstName"/>:</td>
위와 같이 사용하면 user 라는 빈에서 firstName을 꺼내 Hello 옆에 붙여준다.
Template tag
더 장점이 많은 Tiles Framework를 적용하는 것이 나으므로 여기서는 따로 설명하지 않는다.
Nested tag
한 태그를 다른 태그에 중첩하여 사용하고자 할 경우 적용할 수 있다.
Struts에서 지원하는 현재 태그와 매칭되는 HTML Nested Tag, Logic Nested Tag, Bean Nested Tag가 존재하며
사용 방법은 원래 태그와 같다.
JSTL
- JSR52, JSP Standard Tag Library 스펙
- 어떤 컨테이너에서도 사용 가능한 표준 태그 집합을 정의
- core, fmt, xml, sql 태그가 있음
- Servlet 2.3, JSP 1.2 이상을 지원하는 컨테이너 필요(Tomcat 을 비롯하여 대부분 지원함)
JSTL은 데이터의 포맷, 반복 처리, 조건 처리 등 전형적인 프리젠테이션 레이어를 위한 표준 구현을 제공하기 때문에, JSP 작성자들이 어플리케이션 개발에 집중하는데 도움이 되며
일반적인 기능을 커스텀 태그 라이브러리의 표준 세트로 패키징했기 때문에 JSP 작성자들이 스크립팅 엘리먼트에 대한 필요를 줄이고 관련된 관리 비용을 피할 수 있도록 한다.
이에 반해 pure 자바 코드에 비해 시스템 리소스를 많이 사용하며 극한 부하 상황에서는 2~3배의 성능 저하가 발생할 수 있으므로 성능이 이슈가 되는 경우 사용에 유의하도록 한다.
Struts Bean, Logic 태그들은 JSTL로 바꾸어 더 쉽게 사용할 수 있다.
다음은 JSTL Core 태그 중 조건 분기 및 Collection loop 처리의 예이다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
..
<!-- 테이블의 리스트 반복부 -->
<c:choose>
<c:when test="${page.totalCount <= 0}">
<tr class="ct_list_pop">
<td colspan="11" align="center">::: 조회된 사용자 정보가 없습니다. :::</td>
</tr>
<tr>
<td colspan="11" bgcolor="D6D7D6" height="1"></td>
</tr>
</c:when>
<c:otherwise>
<c:forEach var="userVO" items="${page.list}" varStatus="status">
<tr class="ct_list_pop">
<td align="center">
<c:out value="${status.count + ((page.currentPage - 1) * pageSize) }"/>
</td>
<td></td>
<td align="left">
<a href="javascript:fncGetUser('<c:out value="${userVO.userId}"/>');">
<c:out value="${userVO.userId}"/>
</a>
</td>
<td></td>
<td align="left"><c:out value="${userVO.userName}"/></td>
<td></td>
<td align="center" style="padding-right:3px;"><c:out value="${userVO.ssn}"/></td>
<td></td>
<td align="center"><c:out value="${userVO.cellPhone}"/></td>
<td></td>
<td align="left"><c:out value="${userVO.email}"/></td>
</tr>
<tr>
<td colspan="11" bgcolor="D6D7D6" height="1"></td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
..
Jakarata taglibs
JSP Standard Tag Library (JSTL) 의 구현인 Standard Taglib 1.1(JSTL 1.1 - Servlet 2.4, JSP 2.0 이상)을 비롯한
많은 태그 라이브러리들을 활용할 수 있다.
해당 프로젝트에 맞게 작성한 Custom Tag Library
- 전체 업무 영역에서 많이 사용되는 공통 기능을 커스텀 태그로 직접 구현
예를 들면 코드 테이블로부터 코드리스트를 조회하여 select box 형식으로 표출하는 Custom Tag 를 적용하면 전체 업무의 생산성 향상에 도움이 될 것이다.
Anyframe Web에서 제공하는 pagenavigator 태그도 다중 행의 리스트성 자료의 페이지 처리 기능을 돕기 위해 직접 구현한 커스텀 태그이다.