Summary : 지난 강좌에서는 Model 1 개발 방식과 Model 2 개발 방식에 대하여 살펴보았다. Model 2 개발 방식으로 개발하게 된 배경과 Model 2에 기반한 스트러츠가 나타나게 된 배경에 대하여 살펴보았다. 이번 강좌에서는 스트러츠의 Configuration 파일들에 대하여 살펴볼 것이다. 스트러츠 Application을 만들기 위하여 필요한 대표적인 Configuration 파일인 struts-config.xml과 web.xml에 대하여 살펴보겠다.
스트러츠를 위한 web.xml 파일
스트러츠 Application파일을 만들기 위한 작업이 생각보다 간단하지 않다. 스트러츠 Configuration에 관여하는 파일이 많으며, Configuration 파일이 복잡한 것이 사실이다. 스트러츠 첫번째 강좌에서 스트러츠 Application을 만들기 위한 설치과정을 살펴보았다. 생각보다 많은 파일들이 스트러츠 Application을 만들기 위하여 관여하는 것을 알 수 있었다.
각각의 Configuration파일을 설명하면서 스트러츠에 대하여 더 깊이 있게 이해하도록 하겠다.
스트러츠 Application을 만들기 위하여 우선 설정할 파일은 Web Application에 꼭 필요한 web.xml파일의 설정이다. 많은 개발자들이 web.xml에 대하여 잘 알고 있을 것으로 생각되지만 처음 접하는 개발자들을 위하여 web.xml에 대하여 자세하게 살펴보도록 하자.
스트러츠는 MVC 모델에 기반하고 있다. Model 2에서는 각 모듈별로 Servlet을 만들어 Controller역할을 할 수 밖에 없었다. 만약 하나의 Servlet에서 모든 request를 담당한다면, 다양한 정보를 하나의 Servlet에서 모두 처리하기에는 많은 문제점을 가지고 있다. 두번째 강좌의 Model 2에 대한 내용을 보면 알 수 있을 것이다.
이 같은 문제점을 보완하기 위하여 스트러츠에서는 Servlet에서 가지고 있던 정보들을 XML파일로 빼내서 처리하였다. 그리고 모든 request를 하나의 Servlet으로 중앙집중화하였다.
Servlet에서 처리해야할 정보들을 빼낸 XML파일이 struts-config.xml파일이고, 중앙집중적으로 Controller역할을 하는 Servlet이 ActionServlet이다. 이 정보들을 web.xml에 다음과 같이 설정함으로서 스트러츠 Application에서 사용이 가능하게 된다.
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
</servlet>
<!-- Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
web.xml에서 Action Servlet을 mapping하는 부분이다. 이 부분이 나타내는 의미는 URL의 확장자가 .do로 끝나는 모든 request는 ActionServlet으로 mapping된다는 것이다.
따라서 다음과 같은 형태의 URL이 처음에 접근하게 되는 것이 ActionServlet이라는 의미이다.
http://www.javajigi.net/myApplication/actionName.do
스트러츠 Application을 위한 struts-config.xml을 설정하는 과정은 다음과 같이 ActionServlet에 인자로 전달함으로서 가능하게 된다. ActionServlet은 여러개의 초기 인자를 받아서 ActionServlet의 초기화를 진행하게 된다. web.xml에서 초기 인자를 전달하는 것은 init-param태그를 이용하여 가능하다.
<web-app>
<!-- Action Servlet Configuration -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<!-- Resources bundle base class -->
<init-param>
<param-name>application</param-name>
<param-value>net.javajigi.tutorial.MessageResources</param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>3</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>3</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</webapps>
모든 Servlet에 name-value쌍이면 어떠한 인자도 전달할 수 있다. ActionServlet에서는 init-param으로 위와 같은 정보들을 받는다. 스트러츠에서 사용할 Resource Bundle파일(net.javajigi.tutorial.MessageResources), Configuration파일(/WEB-INF/struts-config.xml)등의 정보들을 전달할 수 있다. ActionServlet을 위한 초기인자의 name은 고정되어 있지만 value들을 개발자들이 원하는 것으로 바꾸어 사용하는 것이 web.xml의 수정만으로 가능하게 된다. 소스코드에 이 정보들을 가지고 있는 것보다는 쉽게 설정의 변경이 가능함을 알 수 있다.
헉, 내가 생각해도 설명이 부족함을 느낀다. 어찌 설명해야 쉽게 이해시킬 수 있을지 난감하다. 우선 강좌를 진행하고 앞으로 계속되는 강좌나 Q&A를 통하여 부족한 부분은 보강하도록 하겠다.
Tag Libraries의 Configuration
스트러츠 프레임워크는 뷰를 쉽게 생성하도록 돕기 위하여 많은 커스텀 태그를 제공한다. 이 태그들을 사용하기 위하여 web.xml에서 다음과 같이 설정한다. 스트러츠에서 제공한 모든 tld파일에 대하여 다음과 같이 설정하면 된다.
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-template.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-template.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
커스텀 태그를 사용해본 개발자들이라면 web.xml에 커스텀 태그의 사용을 위한 tld파일을 설정하는 부분에 대하여 쉽게 이해할 수 있을 것이다. web.xml에서 위와 같이 설정한 다음 JSP에서 다음과 같이 사용할 커스텀 태그를 지정하는 것이 가능하게 된다.
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
커스텀 태그의 설정 및 사용에 대한 부분은 이 강좌에서는 다루지 않겠다.
지금까지 스트러츠 Application을 위한 web.xml의 설정에 대하여 살펴보았다. web.xml에는 이 외에도 다양한 태그들을 제공한다. 개발자들이 web.xml에서 제공하는 모든 기능을 유용하게 활용할 수 있다면 Web Application을 개발하는데 많은 도움이 될 것이다. web.xml에 대하여 더 깊이 알고 싶은 개발자들은 http://java.sun.com/dtd/web-app_2_3.dtd의 DTD를 보면 설명이 잘 되어 있기 때문에 꼭 한번 보기 바란다.
XML에 익숙하지 않거나 XML의 기초를 공부한 개발자들은 이 기회를 통해 XML에 대하여 더 깊이 있게 공부해 보기 바란다.
스트러츠 Configuration File
스트러츠 Configuration File의 중심은 뭐니뭐니 해도 struts-config.xml파일이다. web.xml에서 보아서 알 수 있듯이 꼭 struts-config.xml파일이 아니어도 된다. 다른 이름을 가지는 XML파일이어도 상관없다.
먼저 struts-config.xml파일에서 사용 가능한 태그들은 struts-config_1_1.dtd을 살펴보면 다음과 같다.
<!ELEMENT struts-config (data-sources?, form-beans?, global-exceptions?, global-forwards?, action-mappings?, controller?, message-resources*, plug-in*)>
이 내용을 바탕으로 살펴보면 struts-config.xml에서 설정하는 정보는 다음과 같음을 추측할 수 있다.
1. 데이터베이스를 위한 DataSource의 설정.
2. request에 의하여 전달되는 정보를 저장하는 FormBean의 설정.
3. Application에서 발생하는 Exception의 설정.
4. Application에서 요청에 따른 작업을 끝낸후 Forward할 path에 대한 설정.
5. Model과 통신하는 BusinessLogic을 담고 있는 Action클래스의 설정.
6. 스트러츠 Controller의 설정.
7. Message Resource의 설정.
8. 다소 생소하겠지만 스트러츠에서는 플러그인이 가능하도록 구현되어 있다. 따라서 플러그인에 대한 설정이 가능하다.
struts-config.xml의 내용을 자세하게 이해하면 스트러츠가 무엇을 하는 놈인지 많은 부분을 이해할 수 있게 된다. 따라서 struts-config.xml에 대하여 자세하게 살펴보도록 하겠다. 아직 FormBean이나 Action과 같이 처음 접하는 부분이 많기 때문에 이해하는데 어려움을 느끼는 개발자들도 많을 것으로 예상된다. 이 부분은 다음 강좌에서 다룰 부분이기 때문에 이번 강좌에서는 앞의 두 강좌에서 다룬 내용을 바탕으로 설명할 수 밖에 없을 것으로 생각된다.
먼저 struts-config.xml과 같이 Controller의 설정파일까지 생기게된 배경에 대하여 간략하게 설명하고 넘어가겠다.
앞의 Model 2 개발방식에서도 간략하게 설명했다. Model 2의 CommandFactory를 보면 알 수 있듯이 5개의 작업만을 위해서 5번의 if/else가 나타나고 있음을 알 수 있다. 그러나 실제 Web Application을 개발할 때는 수백에서 수천개의 작업이 발생한다. 이 때마다 CommandFactory를 만들 수는 없는 것이다. 또한 그에 따른 Servlet을 각각 생성해 주어야 하는 불편함이 있다. Servlet의 수도 많아질뿐만 아니라 Factory 클래스도 무수히 많이 늘어나게 될 것이다.
이 같은 단점을 보완하기 위하여 모든 request는 하나의 Servlet에서 처리하도록 했으며, 그에 대한 정보는 struts-config.xml을 두어 설정하도록 했다.
Model 2 개발방식으로 개발할 경우, Action을 상속하는 클래스들에서 요청한 작업이 완료된 후 접근할 URL을 지정하고 있다. ListAction을 보면 다음 URL은 list.jsp를 반환하고 있다. 하지만 실무 개발에서 작업 완료후 접근하게될 URL이 바뀌는 경우는 수도 없이 많다. 이럴때마다 해당 Action 클래스를 찾아서 URL을 변경한 후 다시 컴파일, 서버 재시작등의 작업이 이루어져야 한다. 상당히 많은 시간과 짜증나는 일이 아닐 수 없다. 이 같이 Model 2개발방식에서 많은 시간을 요하거나, 재사용이 힘들었던 부분들을 struts-config.xml에서 설정하도록 하여 변경사항이 발생할 경우 struts-config.xml의 정보만 수정하면 되도록 구현한 것이다.
이 같이 Model 2에서 짜증나고, 많은 시간을 요하는 부분을 Framework화하여 재사용이 가능하고, 사용하기 편하도록 만든 것이 스트러츠이다. 이 같은 방식을 강좌 2에서도 살펴보았듯이 Model 2+1이라고 하는 것이다.
그렇다면 struts-config.xml의 내부에 대하여 살펴보도록 하자.
action-mappings 태그
struts-config.xml에서 가장 중심이 되는 부분은 action-mappings태그이다. action-mappings태그는 Model 2 개발방식에서 Command와 Action클래스를 mapping시키는 부분과 해당 command에 해당하는 작업을 완료한 다음 접근하게 될 path를 지정하는 부분이 포함된다.
<action-mappings type="org.apache.struts.action.ActionMapping">
<action
path="/login"
attribute="loginForm"
input="/tutorial/login.jsp"
name="loginForm"
type="net.javajigi.tutorial.action.LoginAction" />
<forward name="mainpage" path="/tutorial/mainpage.jsp" />
</action-mappings>
action-mappings태그에는 여러개의 action태그를 가질 수 있다. action태그는 Model 2에서 보았던 list명령어와 ListAction을 mapping시키는 역할을 한다. 또한 ListAction에서 작업완료후 접근하게 될 path를 struts-config.xml로 설정한 것이다.
위 login action은 강좌 1에서 다룬 부분을 조금 수정한 것이다. 각각에 대하여 설명하면 다음과 같다.
1. Command에 해당하는 부분이 path이다. URL이 login.do일 경우 위 action을 찾게 된다.
2. Command가 login일 경우 mapping되는 Action클래스는 net.javajigi.tutorial.action.LoginAction임을 type에서 명시하고 있다.
3. 에러 없이 작업을 완료했다면 forward할 path를 지정하고 있다. action태그에서 사용되는 forward정보는 자바의 지역변수와 같이 해당 action에서만 사용이 가능하다. 만약 여러개의 action에서 같이 사용되는 forward정보라면 global-forwards태그에서 정의하면 전체 action에서 사용이 가능하다. 이 부분은 계속되는 예제를 통하여 이해할 수 있을 것이다.
4. 로그인을 위해서는 id와 password를 입력값으로 받아야 한다. 사용자가 입력한 값을 저장하는 역할을 하는 것이 FormBean이다. 따라서 로그인 과정에서도 id와 password 정보를 포함하고 있는 FormBean을 지정할 수 있다. 물론 FormBean에 대한 정보는 form-beans태그에서 정의한 것을 사용하게 된다. action태그에서 FormBean을 사용하는 부분은 name attribute를 통하여 사용이 가능하다.
이 외에도 action태그에서 사용가능한 attribute는 무수히 많다. 하지만 처음부터 너무 많은 부분을
다뤄봤자 혼랍스럽기만 할 것으로 생각되어 꼭 필요한 부분만 우선 설명했다. 필자가 생각하기에는 스트러츠의 전체적인 구조 및 직접 테스트를 해보는 것이 중요할 것으로 생각되어 필요한 부분들을 추출하였다.
form-beans 태그
다음은 action태그에서 이용했던 FormBean을 정의하는 form-beans태그에 대하여 살펴보도록 하겠다.
<form-beans type="org.apache.struts.action.ActionFormBean">
<form-bean name="loginForm" type="net.javajigi.tutorial.form.LoginForm">
<form-property name="password" type="java.lang.String" />
<form-property name="id" type="java.lang.String" />
</form-bean>
</form-beans>
form-beans 태그내에도 각각의 FormBean을 설정하는 form-bean태그가 있다.
form-bean태그는 다음과 같은 정보를 담고 있다.
1. FormBean의 타입을 설정.(type attribute)
2. struts-config.xml에서 사용할 FormBean의 name을 설정.(name attribute)
3. FormBean을 이용하여 전달하 property이름과 type을 설정. 위 예제에서는 로그인 입력 폼에서 전달한 id와 password를 전달한다.
위 form-bean태그에서 loginForm으로 설정한 FormBean은 action태그에서 name attribute로 사용된다.
global-forwards 태그\
global-forwards 태그는 action태그 전체에서 사용할 forward정보를 설정한다. 위에서 살펴본 바와 같이 login action 내에서 사용한 mainpage forward는 login action에서만 사용이 가능하다. 만약 login action만이 아닌 여러개의 action에서 사용하고자 한다면 global-forwards에 다음과 같이 설정해야 한다.
<global-forwards type="org.apache.struts.action.ActionForward">
<forward name="mainpage" path="/tutorial/mainpage.jsp" />
</global-forwards>
message-resources 태그
message-resources태그는 스트러츠 Application에서 사용할 Message Resource들을 설정한다. 앞의 예제에서 우리가 사용한 Message Resource는 net.javajigi.tutorial.MessageResources이였다. struts-config.xml에서 다음과 같이 설정하면 된다.
<message-resources parameter="net.javajigi.tutorial.MessageResources" />
지금까지 struts-config.xml에 대하여 간략하게 살펴보았다. 스트러츠를 시작하는 단계에서 struts-config.xml를 전체를 살펴보는 것이 무의미할 것으로 생각되어 이번 강좌에서는 생략했다. 단지 필요한 정보들만 우선 설정해보고 이 부분이 익숙해진 다음 다른 설정으로 넘어가는 것이 좋을 것으로 생각되어 이번 강좌에서는 간략하게 살펴보았다.
지금은 스트러츠가 구현되는 전체적인 구조를 먼저 이해하는 것이 중요하다고 생각된다.
지금까지 진행한 강좌에 대하여 더 깊이 이해하기 위하여 스트러츠 강좌 1에서 진행했던 로그인 예제를 확장하여 로그인 과정을 완성해 보도록 하겠다.
로그인 예제 Model 부분
지금까지 진행한 스트러츠의 Configuration을 더 깊이 이해하기 위하여 실제 개발에서 많이 이루어지고 있는 로그인 과정을 스트러츠로 구현해보자.
예제를 단순화하기 위하여 사용자의 추가, 수정, 삭제는 다루지 않았다. 단지 한 사용자를 고정하여 로그인 예제를 다루도록 하였다.
로그인 예제의 흐름은 로그인 페이지에서 로그인 과정이 정상적으로 이루어지면 사이트의 메인 페이지로 이동한다. 만약 아이디가 없는 사용자이거나 비밀번호가 틀리면 에러메세지를 출력하고 로그인 페이지로 되돌아온다.
사용자에 대한 정보는 데이터베이스와 같은 영구 저장소에 저장하는 것이 일반적이다. 따라서 로그인을 과정을 정상적으로 진행하기 위해서는 먼저 사용자가 입력한 아이디에 해당하는 사용자가 있는지 데이터베이스에서 확인 작업을 거친뒤 사용자가 로그인 하도록 구현하다.
여기에서 사용자의 정보를 데이터베이스에서 가져오는 부분은 담당하는 부분이 Model 부분이다. 예제를 단순화하기 위하여 데이터베이스에 대한 접속부분은 이번 예제에서 제외시켰다.
package net.javajigi.tutorial.user;
public class UserDAO {
public UserVO getUser(String id) {
/*
데이터베이스에 접속하여 해당하는 User의 정보를 가져오는 코드가
실제 Application에서는 있을 것이다.
User의 정보를 가져와 User Value Object에 해당 정보를 저장하여 반환하는
역할을 한다.
이번 예제에서는 스트러츠에 집중하기 위하여 데이터베이스에 접속하여 데이터를
가져오는 부분은 생략했다.
id가 javajigi일 경우 UserVO를 생성하고,
그렇지 않을 경우 존재하지 않는 아이디로 간주하여 null을 반환하도록
구현했다.
*/
if ( id.equals("javajigi") ) {
return new UserVO("javajigi", "password");
} else {
return null;
}
}
}
package net.javajigi.tutorial.user;
public class UserVO {
private String id = null;
private String password = null;
public UserVO(String id, String password){
this.id = id;
this.password = password;
}
public String getId() {
return id;
}
public String getPassword() {
return password;
}
}
로그인을 위한 Model은 단지 두개의 클래스만을 이용하였다. EJB와 같은 복잡한 구조가 아니기 때문에 두개의 클래스만으로 충분히 소화할 수 있을 것으로 생각되었다.
UserDAO는 사용자가 입력한 아이디에 해당하는 사용자 정보를 데이터베이스에서 가져오는 getUser() 메써드를 포함하고 있다. getUser()메써드는 가져온 데이터를 UserVO에 저장하여 반환하고 있다. 실무에서 실질적인 데이터베이스와 접속 및 데이터 조작은 UserDAO를 통하여 이루어지게 된다.
UserDAO를 보면 사용자의 아이디가 javajigi일 경우에 아이디가 javajigi, 비밀번호 password를 가지는 UserVO를 반환하고 아이디가 javajigi가 아닌 사용자에 대해서는 null을 반환한다.
로그인 예제 Controller 부분
로그인 과정을 처리하기 위하여 뷰에서 데이터를 받아 모델과 통신의 역할을 하는 것이 Controller의 역할이다. 스트러츠에서 Control의 역할은 ActionServlet에서 struts-config.xml의 설정 정보를 이용하여 이루어진다. 모델과의 Communication을 담당하는 부분은 Action을 상속하는 클래스에서 이루어지게 된다.
이 예제에서는 LoginAction으로 정의하였다.
package net.javajigi.tutorial.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.javajigi.tutorial.Constants;
import net.javajigi.tutorial.form.LoginForm;
import net.javajigi.tutorial.user.UserDAO;
import net.javajigi.tutorial.user.UserVO;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class LoginAction extends Action {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
LoginForm loginForm = (LoginForm)form;
String inputId = loginForm.getId().trim();
String inputPassword = loginForm.getPassword();
UserDAO userDAO = new UserDAO();
UserVO userVO = userDAO.getUser(inputId);
ActionErrors errors = new ActionErrors();
if ( userVO == null ) {
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("error.id.notexisted"));
} else {
if ( !inputPassword.equals(userVO.getPassword()) ) {
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("error.password.match"));
}
}
if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.getInputForward());
}
HttpSession session = request.getSession();
session.setAttribute(Constants.USER_KEY, userVO);
return(mapping.findForward("mainpage"));
}
}
LoginAction은 사용자가 입력한 데이터를 LoginAction의 FormBean으로 사용되고 있는 LoginForm을 이용하여 받고 있다. 사용자가 입력한 아이디를 UserDAO에 전달하여 UserVO를 얻고 있음을 볼 수 있다. 이와 같이 Action클래스에서 뷰와 모델의 실질적인 통신역할을 하고 있다.
뷰와 모델의 데이터들을 비교하여 로그인 유무를 결정하며, 로그인이 정상적으로 이루어지면 세션에 사용자 정보를 저장한다음 메인페이지로 이동함을 볼 수 있다.
LoginAction에서 FormBean으로 사용된 LoginForm은 다음과 같다.
package net.javajigi.tutorial.form;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
public class LoginForm extends ActionForm {
private String id = null;
private String password = null;
public ActionErrors validate(
ActionMapping mapping,
HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if ( (id.length() < 4) || (id.length() > 10) ) {
errors.add("error.id.lengtherror", new ActionError("error.id.lengtherror"));
}
if ( (password.length() < 4) || (password.length() > 10) ) {
errors.add("error.password.lengtherror", new ActionError("error.password.lengtherror"));
}
return errors;
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
password = "";
id = "";
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
강좌 1에서의 LoginForm과 거의 비슷하다. 달라진 부분은 validate내부이다. 이 예제에서는 아이디와 비밀번호가 4자리 이상, 10자리 이하여야 된다고 가정하고 validate를 작성하였다. FormBean에서는 LoginForm과 같이 사용자가 입력한 값에 대한 유효성을 검사를 validate 메써드를 이용하여 서버사이드에서 가능하다. 물론 대부분의 웹 개발에서 유효성 검사는 클라이언트에서 스트립트를 이용하여 이루어지는 것이 현실이다.
로그인 예제 Configuration 및 Resource Bundle
지금까지 생성한 LoginAction과 LoginForm을 struts-config.xml에서 다음과 같이 설정한다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-
"http:>
<struts-config>
<!-- ========== Data Source Configuration =============================== -->
<data-sources />
<!-- ========== Form Bean Definitions ================================== -->
<form-beans type="org.apache.struts.action.ActionFormBean">
<form-bean name="loginForm" type="net.javajigi.tutorial.form.LoginForm">
<form-property name="password" type="java.lang.String" />
<form-property name="id" type="java.lang.String" />
</form-bean>
</form-beans>
<!-- ========== Global Exception Definitions ============================== -->
<global-exceptions />
<!-- ========== Global Forward Definitions =============================== -->
<global-forwards type="org.apache.struts.action.ActionForward">
<forward name="mainpage" path="/tutorial/mainpage.jsp" />
<forward name="loginForm" path="/tutorial/login.jsp" redirect="true" />
</global-forwards>
<!-- ========== Action Mapping Definitions =============================== -->
<action-mappings type="org.apache.struts.action.ActionMapping">
<action
attribute="loginForm"
input="/tutorial/login.jsp"
name="loginForm"
path="/login"
type="net.javajigi.tutorial.action.LoginAction" />
</action-mappings>
<!-- ========== Controller Configuration ================================ -->
<controller />
<!-- ========== Message Resources Definitions ============================ -->
<message-resources parameter="net.javajigi.tutorial.MessageResources" />
<!-- ========== Plug Ins Configuration ================================= -->
</struts-config>
form-bean태그를 이용하여 LoginForm을 설정하고 있으며, action에서 loginForm을 이용하고 있음을 볼 수 있다. action에서 LoginAction을 path login에 mapping시키고 있다.
다음은 LoginAction과 LoginForm에서 사용한 에러 메세지는 MessageResources에 다음과 같이 저장하면 된다.
login.title = Welcome to RegisterUser
login.login = 로그인
prompt.id=ID :
prompt.password=Password :
mainmenu.title=Main Page
mainmenu.presentation=이 페이지는 Main Menuh2.
#에러 메세지
error.id.lengtherror=<li>아이디는 4자리 이상, 10자리 이하여야 합니다.</li>
error.password.lengtherror=<li>비밀번호는 4자리 이상, 10자리 이하여야 합니다.</li>
error.id.required=<li>아이디는 꼭 입력해야 됩니다.</li>
error.id.notexisted=<li>존재하지 않는 아이디입니다.</li>
error.password.required=<li>비밀번호는 꼭 입력해야 됩니다.</li>
error.password.match=<li>비밀번호가 틀립니다.</li>
errors.header=<h3><font color="red">로그인 에러</font></h3>
You must correct the following error(s) before proceeding:<UL>
errors.footer=</ul><hr>
로그인 예제 View부분
로그인 예제의 View는 강좌1의 로그인과 크게 달라진 것이 없다.
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:html locale="true">
<head>
<title><bean:message key="login.title" /></title>
<html:base/>
</head>
<body>
<html:form action="/login">
<html:errors />
<br/>
<bean:message key="prompt.id" />
<html:text property="id" />
<br />
<bean:message key="prompt.password" />
<html:password property="password" />
<br />
<html:submit>
<bean:message key="login.login" />
</html:submit>
</html:form>
</body>
</html:html>
<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/app.tld" prefix="app" %>
<app:checkLogin/>
<html:html locale="true">
<head>
<title><bean:message key="mainmenu.title" /></title>
<html:base/>
</head>
<body>
<bean:message key="mainmenu.presentation" />
</body>
</html:html>
Login.jsp는 강좌 1과 같다. mainpage.jsp(강좌 1에서는 mainmenu.jsp)에서 달라진 부분은 <app:checkLogin/>가 추가되었다. checkLogin태그의 역할은 로그인하지 않은 사용자는 로그인 페이지로 이동하도록 하는 역할을 한다. 이 같이 여러페이지에서 사용되는 코드는 커스텀 태그화하여 재사용하면 유용하다.
checkLogin태그의 소스는 다음과 같다.
package net.javajigi.tutorial.tag;
import java.io.IOException;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
import net.javajigi.tutorial.Constants;
import org.apache.struts.action.Action;
import org.apache.struts.util.MessageResources;
import org.apache.struts.config.ModuleConfig;
public final class CheckLoginTag extends TagSupport {
private String name = Constants.USER_KEY;
private String page = "/tutorial/login.jsp";
public String getName() {
return (this.name);
}
public void setName(String name) {
this.name = name;
}
public String getPage() {
return (this.page);
}
public void setPage(String page) {
this.page = page;
}
public int doStartTag() throws JspException {
return (SKIP_BODY);
}
public int doEndTag() throws JspException {
boolean valid = false;
HttpSession session = pageContext.getSession();
if ((session != null) && (session.getAttribute(name) != null))
valid = true;
if (valid)
return (EVAL_PAGE);
else {
try {
pageContext.forward(page);
} catch (Exception e) {
throw new JspException(e.toString());
}
return (SKIP_PAGE);
}
}
/**
* Release any acquired resources.
*/
public void release() {
super.release();
this.name = Constants.USER_KEY;
this.page = "/tutorial/login.jsp";
}
}
CheckLoginTag는 사용자 정보가 세션에 있는지를 판단후에 세션에 사용자 정보가 없을 경우 login.jsp로 이동시키는 역할을 한다. CheckLoginTag의 tld 및 설정은 첨부되는 소스를 통하여 이해하기 바란다.
지금까지 스트러츠 프레임?을 이용하여 로그인예제를 구현하는 과정을 살펴보면서 스트러츠 Configuration과정을 살펴보았다. 스트러츠 Configuration에 대한 전체를 설명하지 않았지만 계속되는 강좌와 예제를 통하여 하나씩 추가하여 설명하도록 하겠다.
다음 강좌에서는 ActionServlet, RequestProcessor, Action, ActionForm등 스트러츠 프레임?의 근간이 되는 클래스들에 대하여 살펴보도록 하겠다.
참고 자료