반응형
(자카르타_스트럿츠완전해부)Section_1.pdf
Posted by 1010
반응형
Struts의 Configuration File인 struts-config.xml에는
다음과 같은 내용이 기술되어 있다.

<struts-config>

1. Data Sources
데이터베이스를 위한 DataSource의 설정.

2. Form Beans
request에 의하여 전달되는 정보를 저장하는 FormBean의 설정.

3. Global Exceptions
Application에서 발생하는 Exception의 설정.

4. Global Forwards
Application에서 요청에 따른 작업을 끝낸후 Forward할 path에 대한 설정.

4. Action Mappings
Model과 통신하는 BusinessLogic을 담고 있는 Action클래스의 설정.

5. Controller
스트러츠 Controller의 설정.

6. Message Resources
Message Resource의 설정.

7. Plugins
다소 생소하겠지만 스트러츠에서는 플러그인이 가능하도록 구현되어 있다. 따라서 플러그인에 대한 설정이 가능하다

</struts-config>

닫기


struts-config.xml 설정에 관해

org.apache.struts.config package는 struts 1.1에서 추가 되었으며 자바 빈을 이용하여 실행 시 XML 설정 파일의 설정 내용을 자바 빈의 인스턴스를 이용하여 담아두는 역할을 합니다.

아래는 org.apache.struts.config에 대한 클래스 다이어그램 입니다.




설정 파일(struts-config.xml)의 검증과 파싱 작업이 마무리 되면 메모리에 올라와 있는 설정 정보를 빈즈 인스턴스를 이용하여 담아 둡니다.

위 클래스 다이어그램에서 ConfigResultSet 클래스는 다른 클래스와 다른 작업을 하는데 주로 struts 설정 파일을 해석 하는데 필요한 규칙을 포함하고 있는데 그 역할은 값을 설정하기 위해 필요한 빈의 인스턴스를 생성 하는 것 입니다.


-------------------------------
Struts-Config.xml 설정
-------------------------------

설정을 위한 DTD

<!ELEMENT struts-config (data-source?, form-beans?, global-exceptions?, global-forwards?, action-mappings?, controller?, message-resources*, plug-in*)

Struts-config.xml 파일에 설정시 위에서 정의한 순서는 지켜져야 합니다.

1.        data-source 요소

데이터 소스에 대한 설정을 하는 곳이며 대부분의 벤더들이 데이터 소스 객체의 구현을 제공 하는데 자바에서는 javax.sql.DataSource 인터페이스를 제공 하며 벤더들이 제공하는 구현 부에서 이 인터페이스를 구현해 주어야 합니다.

DTD정의

<!ELEMENT data-sources (data-source*)>
<!ELEMENT data-source (set-property*)>

예)
<data-sources>
        <!-- configuration for commons BasicDataSource -->
        <data-source key="mysql" type="org.apache.commons.dbcp.BasicDataSource">
                                <set-property property="driverClassName" value="com.mysql.jdbc.Driver" />
                                <set-property property="url" value="jdbc:mysql://localhost/test" />
                <set-property property="username" value="root" />
                <set-property property="password" value="pw" />
                <set-property property="maxActive" value="10" />
                <set-property property="maxWait" value="5000" />
                <set-property property="defaultAutoCommit" value="true" />
                                <set-property property="defaultReadOnly" value="false" />
                <set-property property="maxIdle" value="10000" />
        </data-source>
        <data-source key="oracle" type="org.apache.commons.dbcp.BasicDataSource">
                <set-property property="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
                <set-property property="username" value="scott" />
                <set-property property="password" value="tiger" />
                <set-property property="url" value="jdbc:oracle:thin:@localhost:1521:WINK" />
                <set-property property="maxActive" value="50" />
                <set-property property="maxIdle" value="10000" />
                <set-property property="maxWait" value="100" />
                <set-property property="defaultAutoCommit" value="true" />
        </data-source>
</data-sources>

속성으로 다음과 같은 것들이 있습니다.

key : 데이터 소스가 저장 될 서블릿 컨텍스트 속성, 선택적 이며 기본값은 Action.DATA_SOURCE_KEY 입니다.

type : 데이터 소스 구현체 클래스의 전체 클래스 이름, 반드시 javax.sql.DataSource 클래스를 구현해야 합니다.

2.        form-bean 요소

form-bean 요소는 여러 개의 ActionForm 클래스를 설정 할 수 있게 합니다. Form-beans 요소에서 form-bean 요소를 사용할 수도 있고 안 할 수도 있습니다. 각각의 form-bean 요소 역시 자식 요소를 포함 합니다.

DTD 정의

<!ELEMENT form-bean (icon?, display_name?, description?, set-property*, form-property*)>

가질 수 있는 속성은 다음과 같습니다…

className : 기본값은 FormBeanConfig이며 이 클래스를 사용하지 않는다면 지정한 클래스는 반드시 FormBeanConfig를 상속 해야 합니다.

dynamic : type속성 값이 org.apache.struts.DynaActionForm 클래스나 그 하위클래스라면 이 속성값을 true라고 설정하며 그렇지 않은 경우엔 false이다. 기본값은 false

name : 이 빈의 유일한 식별자

type : ActionForm 클래스를 상속하는 자바 클래스의 전체 이름, 실제 폼빈 역할을 하는 클래스의 이름을 패키지 명까지 포함해서 기술 합니다. 만약 이 값을 org.apache.struts.action.DynaActionForm으로 지정하면 스트럿츠가 DynaActionForm 클래스의 인스턴스를 동적으로 생성 합니다. 속성값은 반드시 지정해야 합니다.

<form-beans>
        <form-bean name="loginForm" type="login2.LoginForm">                  
            <form-property name="pwd" type="java.lang.String" />
            <form-property name="id" type="java.lang.String" />            
        </form-bean>            
    </form-beans>


3.        global-exceptions 요소

예외처리에 관한 설정을 하며 global-exceptions에는 exception 요소가 0개 이상 나타날 수 있습니다.

<!ELEMENT global-exceptions (exception*)>

exception 요소는 action 요소에도 정의가 가능한데 exception 요소가 global-exceptons와 action 요소에 동시에 정의되어 있다면 action에 선언된 것이 우선권을 가집니다.

exception 요소는 클라이언트의 요청을 처리하는 동안 발생하는 자바의 예외상황과 해당 예외를 처리하는 org.apache.struts.action.ExceptionHandler 클래스의 인스턴스를 매핑하는 역할을 합니다.

exception 요소의 DTD는 다음과 같습니다.

<!ELEMENT exception (icon?, display-name?, description?, set-property*)

Exception 요소의 속성은 다음과 같습니다.

className : 예외정보를 가지게 되는 설정 빈의 구현 클래스, 지정한 클래스는 ora.apache.struts.config.ExceptionConfig의 하위 클래스여야 하며 기본값은 ExceptionConfig 클래스 입니다.

handler : 예외 처리를 담당할 예외 처리 클래스의 전체 이름. 값을 지정하지 않으면 org.apache.struts.action.ExceptionHandler 클래스 입니다. 만약 다른 클래스를 지정한다면 ExceptionHandler의 하위클래스 이여야 합니다.

key : 하위 애플리케이션의 리소스 번들에 지정된 키 값이며 ActionError 클래스의 인스턴스에서 이 속성의 값을 사용 합니다.

path : 예외가 발생 시 전송해야 할 리소스의 상대 경로, 만약 값을 지정하지 않으면 action mapping에서 지정하는 input 속성의 값을 사용 합니다.

scope : ActionError 클래스의 인스턴스를 저장 할 스코프, “request” 또는 “session” 이여야 합니다. 기본값은 “request” 입니다.

type : 예외 처리 클래스의 전체 이름

<global-exceptions>
        <exception
                key = “global.error.invalidlogin”
                path = “/login/login.jsp”
                scope = “request”
                type = “login2.exception.InvalidLoginException”
        />
</global-exceptions>


4.        global-forward 요소

모든 Action은 View화면으로 포워드 되거나 리다이렉트 되는 방법으로 실행 됩니다. 뷰의 역할은 JSP가 할 수도 있고 HTML이 할 수도 있습니다. Struts 에서는 뷰 역할을 하는 자원을 직접 참조하는 대신 주로 어떤 이름을 사용 합니다.

global-forwards에서는 0개 이상의 forward요소를 가집니다.

<!ELEMENT global-forwards (forward*)>

forward 요소 역시 몇 개의 자식 요소를 가집니다. forward 요소는 논리적인 이름을 Web Application의 URI,의 상대 경로에 mapping 합니다. 이를 통해 애플리케이션은 논리적인 이름을 통해 포워드 또는 리다이렉트를 수행 합니다.  이러한 방법을 통해 뷰로부터 컨트롤러와 모델을 분리 할 수 있습니다.

<!ELEMENT forward(icon?, display-name?, description, set-property*)>

다음은 forward요소의 속성 입니다.

className : 포워드 설정 값을 포함하게 되는 자바 빈을 구현한 클래스, 기본값은 ForwardConfig 입니다. Optional 값

contextRelative : true로 지정 할 경우 path 속성에 지정된 리소스를 컨텍스트 상대 경로로 인식 합니다, 기본값은 false

name : 애플리케이션에서 이 포워드를 참조하기 위한 유일한 값, 반드시 값을 지정 해야 합니다.

path : 포워드 되거나 redirect 되어야 하는 애플리케이션의 상대 경로(contextRalative가 false인 경우) 또는 컨텍스트의 상대 경로의 URI를 지정,  반드시 “/”로 시작해야 합니다.

<global-forwards>
        <forward name="success" path="/main.jsp"  redirect=”true”/>
        <forward name="logoff" path="/logoff.do" />
        <forward name="login" path="/login.jsp" />
    </global-forwards>

5.        action-mapping 요소

action-mappings 요소는 action 요소를 0개 이상 포함할 수 있습니다.

<!ELEMENT action-mappings(action*)>

Action 요소는 사용자의 요청을 특정한 경로에 해당 하는 Action 클래스와 매핑을 하는 역할을 담당 합니다. Action 요소의 path 속성과 클라이언트로부터 들어온 요청 URI 경로를 매치시켜 특정 매핑을 선택 합니다.

Action 요소의 자식요소는 다음과 같습니다.

<!ELEMENT action (icon?, display-name?, description, set-property*, exception*, forward*)>

앞에서 설명한 exception 요소의 경우 global, action 양쪽에서 선언이 가능하다고 했습니다. 물론 action에서 정의한 exception이 global로 선언 한 것 보다는 우선권이 있습니다.


또한 forward인 경우에도 global, action 양쪽에서 선언이 가능하며 exception의 경우와 비슷합니다.

다음은 action 요소의 속성 입니다.

attribute : 이 action이 접근 가능한 요청 또는 세션 scope의 폼빈 이름, 이속성은 name 속성에 폼빈이 지정되어 있는 경우에만 사용 가능 합니다.

className : 액션과 관련된 정보를 포함하게 되는 빈을 구현한 클래스, 값을 지정하지 않으면 org.apache.struts.action.ActionMapping이 기본 클래스 입니다. 이속성은 선택적 입니다.

forward : 포워드될 서블릿이나 JSP의 상대경로, 이 속성은 선택적이며 org.apache.struts.action.ForwardAction 클래스가 동일한 역할을 하는데 서용 됩니다.

include : 클라이언트의 요청에 대해 포함되어야 할 서블릿이나 JSP의 상대경로

input : 폼빈에서 검증오류가 생기는 경우 돌아갈 곳

name : 폼빈의 이름

path : 액션의 이름, 요청을 처리할 액션의 상대 경로

parameter : action 인스턴스에 어떤 정보를 넘기기 위해 사용, 여기에 값을 지정하면 getParameter  메소드를 이용하여 값을 알아낼 수 있습니다.

prefix : 예를 들어 폼빈의 모든 속성이 “pre_”로 시작한다면 prefix 속성을 세팅해서 요청 인자가 ActionForm 속성과 일치  하도록 할 수 있습니다. Name 속성이 지정된 경우에만 사용 가능 합니다.

scope : 폼빈의 스코프, name 속성을 지정한 경우에만 사용 가능 하며 “request” or “session” 이 들어 올 수 있으며 기본값은 “session” 입니다.

suffix : 예를 들어 폼빈의 모든 속성이 “_foo” 로 끝난다면 suffix 속성을 지정하여 요청인자가 ActionForm 빈의 속성과 일치하게 합니다.

type : Action 클래스를 상속하는 즉 액션을 처리하는 클래스의 전체경로 및 이름

unknown : 이 액션을 애플리케이션에서 기본으로 설정 해야 하는지 여부를 표시 한다. true로 설정하면 이 액션은 처리할 액션이 지정되지 않은 모든 요청을 처리

validate : 폼빈의 validate() 메소드가 execute() 메소드 전에 실행 되어야 하는 지의 여부를 표시, 기본값은 true

<action-mappings>
        <!-- loginAction에 대한 정의 -->
        <action          
                path="/LoginSubmit"
                type="login2.LoginAction"
                    scope=”rewuest”
                name="loginForm"                        
                validate="true"
                input="/login.jsp"
        >        
                   <forward name=”SUCCESS” path=”/success.jsp” redirect=”true”/>
            </action>
    </action-mappings>


6.        controller 요소

struts 1.1이상에서 새로 나온 기능으로 이전 버전에서는 ActionServlet 클래스가 컨트롤러 기능을 하며 이를 상속해서 컨트롤러의 기능을 구현 했습니다. 그러나 1.1이상에서는 대부분의 컨트롤러의 기능은 RequestProcessor 클래스로 이전 했습니다.

1.1이전의 예

<servlet>
<servlet-name>action</servlet-name>
<servlet-class>myPackage.myActionServlet</servlet-class>
</servlet>


ActionServlet은 여전히 사용자의 요청을 받아 들이지만 그 요청에 대한 처리는 RequestProcessor 클래스의 인스턴스에 위임 합니다. 이렇게 함으로서 요청을 처리하는 클래스를 할당하고 기능을 수정 하는 것이 가능 해집니다.

Controller 요소는 자식요소 하나를 포함 합니다.

<!ELEMENT controller (set-property*)>

아래는 controller 요소의 속성 입니다.

bufferSize : 파일 업로드 시 사용하는 입력 버퍼의 크기, 기본값은 4096 입니다.

className : 컨트롤러 정보를 담아놓을 자바 빈을 구현한 클래스, org.apache.struts.config.ControllerConfig의 하위클래스여야 합니다. 기본값은 ControllerConfig 입니다.

contentType : 응답 경과를 보낼 때 사용하는 contentType, 이 속성은 선택적으로 사용 할 수 있으며 기본값은 “text/html” 입니다. 이 속성에 값이 있더라도 JSP 파일에 contentType을 기술 한다면 JSP 파일의 내용이 우선 합니다.

debug : 디버깅 레벨, 값이 클수록 많은 로깅 정보들이 기록되며, 기본값은 0으로 로깅 정보가 기록되지 않습니다.

locale : 사용자의 지역 정보를 세션에 저장 할지 여부, 기본값은 false

maxFileSize : 파일 업로드 시 허용되는 최대 용량

multipartClass : Multipart 요청에 대한 처리를 담당할 클래스, 기본값은 ora.apache.struts.upload 패키지의 DiskMultipartRequestHandler 입니다.

nocache : 응답에 있는 nocache 헤더에 대한 설정, true 또는 false, 기본값은 false, c가 소문자임을 주의

processClass : 사용자의 요청을 처리 할 클래스의 전체 이름

tempDir : 파일 업로드의 경우 사용할 임시 디렉토리


<controller
            contentType="text/html;charset=euc-kr"
            debug="3"            
            locale="true"
            nocache="true"
            processorClass="filter.MyFilter"/>


7.        message-resources 요소

메시지 리소스 번들과 관련된 특징을 기술 합니다.

각각의 스트럿츠 설정 파일은 메시지 리소스 번들을 하나 이상 지정 할 수 있습니다.

<!ELEMENT message-resources (set-propert*)>

필요한 속성만 살펴 보도록 합니다.

className : message-resources의 정보를 담을 자바빈을 구현한 클래스

key : 메시지 리소스 번들이 저장될 서블릿 컨텍스트 속성, 기본값은 Action.MESSAGE_KEY에 의해 지정 됩니다.

null : 정의되지 않은 메시지 키가 사용된 경우 MessageResource의 하위 클래스에서 어떻게 처리 할지를 나타내는 Boolean 값. true로 설정하면 빈 문자열을 돌려줍니다. false로 설정하면 “???global.label.missing???”와 같은 문자를 돌려 줍니다.

parameter : 예를 들어 리소스 번들의 이름이 Application.properties 라면 이 속성의 값은 Application이 됩니다. 반드시 정의해야 하는 속성 입니다. 만약 리소스 번들이 패키지화 되어 있다면 전체 경로를 지정 해야 합니다.

<message-resources parameter="resources.application"/>


8.        plug-in 요소

struts application이 구동 시에 동적인 자원을 처리 할 수 있게 해주는 기능

예를 들어 애플리케이션 구동 시 원격의 시스템에 연결 해야 된다면 직접 코딩하지 않고 플로그인 기능을 이용하여 이 기능을 구현할 수 있습니다. 플러그인을 사용하기 위해서는 org.apache.struts.PlugIn 인터페이스를 구현하는 자바 클래스를 생성 한 후 설정 파일의 plug-in 요소에 추가 하면 됩니다.

public interface PlugIn {
public void init(ActionServlet servlet, ApplicationConfig config)
throws ServletException;
public void destroy();
}


1.1이전 버전에서는 ActionServlet의 subClass를 통해 애플리케이션 구동 시 애플리케이션의 리소스를 초기화 했지만 1.1이후 버전에서는 Plug-In을 통해 이것이 가능 하게 됨

Plug-In의 경우 스트럿츠 애플리케이션이 초기화 되는 경우 ActionServlet이 Plug-In의 Init() method 를 호출 합니다.

Plug-In은 struts-config.xml 파일에 기술된 순서에 따라  호출됩니다.

<!ELEMENT plug-in (set-property*)>

<plug-in className="org.apache.struts.plugins.ModuleConfigVerifier"/>
<plug-in
className="org.apache.struts.webapp.example.memory.MemoryDatabasePlugIn">
<set-property property="pathname" value="/WEB-INF/database.xml"/>
</plug-in>

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,
/WEB-INF/validation.xml"/>
</plug-in>

예제

public final class MemoryDatabasePlugIn implements PlugIn {
...

        public void init(ActionServlet servlet, ModuleConfig config) throws ServletException {
                log.info("Initializing memory database plug in from '" +        pathname + "'");
                // Remember our associated configuration and servlet
                this.config = config;
                this.servlet = servlet;

                // Construct a new database and make it available
                database = new MemoryUserDatabase();
                try {
                        String path = calculatePath();
                        if (log.isDebugEnabled()) {
                                log.debug(" Loading database from '" + path + "'");
                        }
                        database.setPathname(path);
                        database.open();
                } catch (Exception e) {
                        log.error("Opening memory database", e);
                        throw new ServletException("Cannot load database from '" +        pathname + "'", e);
                }

                // Make the initialized database available
                servlet.getServletContext().setAttribute(Constants.DATABASE_KEY,database);

                // Setup and cache other required data
                setupCache(servlet, config);
        }
}

public final class MemoryDatabasePlugIn implements PlugIn {
...

        public void destroy() {
                log.info("Finalizing memory database plug in");
                if (database != null) {
                        try {
                                database.close();
                        } catch (Exception e) {
                                log.error("Closing memory database", e);
                        }
                }

                servlet.getServletContext().removeAttribute(Constants.DATABASE_KEY);
                database = null;
                servlet = null;
                database = null;
                config = null;
        }
}
Posted by 1010
반응형

1장. 사용자 관리 프로젝트로 알아본 스트럿츠 탄생 배경

Summary : 스트럿츠 프레임워크 워크북의 1장 원고를 통하여 스트럿츠 탄생 배경에 대하여 총 5번의 강좌를 통하여 알아본다. 이번 강좌에서는 사용자 관리 프로젝트의 모델 파트를 개발한다. 이번 강좌에서 개발되는 모델은 앞으로 진행할 3번의 강좌에서 공통적으로 사용한다.

2.모델(Model) 부분의 개발

지금까지 사용자 관리를 위한 요구분석을 진행하였다. 이 절에서는 앞절의 요구분석을 바탕으로 사용자 관리 프로젝트의 모델(Model)부분을 개발하도록 하겠다. 이 절에서 개발되는 모델은 계속해서 진행될 모델1 개발방식, 모델2 개발방식, 스트럿츠 프레임워크를 이용한 개발에 공통적으로 사용될 것이다.

이 절에서 개발되는 모델이 3가지 개발방식에 모두 사용되기 때문에 모델1 개발방식에는 적합하지 않을 수도 있다. 그렇지만, 모델1 개발방식도 이 절에서 살펴보는 방식으로 모델을 개발할 경우 유지보수가 편해질 수 있으며, JSP(Java Server Pages : 이하 JSP)의 복잡도를 다소나마 줄일 수 있을 것이다.

2.1 실행환경 설정

사용자 관리 모델의 직접적인 개발에 들어가기에 앞서 기본적인 실행환경을 설정하도록 하겠다. 이 절에서는 실행환경 설정에 대한 설명은 최대한 줄이도록 하겠다. 부족한 부분은 부록과 다양한 외부 문서를 링크하는 방식을 취하도록 하겠다.

이 책에서 다룰 예제들의 기본적인 실행환경은 다음과 같다.

JDK : 1.3이상

Servlet Server : Jakarta-Tomcat 4.1.24

관계형 데이터베이스 : MySQL 4.0.x

JDBC 드라이버 : MySQL JDBC 드라이버 2.0.14

Connection Pooling :

빌드 툴 : Apache Ant 1.5.3

스트러츠 프레임워크 : Jakarta-Struts 1.1

위에서 살펴본 항목들이 이 책의 예제들을 개발하고, 테스트해보기 위한 기본적인 환경이다. 컨넥션 풀링을 사용한 이유는 최근에 진행하는 대부분의 프로젝트에 컨넥션 풀링을 사용하고 있기 때문에 실전 프로젝트에서 직접 사용이 가능하도록 하기 위해서이다. MySQL JDBC 드라이버는 최신 버전인 3.0.8을 사용하지 않고 2.0.14로 사용한 이유는 최신 버전인 3.0.8는 데이터를 읽을 때 버그가 있기 때문이다.

이 책의 예제를 실행하기 위해서는 JDK, MySQL데이터베이스, Tomcat 애플리케이션 서버, ANT가 설치되어 있어야 한다. 나머지 패키지는 예제 소스에 포함되어 있기 때문에 신경쓰지 않아도 된다. 예제를 실행하는 환경을 위와 같이 선택한 이유는 예제 소스를 최대한 쉽게 테스트해볼 수 있도록 위함이다. 모든 툴이 설치가 간단하고 좋은 사양의 컴퓨터가 아니라도 테스트가 가능하다.

모든 예제소스는 ANT툴로 빌드하여 서버에 디플로이 시키기 때문에 예제를 실행하기 위하여 클래스패스를 추가할 필요가 없다. ANT툴에 대한 자세한 사용방법은 부록2를 참조하기 바란다.

JDK, MySQL데이터베이스, Tomcat 애플리케이션 서버, ANT중 설치하는 방법을 모르는 것이 있다면 이 책의 부록 1을 참고하여 예제를 실행해볼 수 있는 기본 환경을 세팅하기 바란다.

2.2 사용자 관리 테이블과 생성 쿼리

사용자 관리 테이블은 1절의 요구분석에서
정의했듯이 사용자 아이디, 비밀번호, 사용자 이름, 사용자 이메일주소만을 포함하도록 생성하였다. 가능한 작은 규모의 프로젝트를 만들기 위한 의도였다.

# DROP TABLE USERINFO;

CREATE TABLE USERINFO ( 	
userId          varchar(12)		NOT NULL, 	
password		varchar(12)		NOT NULL,	
name			varchar(20)		NOT NULL,	
email			varchar(50),  		
PRIMARY KEY               (userId),  		
INDEX USERINFO_userId_idx  (userId));

INSERT INTO USERINFO VALUES('admin', 'admin', '관리자', 'admin@javajigi.net');

2.3 데이터베이스 설정 및 테이블 생성
사용자 관리 모델을 만들기 위하여 먼저 데이터베이스 정보를 설정한 다음 테이블을 생성해야 한다. 데이터베이스 설정은 컨넥션 풀링에서 사용하고 있는 /chapter1/src/dbpool.properties의 값을 자신의 데이터베이스 환경에 맞도록 설정하면 된다. dbpool.properties의 내용은 이 장 초입부의 예제 실행 방법란을 참고하기 바란다.

사용자 관리용 테이블 생성은 ANT를 이용하였다. ANT를 이용한 이유는 독자들의 테스트를 쉽고 빠르게 하기 위해서이다. 많은 책들의 경우 너무도 많은 환경 설정과 클래스패스를 추가해주어야 하기 때문에 독자들이 테스트하는데 많은 시간을 소비해왔다. 따라서 필자는 테스트하는데 가능한 적은 시간을 소비하고, 책에서 다루고있는 내용에 독자들이 많은 시간을 투자하여 공부할 수 있게하기 위함이다. 그 일환으로 테이블의 생성 또한 ANT를 이용하였다.

ANT를 이용하여 테이블을 생성하는 자세한 방법은 이 장 초입부의 예제 실행 방법란을 참고하기 바란다.

2.4 모델 개발

많은 개발자들이 모델이라는 용어를 사용한다. 하지만, 대부분의 개발자들이 모델파트에서 담당해야하는 부분이 무엇인지에 대하여 모르고 있다. 필자 또한 MVC 아키텍처(모델, 뷰, 컨트롤러 계층으로 나뉘어 개발. 이하 MVC)에서 모델파트가 담당해야하는 부분이 무엇이라고 정확하게 경계선을 그어서 말하기는 힘들다. 각 계층의 경계선을 명확히 했다고 하지만 경우에 따라 경계가 무너지는 경우도 종종 본다.

하지만 간략하게나마 모델이 담당해야할 역할에 대해서 규명할 필요는 있다고 생각한다. 따라서 필자는 모델파트에서 담당해야하는 가장 큰 역할을 다음 두가지로 규명하겠다.

첫째, 애플리케이션의 비즈니스 로직적인 부분을 담당한다.

둘째,
애플리케이션이 데이터베이스, 레거시 시스템, 파일 시스템과의 데이터 조작등의 작업을 담당한다.

이와 같은 기본적인 개념하에 이 책에서 진행하게될 모델 예제들을 위한 기본적인 클래스 설계는 다음과 같이 진행하도록 하겠다.

첫째, 객체의 정보를 포함하고 있는 도메인 클래스이다. 도메인 클래스의 역할은 사용자가 입력한 정보를 모델에 전달하는 역할과 데이터베이스의 정보를 JSP에 전달하는 매개자 역할을 한다. 이 개념은 Value Object 패턴과 Data Transfer Object 패턴이라는 이름으로 많이 알려져 있다. 각각의 기능으로 나누어 개발할 수도 있지만 대부분의 Value Object와 Data Transfer Object는 같은 속성과 setter, getter를 가지고 있기 때문에 필자는 하나의 클래스로 이 두가지 기능을 모두 포함하도록 구현하기를 좋아한다.

참고문서

Value Object 패턴 :

  • "Patterns of Enterprise Application Architecture", Martin Fowler, 2003, Addison Wesley 486Page
  • "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 261Page

Data Transfer Object패턴 :

  • "Patterns of Enterprise Application Architecture", Martin Fowler, 2003, Addison Wesley 401Page

둘째, 데이터베이스나 기존의 레거시 시스템과의 데이터작업을 전담하게될 데이터 접근 객체(Data Access Object:DAO)클래스이다. 모델 개발시 중요한 역할중의 하나는 데이터베이스나 기존의 레거시 시스템과의 데이터 교환작업이다. 비즈니스 로직과 별개로 이 같은 작업을 전담하는
클래스가 필요하게 되었다. 따라서 앞으로 진행되는 모든 프로젝트에서 데이터 접근 로직은 전담하는 이 같은 DAO클래스가 존재할 것이다. DAO클래스라고 해서 색다른 것이 아니다. 단지 독자들이 JSP와 여러 클래스에서 데이터베이스와의 데이터교환을 하던 부분을 하나의 클래스에서 전담하는 역할을 한다고 생각하면 된다.

참고문서

Data Acces Object 패턴 :

  • "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 390Page

셋째, 비즈니스 로직을 전담하게될 비즈니스 객체(Business Object:BO)클래스이다. 단, 비즈니스 로직이 간단할 경우에 이 클래스는 생략할 수 있다. 모델을 개발할 때 또 하나의 중요한 부분은 비즈니스 로직을 담당하는 부분이다. 프로젝트의 성격에 따라 비즈니스 로직이 복잡한 경우에는 비즈니스 로직을 전담할 비즈니스 객체가 필요한 경우가 발생할 수 있다. 하지만, 간단한 경우에는 네번째로 살펴볼 Manager클래스에서 비즈니스 로직을 담당할 수도 있다.

넷째, 모델1 개발방식이라면 JSP, 모델2 개발 방식(MVC)이라면 컨트롤러(Controller)가 직접적으로 사용하게될 API를 전담하는 Manager클래스이다. Manager클래스를 만들 경우 비즈니스 로직이 간단할 경우에는 Manager클래스에서 비즈니스 로직을 수행할 수도 있으며, 모델을 접근할 때 Manager클래스만을 통해 접근하기 때문에 API를 사용하기 편하다는 장점이 있다. 사용자가 접근해야 하는 클래스가 여러 클래스에 분산되어 있다면, API사용에 익숙하지 않은 개발자들은 모델을 사용하는데 어려움을 느낄 수 있다. Manager클래스를 두는 방식을 Session Facade 패턴이라고 일컫는다.

참고문서

Session Facade 패턴 :

  • "Core J2EE Patterns" Deepak Alur/John Crupi/Dan Malks, 2001, Prentice Hall 291Page

지금까지 살펴본 4개 클래스의 관계를 살펴보면 [그림 1-8]과 같다.

모델 API를 사용하기 위하여 Manager클래스만을 접근하는 것을 볼 수 있다. Manager클래스는 도메인 클래스에 담긴 데이터를 비지니스 객체와 데이터 접근 객체에 전달하는 역할을 하며, 작업후 결과 반환되는 도메인 클래스를 JSP, Servlet, Action에 전달하는 역할을 한다.

사용자 관리는 1절의 요구사항에서도 볼 수 있듯이 복잡한 비즈니스 로직을 포함하고 있지 않다. 따라서 비즈니스 객체를 따로 두지않고 Manager클래스에서 비즈니스 로직을 수행하도록 구현하였다. 사용자 관리 모델파트의 클래스 다이어그램은 다음과 같다.[그림1-9]

먼저 사용자의 정보를 담고 있는 도메인 클래스를 살펴보자. 사용자 관리의 도메인 클래스는 USERINFO테이블의 각 칼럼과 일치하는 속성들을 가지고 있으며, 각각의 속성에 대한 setter와 getter 메소드를 가진다.

package net.javajigi.user;

/** 
 * 사용자 관리를 위하여 필요한 도메인 클래스. 
 * USERINFO 테이블의 각 칼럼에 해당하는 setter와 getter를 가진다.  
 */
public class User {
	private String userId = null;
	private String password = null;
	private String name = null;
	private String email = null;
	public String getEmail() {
		return email;
	}
	public String getName() {
		return name;
	}
	public String getPassword() {
		return password;
	}
	public String getUserId() {
		return userId;
	}
	public void setEmail(String string) {
		email = string;
	}
	public void setName(String string) {
		name = string;
	}
	public void setPassword(String string) {
		password = string;
	}
	public void setUserId(String string) {
		userId = string;
	} 
	
	/**	 
	 * 비밀번호가 일치하는지 여부를 결정하는 메소드.	
	 */
	public boolean isMatchPassword(String inputPassword) {
		if (getPassword().equals(inputPassword)) {
			return true;
		} else {
			return false;
		}
/* This is the better.
                return getPassword().equals(inputPassword);
*/
	}
}

[예제 1-1]의 User 도메인 클래스에도 비밀번호가 일치하는지의 여부를 체크하는 간단한 메소드를 가진다.

UserDAO는 사용자 관리 테이블인 USERINFO와의 데이터 작업을 전담하는 클래스이다.

package net.javajigi.user;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import net.javajigi.db.ConnectionManager;

/**
 * 사용자 관리에서 데이터베이스와의 작업을 전담하는 클래스.
 * UserInfo 테이블에 사용자를 추가, 수정, 삭제, 검색등의 작업을 한다.  
 */
public class UserDAO {

	/**	
	 * 사용자 관리 테이블에 새로운 사용자 생성.	 
	 */
	public int create(User user) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			StringBuffer insertQuery = new StringBuffer();
			insertQuery.append("INSERT INTO USERINFO VALUES ");
			insertQuery.append("(?, ?, ?, ?)");
			con = ConnectionManager.getConnection();
			pstmt = con.prepareStatement(insertQuery.toString());
			pstmt.setString(1, user.getUserId());
			pstmt.setString(2, user.getPassword());
			pstmt.setString(3, user.getName());
			pstmt.setString(4, user.getEmail());
			int result = pstmt.executeUpdate();
			pstmt.close();
			con.close();
			return result;
		} finally {
			if (pstmt != null) {
				pstmt.close();
			}
			if (con != null) {
				con.close();
			}
		}
	}

	/**	 
	 * * 기존의 사용자 사용자 정보를 수정.	 */
	public int update(User user) throws SQLException {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	
	}

	/**	 
	 * 사용자 아이디에 해당하는 사용자를 삭제.	 
	 */
	public int remove(String userId) throws SQLException {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	
	}

	/**	 
	 * 사용자 아이디 정보를 데이터베이스에서 찾아 User 도메인 클래스에 	 
	 * 저장하여 반환.	 
	 */
	public User findUser(String userId) throws SQLException { 
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	
	}
	
	/**	 
	 * 사용자 리스트를 만들기 위한 부분으로 현재 페이지와 	
	 * 페이지당 카운트수를 이용하여 해당부분의 사용자만을 List콜렉션에	 
	 * 저장하여 반환.	 
	 */
	public List findUserList(int currentPage, int countPerPage) {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.	

	}
	/**	
	 * 인자로 전달되는 아이디를 가지는 사용자가 존재하는지의 
	 * 유무를 판별. 	 
	 */
	public boolean existedUser(String userId) throws SQLException {
		//구체적인 구현 부분은 첨부되는 CD의 예제소스 참조.		
	}
}

[예제 1-2]를 보면 데이터베이스와의 데이터 교환 작업 이외에 다른 작업은 존재하지 않음을 확인할 수 있다.

package net.javajigi.user;

import java.sql.SQLException;
import java.util.List;

/** 
 * 사용자 관리 API를 사용하는 개발자들이 직접 접근하게 되는 클래스. 
 * UserDAO를 이용하여 데이터베이스에 데이터 조작 작업이 가능하도록 하며, 
 * 데이터베이스의 데이터들을 이용하여 비지니스 로직을 수행하는 역할을 한다. 
 * 비지니스 로직이 복잡한 경우에는 비지니스 로직만을 전담하는 클래스를  
 * 별도로 둘 수 있다. 
 */
public class UserManager {
	private UserManager() {
	}
	public static UserManager instance() {
		return (new UserManager());
	}
	public int create(User user) throws SQLException, ExistedUserException {
		if (getUserDAO().existedUser(user.getUserId())) {
			throw new ExistedUserException(user.getUserId() + "는 존재하는 아이디입니다.");
		}
		return getUserDAO().create(user);
	}
	public int update(User user) throws SQLException {
		return getUserDAO().update(user);
	}
	public int remove(String userId) throws SQLException {
		return getUserDAO().remove(userId);
	}
	public User findUser(String userId) throws SQLException, UserNotFoundException {
		User user = getUserDAO().findUser(userId);
		if (user == null) {
			throw new UserNotFoundException(userId + "는 존재하지 않는 아이디입니다.");
		}
		return user;
	}
	public List findUserList(int currentPage, int countPerPage) throws SQLException {
		return getUserDAO().findUserList(currentPage, countPerPage);
	}
	public boolean login(String userId, String password)
		throws SQLException, UserNotFoundException, PasswordMismatchException {
		User user = findUser(userId);
		if (!user.isMatchPassword(password)) {
			throw new PasswordMismatchException("비밀번호가 일치하지 않습니다.");
		}
		return true;
	}
	private UserDAO getUserDAO() {
		return new UserDAO();
	}
}

[예제 1-3]은 사용자 관리 API를 사용하는 개발자들이 직접 접근하게 되는 클래스로서UserDAO를 이용하여 데이터베이스에 데이터 조작 작업이 가능하도록 하며, 데이터베이스의 데이터들을 이용하여 비지니스 로직을 수행하는 역할을 한다.

이상으로 사용자 관리를 위한 3개의 클래스를 모두 살펴보았다. 사용자 관리 프로젝트의 규모가 작기 때문에 위 3개의 클래스만으로 모델을 구현하는 것이 가능했다.

강좌에 대하여

작성자 : 박재성
작성일 : 2005년 2월 20일

문서이력 :

  • 2005년 2월 20일 박재성 문서 최초 생성

참고 자료

Posted by 1010
반응형

1장. 사용자 관리 프로젝트로 알아본 스트럿츠 탄생 배경

Summary : 스트럿츠 프레임워크 워크북의 1장 원고를 통하여 스트럿츠 탄생 배경에 대하여 총 5번의 강좌를 통하여 알아본다. 이번 강좌에서는 사용자 관리 프로젝트에 대한 간단한 요구분석을 진행해 보도록 하겠다. 프로젝트라는 용어를 사용해서 사용자 관리 프로젝트가 상당히 거창하게 들릴지도 모른다. 그렇지만 독자들이 생각하는 것만큼 거창한 프로젝트는 아니다. 오히려 너무 간단해서 "이게 무슨 프로젝트야?"라고 실망감이 클지도 모르겠다.

예제 실행 방법

스트럿츠의 세부 항목을 살펴보기에 앞서 사용자 관리프로젝트 예제를 먼저 실행해보는 것이 이 장을 이해하는데 많은 도움이 될 것이다. 따라서 사용자 관리프로젝트 예제를 실행하는 방법에 대하여 살펴보도록 하겠다. 1장 예제의 디렉토리 구조를 살펴보면 다음과 같다.

[그림 1-1]은 이 장에서 개발하게 될 사용자 관리프로젝트의 디렉토리 구조이다. 1장 예제를 실행하기 위한 과정을 살펴보면 다음과 같다. MySQL, Tomcat, ANT의 설치에 관한 자세한 내용은 부록 1을 참조하기 바란다. 다음 설명은 MySQL, Tomcat, ANT가 설치되어 있다는 가정하에 설명을 진행하도록 하겠다.

사용자 관리 프로젝트를 위한 테이블 생성

1) chapter1/src 디렉토리를 보면 dbpool.properties파일이 존재한다. 이 파일은 MySQL데이터베이스에 대한 정보를 담고 있다. 이 파일을 자신의 데이터베이스 환경에 맞도록 수정해준다. dbpool.properties파일의 내용은 다음과 같다.

#JDBC Driver 등록한다.
drivers=org.gjt.mm.mysql.Driver

#사용할 데이터베이스의 URL
mysql.url=jdbc:mysql://localhost:3306/struts?useUnicode=true&characterEncoding=KSC5601

#사용할 데이터베이스의 사용자 아이디
mysql.user=javajigi

#사용할 데이터베이스의 비밀번호
mysql.password=javajigi

#초기 Connection pooling 사이즈를 입력한다.
mysql.initsize=5#Connection pooling 

최대 사이즈를 입력한다.
Mysql.maxpool=10

2) MySQL데이터베이스를 시작한 후 프롬프트 상에서chapter1 디렉토리로 이동하여 "ant db"를 실행하면 사용자 관리 프로젝트 예제에 필요한 테이블이 생성된다. 자료실 게시판을 위한 SQL스크립트는 chapter1/resources 디렉토리 아래의 chapter1.sql파일을 보면 된다.

[그림 1-2]와 같은 결과화면을 볼 수 있다면 데이터베이스 설정 및 테이블 생성이 제대로 된 것이다.

3) "ant db"를 실행할 때 빌드가 실패하면 두가지 원인이 있다. 첫번째는 dbpool.properties파일의 설정정보가 맞지 않을 경우이다. dbpool.properties파일에서 잘못된 부분을 수정한 다음 다시 실행한다. 두번째는 테이블을 drop할 때 테이블이 존재하지 않거나, 테이블을 생성할 때 테이블이 이미 존재할 때 발생한다. 이때는 에러 메시지에 따라 drop SQL의 주석을 추가하거나 제거해주면 된다.

사용자 관리 프로젝트 예제를 빌드.

사용자 관리 프로젝트 예제를 빌드하는 방법은 간단하다. 이 장에서 개발한 사용자 관리 프로젝트 예제는 모델1, 모델2, 스트럿츠 3가지 개발 방식으로 개발되었다. 따라서 3가지 예제를 각각 실행해 보기 위하여 빌드 또한 3가지 방법으로 할 수 있도록 구현하였다. 따라서 빌드하는 과정을 3가지 방법으로 살펴보도록 하겠다.

모델 1

1) 모델 1 개발 방식으로 개발한 예제를 보기 위해서는 프롬프트에서 chapter1로 이동한 다음 "ant model1" 명령어를 실행하여 사용자 관리 프로젝트 예제를 빌드한다.

2) 빌드가 정상적으로 진행되면 chapter1 디렉토리 하위에 dist라는 디렉토리가 생성되면서 chapter1.war파일이 생성된다.

3) 만약 시스템 환경 변수에 CATALINA_HOME 이 설정되어 있다면 CATALINA_HOME/webapps 디렉토리에 chapter1.war파일이 자동으로 디플로이 된다. 시스템 환경 변수에 CATALINA_HOME이 설정되어 있지 않다면 chapter1/dist디렉토리 아래의 chapter1.war파일을 CATALINA_HOME/webapps 에 복사한다.

4)
Tomcat서버를 시작한 다음 http://localhost:8080/chapter1/model1/login.jsp URL로 접근하여 모델1 방식으로 개발한 사용자 관리 프로젝트 예제를 테스트 할 수 있다. 자세한 테스트 방법 및 설명은 3절의 본문을 참조하기 바란다.

모델 2

1) 모델 2 개발 방식으로 개발한 예제를 보기 위해서는 프롬프트에서 chapter1로 이동한 다음 "ant model2" 명령어를 실행하여 사용자 관리 프로젝트 예제를 빌드한다.

2) 빌드가 정상적으로 진행되면 chapter1 디렉토리 하위에 dist라는 디렉토리가 생성되면서 chapter1.war파일이 생성된다.

3) 만약 시스템 환경 변수에 CATALINA_HOME 이 설정되어 있다면 CATALINA_HOME/webapps 디렉토리에 chapter1.war파일이 자동으로 디플로이 된다. 시스템 환경 변수에 CATALINA_HOME이 설정되어 있지 않다면 chapter1/dist디렉토리 아래의 chapter1.war파일을 CATALINA_HOME/webapps 에 복사한다.

4) Tomcat서버를 시작한 다음 http://localhost:8080/chapter1/model2/user_list.m2?command=list URL로 접근하여 모델2방식으로 개발한 사용자 관리 프로젝트 예제를 테스트 할 수 있다. 자세한 테스트 방법 및 설명은 4절의 본문을 참조하기 바란다.

스트럿츠

1) 스트럿츠 개발 방식으로 개발한 예제를 보기 위해서는 프롬프트에서 chapter1로 이동한 다음 "ant struts" 명령어를 실행하여 사용자 관리 프로젝트 예제를 빌드한다.

2) 빌드가 정상적으로 진행되면 chapter1 디렉토리 하위에 dist라는 디렉토리가 생성되면서 chapter1.war파일이 생성된다.

3) 만약 시스템 환경 변수에 CATALINA_HOME 이 설정되어 있다면 CATALINA_HOME/webapps 디렉토리에 chapter1.war파일이 자동으로 디플로이 된다. 시스템 환경 변수에 CATALINA_HOME이 설정되어 있지 않다면 chapter1/dist디렉토리 아래의 chapter1.war파일을 CATALINA_HOME/webapps 에 복사한다.

4) Tomcat서버를 시작한 다음 http://localhost:8080/chapter1/user_list.do URL로 접근하여 스트럿츠로 개발한 사용자 관리 프로젝트 예제를 테스트 할 수 있다. 자세한 테스트 방법 및 설명은 5절의 본문을 참조하기 바란다.

기타

1) 웹 애플리케이션 기본 디렉토리 구조로 생성된 예제는 chapter1/build 디렉토리에 생성된다. 따라서 [그림 1-1]의 디렉토리 구조가 생소한 독자들은 빌드를 실행한 다음 chapter1/build 디렉토리를 참조하면 쉽게 이해할 수 있다.

2) 각각의 개발 방법으로 새로 빌드하면 이전에 빌드한 디렉토리 내용은 삭제된다. 따라서 모델 1방식으로 개발된 사용자 관리 프로젝트의 예제 소스만을 보고 싶다면 "ant model1"으로 빌드한 다음 chapter1/build 디렉토리를 보면 된다. 모델2, 스트럿츠 또한 동일한 방법으로 확인할 수 있다.

빌드시 주의할 점과 궁금한 점.

1) 처음 빌드할 때는 상관없지만 한번 빌드한 예제의 소스를 수정한 다음 다시 빌드하고자 할 때는 Tomcat서버를 멈춘 상태에서 해야한다. 이유는 Tomcat서버가 시작된 상태일 경우 이전에 디플로이했던 디렉토리가 삭제되지 않는 문제가 발생해 빌드가 실패하는 경우가 발생하기 때문이다. 따라서 빌드는 항상 Tomcat서버가 멈춘 상태에서 실시해야 한다.

2) [그림
1-1]을 보면 WEB-INF디렉토리와 컴파일된 class파일이 없는 것을 볼 수 있다. 또한 독자들이 첨부된 CD의 예제소스를 보면 web.xml, struts-config.xml 파일들이 보이지 않는다. 이 파일들은 ANT로 빌드하면 자동생성되기 때문에 빌드를 실행한 다음 chapter1/build/WEB-INF 디렉토리에서 찾을 수 있다. 따라서 예제 소스에 보면 web.xml, struts-config.xml파일들이 없다고 의아해할 필요는 없다.

3) 모델 2나 스트럿츠를 기반으로한 애플리케이션을 개발하기 위해서는 기존의 모델1에 추가적으로 설정해야하는 정보가 있다. 그러나 이 장의 예제는 ANT로 빌드시 자동적으로 설정되기 때문에 추가적인 설정이 필요없다.

4) 예제 소스를 독자들이 수정해보고 싶다면 src디렉토리의 예제 소스나 JSP파일을 수정한 다음 새로 빌드하면 자동 컴파일 및 war로 압축되기 때문에 독자들이 수정한 예제를 테스트해볼 수 있다.

예제 테스트시 고려할 사항.

1) 이 장에서 개발한 예제는 사용자 관리 프로젝트를 위한 최소한의 기능을 포함하고 있다. 사용자 추가와 같이 새로운 내용을 추가, 수정하는 부분에서 자바스크립트를 이용한 데이터 검증과 같이 세세한 부분의 구현은 빠져있다. 예제를 테스트하는 사람이 개발자로 생각했기 때문에 비정상적으로 발생하는 부분까지 고려하지 않았다.

2) 실제 사용자 관리 프로젝트라면 권한에 따라 기존의 사용자를 수정, 삭제할 수 있겠지만 권한 기능이 빠져 있기 때문에 로그인이 가능한 상태에서는 모든 사용자들의 정보를 수정, 삭제할 수 있도록 구현하였다.

서두

초기 웹은 하이퍼 텍스트로 대표되는 HTML로 작성된 문서를 통하여 급속하게 발전하였다. 초창기 웹은 정적인 문서를 다루도록 설계되었으나, 웹이 점차 발전하면서 사용자들의 요구수준 또한 높아지면서 동적인 서비스를 요구하게 되었다.

이와 같은 동적인 페이지의 생성을 위하여 CGI, PHP, ASP, JSP등 다양한 언어가 등장하게 되었다. 이 같은 기술은 웹 환경을 획기적으로 변화시켰으며, 더 나은 웹 환경을 만들어내기 위한 기틀을 마련할 수 있었다. 그러나 웹 환경이 지속적으로 발전하면서 사용자들의 요구수준 또한 한층 높아지게 되었으며, 기존에 클라이언트/서버 환경으로 존재하던 수많은 애플리케이션이 웹 기반으로 전환하는 과정까지 진행되었다.

동적인 페이지들이 등장하기 시작했을 때는 웹 애플리케이션을 얼마나 빨리 만들어내느냐가 최대의 관심사였다. 따라서 고객이 원하는 결과물을 최대한 빨리 만들어 내는 기업들이 IT업계에서 인정받을 수 있었으며, 급성장하는 결과를 가져올 수 있었다. 그러나 시간이 지나면서 많은 기업들은 배보다 배꼽이 더 크다는 것을 느낄 수 있었다. 즉, 초기 웹 애플리케이션을 구축하는 비용보다 유지보수하는 비용이 몇배 더 발생하게 되었다. 또한 급속히 변하는 웹 환경에 기존의 개발 방법으로는 신속히 대응하기 힘들다는 것을 느끼기 시작했다.

이 같은 이유로 기존의 모델1개발 방식의 한계를 느끼게 된 개발자들은 MVC(Model, View, Controller)모델에 기반한 모델2라는 개발 방법론을 웹에 적용하게 된다. 하지만 모델2방식으로 전환하려고 했던 많은 개발자들은 너무도 복잡한 개발 방법과 재사용성에 한계를 느끼고, 이전의 개발 방법인 모델 1방식으로 되돌아가게 되었다. 모델2 방식으로 개발하기 위해서는 코드의 재사용성이 가능해야했으며, 웹 애플리케이션을 쉽게 만들 수 있도록 지원하는 기반이 마련되어야 했다. 이러한 필요성으로 인해 스트럿츠
프레임워크가 탄생하게 되었다. 모델1, 모델2, 스트럿츠에 대한 자세한 내용은 앞으로 자세하게 다룰 것이기 때문에 각각의 개발 방법을 모른다고 너무 걱정하지 말기 바란다.

스트럿츠 프레임워크의 기본 골격은 모델2와 같이 MVC 모델에 기반하고 있다. 모델2에서 부족했던 많은 부분들을 프레임워크 형태로 만들어 많은 부분의 재사용성을 높일 수 있도록 하였다. 따라서 아무런 기반도 없는 환경에서 모델2방식으로 개발을 시도했던 많은 개발자들이 더 쉽게 웹 애플리케이션을 만들 수 있는 기반을 제공하고 있는 것이다. 개발자들은 웹 애플리케이션의 하부에 대한 고민은 최소화하고, 애플리케이션의 비즈니스 로직에 집중할 수 있게 된 것이다.

책을 시작하는 이 장에서는 대부분의 웹 애플리케이션에 등장하는 사용자 관리 프로젝트를 모델1, 모델2, 스트럿츠 프레임워크 3가지 방법으로 개발해본다. 먼저 지금까지 가장 많은 개발자들이 이용하고 있는 모델1 방식에 대하여 살펴보겠다. 모델 1 개발방식의 장,단점을 살펴본 다음, 모델2 개발방식으로 변화되어 가는 과정을 예제를 통하여 이해할 수 있도록 하겠다. 모델 1에서 모델2로 바뀐다는 것은 기존의 웹 개발방식에 대한 사고의 전환을 필요로한다. 모델2로의 사고전환에 가장 핵심적인 부분에 대해서도 알아볼 것이다. 마지막으로 모델2의 한계점에 대하여 알아본 다음, 스트러츠와 같은 프레임워크가 등장할 수 밖에 없었던 배경에 대하여 살펴보겠다.

이 책은 스트러츠의 실전 활용 예제들을 중심으로 다루고 있는 책이다. 하지만 스트러츠에 대하여 바로 들어가지 않는 이유는 모델1, 모델2개발방식을 거쳐 스트러츠가 만들어지게된 배경이 무엇보다도 중요하기 때문이다. 또한, 모델 1 개발 방법에서 모델2 개발 방법으로 옮겨가기 위해서는 기존의 개발 방법에 대한 생각을 완전히 깰 필요가 있다. 기존에 가지고 있던 생각을 깨기에 복잡한 구조로 구성되어 있는 스트러츠
프레임워크를 바로 다루기 보다는 모델2를 통하여 사고의 전환을 꾀하고자 했다. 마지막 이유는 간단한 모델2 예제를 통하여 MVC모델에 대하여 깊이 있게 이해하려는 의도이다. 필자가 처음 MVC개념을 이해하기 위하여 스트럿츠 프레임워크를 접했을 때, 스트럿츠의 복잡한 구조와 세팅 등으로 인해 MVC에 대한 개념을 이해하기 전에 지쳐버렸던 기억이 있기 때문이다.

1. 사용자 관리 프로젝트 요구분석

프로젝트라는 용어를 사용해서 사용자 관리 프로젝트가 상당히 거창하게 들릴지도 모른다. 그렇지만 독자들이 생각하는 것만큼 거창한 프로젝트는 아니다. 오히려 너무 간단해서 "이게 무슨 프로젝트야?"라고 실망감이 클지도 모르겠다.

이 같이 작은 프로젝트를 선택하게 된 이유는 이 장에서 독자들이 이해해야하는 부분이 새로운 프로젝트를 이해하는 것이 주가 아니기 때문이다. 이 장에서 독자들이 꼭 이해해야 하는 부분은 모델 1 개발 방식과 모델 2개발방식, 스트럿츠 프레임워크를 이용한 개발 방법의 차이점을 이해하고 점진적으로 변화해가는 과정을 이해하는 것이 더욱 중요하다. 따라서 가능한 작은 규모로 프로젝트를 진행하면서 각각의 개발방식을 이해하는 데 중점을 둘 수 있도록 했다.

실전 프로젝트에서는 회원관리 같은 경우 무수히 많은 정보들을 요구한다. 하지만 이 장에서 살펴볼 사용자 관리 프로젝트는 사용자 아이디, 비밀번호, 사용자 이름, 사용자 이메일 주소만을 관리하도록 구현하겠다.

사용자 관리 프로젝트를 위하여 필요한 기능들을 사용자 관리 화면과 더불어 살펴보도록 하겠다.

로그인 기능을 제공해야 한다. 사용자가 입력한 아이디와 비밀번호가 같을 경우 로그인이 가능하며, 세션에 사용자 정보를 저장한다. 회원가입하지 않은 상태일 경우 회원가입 후에 로그인이 가능하다. 로그인이 된 상태에서 로그아웃을 할 수 있는 기능을 제공해야한다.

회원가입을 하지 않은 상태이면 회원가입을 통해 사용자 관리 프로젝트에 로그인이 가능하도록 해야한다.

사용자가 로그인 하면 사용자 리스트를 볼 수 있는 화면으로 이동한다. 사용자 리스트 화면은 현재 사용자 관리 프로젝트에 접근할 수 있는 모든 사용자들을 볼 수 있어야한다.

사용자 관리 프로젝트는 사용자별 정보를 볼 수 있는 기능을 제공해야한다. 사용자 정보화면에서 수정, 삭제화면으로 이동하여 수정, 삭제가 가능하도록 해야한다.

지금까지 살펴본 기능들을 제공한다면 사용자 관리를 위해 필요한 모든 기능들을 제공하는 것이다. 이상의 기능들을 바탕으로 유즈케이스 다이어그램을 그려보면 다음과 같다.[그림1-7 참조]

지금까지 살펴본 사용자 관리 프로젝트의 기능은 실제 사용자 관리 시스템을 구축하기 위한 최소한의 기능이다. 실제로 현업에서 개발되는 사용자 관리 시스템은 더 많은 사용자 정보를 요구하며, 사용자변 권한 관리까지 추가되기 때문에 이 절에서 분석한 기능보다 훨씬 더 복잡하고 많은 기능들을 요구하게 된다.

계속되는 절에서는 지금까지 살펴본 사용자 관리 기능을 구현해 보도록 하겠다. 먼저 모델1, 모델2, 스트럿츠에서 공통적으로 사용할 모델(비즈니스 로직과 데이터베이스 접근 로직을 말한다.)을 먼저 개발한다. 모델 1방식으로 개발할 경우 모델을 따로 만들지 않고 모델파트에서 할 모든 작업을 JSP에서 처리하는 경우도 있다. 이 장의 예제는 모델 1방식에 필요한 모델을 따로 분리하여 개발하는 것으로 한다.

그 다음 모델1, 모델2, 스트럿츠 개발방식으로 페이지의 흐름을 처리하는 방법과 모델 데이터를 사용자에게 보여주는 과정을 살펴보도록 하겠다.

강좌에 대하여

작성자 : 박재성
작성일 : 2005년 2월 20일

문서이력 :

  • 2005년 2월 20일 박재성 문서 최초 생성

참고 자료

Posted by 1010
반응형

Jakarta Struts 강좌 7 - 스트러츠와 데이터베이스 연결

Summary : 지금까지 6번의 강좌를 통하여 스트러츠의 기본적인 내용에 대하여 살펴보았다. 최근 대부분 웹 애플리케이션은 데이터베이스와의 연동을 통하여 구현이 되고 있다. 따라서 스트러츠 프레임?을 이용하여 관계형 데이터베이스와 연결하여 사용하는 방법에 대하여 살펴보는 것 또한 중요하다. 따라서 이번 강좌에서는 스트러츠 프레임?에서 관계형 데이터베이스와 연동하여 구현하는 방법에 대하여 살펴본다. 이번 강좌에서 사용할 예제는 지난 강좌까지 만들어 왔던 사용자 관리를 mySQL 데이터베이스를 이용할 수 있도록 변경해 보도록 하겠다.

사용자 관리 클래스 다이어 그램

사용자 관리의 클래스는 앞으로 계속해서 진행될 강좌(EJB, JDO)를 위하여 다양한 J2EE패턴을 적용하여 설계하였다. 사용자 관리의 클래스 다이어그램은 다음과 같다.

위 사용자 관리 클래스 다이어그램에서 사용한 패턴은 다음과 같다.

첫째, 다양한 데이터베이스의 지원이 가능하도록 하기 위하여 Factory Method 패턴과 Abstract Factory 패턴을 적용하였다. Abstract Factory 패턴을 제대로 적용하려고 했으나 부족한 부분이 있다.

둘째, 비지니스 로직과 데이터 베이스와의 조작을 분리하기 위하여 데이터 베이스와의 작업을 전담하는 Data Access Object클래스를 따로 두었다. J2EE패턴의 DAO패턴을 적용하였다.

셋째, J2EE패턴의 Transfer Object, Value Object를 만들어 적용해 보았다. 굳이 J2SE만을 이용하는 프로젝트에서는 필요없는 부분이지만 앞으로의 예제를 위하여 미리 구현해봤다.

강좌를 단순히 하기 위하여 패턴의 적용없이 구현하려고 했으나 앞으로 계속되는 강좌를 위해 패턴을 적용하여 확장성을 기해보았다.

강좌를 위한 초기 설정

이번 강좌에서는 데이터베이스와 연관되는 부분이 있기 때문에 초기에 설정해야 하는 작업이 있다.

첫번째로 데이터베이스는 mySQL을 사용하고 있다. http://www.mysql.com에서 3.23.X버전 이상을 다운받아 설치하기 바란다. mySQL에 대한 자세한 설명은 웹상의 문서들을 참조하기 바란다.

두번째는 http://homepages.nildram.co.uk/~slink/java/DBPool/에서 제공하는 Connection Pooling을 사용하고 있다. zigimall/WEB-INF/classes아래에 보면 dbpool.properties파일을 자신의 데이터베이스 환경에 맞도록 수정한다. dbpool.properties의 내용은 다음과 같다.

#JDBC Driver 등록
drivers=org.gjt.mm.mysql.Driver

#초기 Connection Pool 사이즈
mysql.initsize=10

#최대 Connection Pool 사이즈
mysql.maxpool=20

#Mysql의 비밀번호
mysql.password=javajigi

#MySQL URL
mysql.url=jdbc:mysql://127.0.0.1:3306/zigimall?useUnicode=true&characterEncoding=KSC5601

#MySQL의 User ID
mysql.user=javajigi

fdsdf
세번째는 사용자 관리에 사용할 테이블의 생성이다. 사용자 관리 테이블은 단순히 사용자 아이디, 비밀번호, 이름, 이메일 정보만을 가지도록 생성하였다.

CREATE TABLE userInfo ( 
	userId          varchar(12)		NOT NULL, 
	password		varchar(12)		NOT NULL,
	name			varchar(10)		NOT NULL,
	email			varchar(50),
  	
  	PRIMARY KEY               (userId),  	
  	INDEX userInfo_userId_idx  (userId)
);

이상으로 이번강좌에서 사용자 관리를 만들기 위하여 필요한 데이터베이스 설정이 완료되었다. 다음은 데이터베이스와의 작업을 전담하는 클래스 내부에 대하여 살펴보도록 하겠다.

데이터베이스 전담 클래스들

데이터베이스 전담 클래스는 섹션 1에서 살펴본 클래스 다이어그램에 해당하는 클래스들이다. 이번 섹션에서는 데이터베이스와의 작업을 전담하는 클래스 각각의 내부에 대하여 살펴보도록 하겠다.

package net.javajigi.mall.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import snaq.db.ConnectionPoolManager;

/**
 * 데이터베이스의 Connection객체를 얻는 작업과 
 * Connection, Statement, ResultSet를 닫는 역할을 하는 클래스.
 */
public class RDBDatabase {
	protected Connection getConnection() throws SQLException {
		ConnectionPoolManager conMgr = ConnectionPoolManager.getInstance();

		return conMgr.getConnection("mysql");
	}

	protected void cleanUp(Connection con, Statement stmt)
		throws SQLException {
		cleanUp(con, stmt, null);
	}


	protected void cleanUp(Connection con, Statement stmt, ResultSet rs)
		throws SQLException {
		if ( rs != null ) {
			rs.close();
		}
		
		if ( stmt != null ) {
			stmt.close();
		}
		
		if ( con != null ) {
			con.close();
		}
	}
}
package net.javajigi.mall.user;

import java.sql.SQLException;
import java.util.List;

/**
 * 사용자 관리에 필요한 일들을 정의하는 인터페이스
 */
public interface UserDAO {
	public UserVO getUser(String userId) throws SQLException;

	public int addUser(UserTO userTO) throws SQLException;
	
	public List getUsers() throws SQLException;
	
	public int updateUser(UserTO userTO) throws SQLException;
	
	public int removeUser(String userId) throws SQLException;
}
package net.javajigi.mall.user.rdb;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import net.javajigi.mall.db.RDBDatabase;
import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserTO;
import net.javajigi.mall.user.UserVO;

/**
 * UserDAO를 구현하는 클래스.
 * MySQL데이터베이스와의 작업을 전담하는 클래스.
 */
public class UserMysqlDAO extends RDBDatabase implements UserDAO {

	/**
	 * @see net.javajigi.mall.user.UserDAO#getUser(String)
	 */
	public UserVO getUser(String userId) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			String findUserQuery =
				"SELECT "
					+ "userId, password, "
					+ "name, email "
					+ "FROM userinfo "
					+ "WHERE userId = ?";

			pstmt = getConnection().prepareStatement(findUserQuery);
			pstmt.setString(1, userId);

			rs = pstmt.executeQuery();

			if (rs.next()) {
				return new UserVO(
					(String) rs.getString("userId"),
					(String) rs.getString("password"),
					(String) rs.getString("name"),
					(String) rs.getString("email"));
			} else {
				return null;
			}
		} finally {
			cleanUp(con, pstmt, rs);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#addUser(UserTO)
	 */
	public int addUser(UserTO userTO) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;

		try {
			String insertQuery = "INSERT INTO userinfo VALUES (?, ?, ?, ?)";

			pstmt = getConnection().prepareStatement(insertQuery);
			pstmt.setString(1, userTO.getId());
			pstmt.setString(2, userTO.getPassword());
			pstmt.setString(3, userTO.getName());
			pstmt.setString(4, userTO.getEmail());

			int result = pstmt.executeUpdate();

			return result;
		} finally {
			cleanUp(con, pstmt);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#getUsers()
	 */
	public List getUsers() throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			String findUsersQuery =
				"SELECT "
					+ "userId, password, "
					+ "name, email "
					+ "FROM userInfo ";

			pstmt = getConnection().prepareStatement(findUsersQuery);

			rs = pstmt.executeQuery();

			List users = new ArrayList();

			while (rs.next()) {
				UserVO userVO =
					new UserVO(
						(String) rs.getString("userId"),
						(String) rs.getString("password"),
						(String) rs.getString("name"),
						(String) rs.getString("email"));

				users.add(userVO);
			}

			return users;
		} finally {
			cleanUp(con, pstmt, rs);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#updateUser(UserTO)
	 */
	public int updateUser(UserTO userTO) throws SQLException {
		Connection con = getConnection();
		PreparedStatement pstmt = null;

		try {
			String updateQuery =
				"UPDATE userinfo SET "
					+ "password = ?, "
					+ "name = ?, "
					+ "email = ? "
					+ "WHERE userId = ?";

			pstmt = con.prepareStatement(updateQuery);
			pstmt.setString(1, userTO.getPassword());
			pstmt.setString(2, userTO.getName());
			pstmt.setString(3, userTO.getEmail());
			pstmt.setString(4, userTO.getId());

			int result = pstmt.executeUpdate();

			return result;
		} finally {
			cleanUp(con, pstmt);
		}
	}

	/**
	 * @see net.javajigi.mall.user.UserDAO#removeUser(String)
	 */
	public int removeUser(String userId) throws SQLException {
		Connection con = null;
		PreparedStatement pstmt = null;

		try {
			String removeQuery = "DELETE FROM userinfo WHERE userId = ?";

			pstmt = getConnection().prepareStatement(removeQuery);
			pstmt.setString(1, userId);

			int result = pstmt.executeUpdate();

			return result;
		} finally {
			cleanUp(con, pstmt);
		}
	}
}
package net.javajigi.mall.user;

import net.javajigi.mall.user.rdb.UserMysqlDAO;

public class UserDAOFactory {
	private static UserDAOFactory _instance = null;
	
	public static int DATABASE_MYSQL = 1;

	private UserDAOFactory() {
	}

	public static synchronized UserDAOFactory instance() {
		if (_instance == null) {
			_instance = new UserDAOFactory();
		}

		return _instance;
	}
	
	public UserDAO getUserDAO(int dbType) {
		if ( dbType == DATABASE_MYSQL ) {
			return (new UserMysqlDAO());
		} else {
			return null;
		}
	}
}
package net.javajigi.mall.user;

public class UserTO {
	private String id = null;
	private String password = null;
	private String name = null;
	private String email = null;
	
	public String getEmail() {
		return email;
	}

	public String getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public String getPassword() {
		return password;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public void setId(String id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}
package net.javajigi.mall.user;

public class UserVO {
	private String id = null;
	private String password = null;
	private String name = null;
	private String email = null;
	
	public UserVO(String id, String password, String name, String email){
		this.id = id;
		this.password = password;
		this.name = name;
		this.email = email;
	}
	
	public String getId() {
		return id;
	}

	public String getPassword() {
		return password;
	}

	public String getEmail() {
		return email;
	}

	public String getName() {
		return name;
	}

	public boolean equals(Object userVO) {
		return this.equals(userVO);
	}

	public String toString() {		
		return getId();
	}
}

스트러츠의 Action클래스, JSP파일

이번 섹션에서는 사용자 관리를 위한 Action클래스에 대하여 살펴본다. 모든 구조는 앞에서 살펴본 Action클래스와 같다. 단지 달라진 부분은 데이터베이스와의 작업이 가능하도록 수정해 주면 된다. 다음에 살펴볼 클래스들은 앞에서 살펴본 데이터베이스의 접근을 전담하는 클래스들을 이용하는 Action클래스들이다.

또한 각각의 Action클래스 작업이 완료한 다음 뷰를 전담하는 JSP파일에 대하여 살펴보도록 하겠다.

package net.javajigi.mall.user.action;

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserDAOFactory;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class UserListAction extends Action {

	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {

		
		UserDAOFactory udf = UserDAOFactory.instance();
		UserDAO userDAO = udf.getUserDAO(UserDAOFactory.DATABASE_MYSQL);
		List users = userDAO.getUsers();
		
		//사용자의 정보를 얻어와 사용자의 정보를 담은 객체를 
		//request에 저장하여 전달하고 있다.
		//이것이 가능한 이유는 RequestDispatcher를 이용하기 때문이다.
		request.setAttribute("listuser", users);	
		
		return mapping.findForward("userlist");
	}
}
<%@ page contentType="text/html;charset=EUC-KR"%>

<%@ page import="java.util.*"%>
<%@ page import="net.javajigi.mall.user.*"%>

<%@ 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>사용자 리스트</title>
    <html:base/>
<script language="JavaScript">
function update(userId) {
	f.userId.value = userId;
	f.action = "./userUpdateForm.do";
	f.submit();
}

function del(userId) {
	f.userId.value = userId;
	f.action = "./deleteuser.do";
	f.submit();
}
</script>    
</head>

<body>
<form name="f" method="post">
	<html:errors />
	<br/>
	<table border="1">
		<tr>
			<td><bean:message key="prompt.id" /></td>
			<td><bean:message key="prompt.name" /></td>
			<td><bean:message key="prompt.email" /></td>	
			<td></td>	
		</tr>
<%
	List users = (List)request.getAttribute("listuser");

	for (int i=0; i < users.size(); i++) {
		UserVO userVO = (UserVO)users.get(i);
%>
		<tr>
			<td><%= userVO.getId() %></td>
			<td><%= userVO.getName() %></td>
			<td><%= userVO.getEmail() %></td>
			<td>
				<a href="javascript:update('<%= userVO.getId() %>')">수정</a> 
				<a href="javascript:del('<%= userVO.getId() %>')">삭제</a>				
			</td>	
		</tr>
<%
	}
%>		
	</table>
	<input type="hidden" name="userId"/>

    <html:link page="/useraddform.do">
        <bean:message key="user.useradd" />
    </html:link>
    <html:link page="/login.jsp">
        <bean:message key="useradd.cancel" />
    </html:link>    
<form>
</body>
</html:html>
package net.javajigi.mall.user.action;

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

import net.javajigi.mall.user.form.UserForm;
import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserDAOFactory;
import net.javajigi.mall.user.UserTO;

import org.apache.commons.beanutils.PropertyUtils;
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 UserAddAction extends Action {
	public ActionForward execute(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest request,
		HttpServletResponse response)
		throws Exception {
		UserForm useraddForm = (UserForm) form;

		ActionErrors errors = new ActionErrors();
		
		//요청된 Request의 Token이 유효한지를 처리하는 부분이다.
		//Token이 유효하지 않을 경우 ActionErrors에 에러를 저장한다.
		//따라서 같은 요청이 재요청될 경우 에러를 발생시키기 때문에 같은 요청이 
		//두번 반복해서 처리되지는 않는다.
        if (!isTokenValid(request)) {
            errors.add(ActionErrors.GLOBAL_ERROR,
                       new ActionError("error.transaction.token"));
        }
       	resetToken(request);		
		        
		// Report any errors we have discovered back to the original form
		if (!errors.isEmpty()) {
		    saveErrors(request, errors);
 	        saveToken(request);

	        return (mapping.getInputForward());
		}

		UserTO userTO = new UserTO();
		
/*		
		userTO.setId(useraddForm.getId());
		userTO.setPassword(useraddForm.getPassword());
		userTO.setName(useraddForm.getName());
		userTO.setEmail(useraddForm.getEmail());
*/
		PropertyUtils.copyProperties(userTO, useraddForm);
		
		UserDAOFactory udf = UserDAOFactory.instance();
		UserDAO userDAO = udf.getUserDAO(UserDAOFactory.DATABASE_MYSQL);
				
		userDAO.addUser(userTO);
		
		useraddForm.reset(mapping, request);
		
		return(mapping.findForward("loginForm"));
	}
}
package net.javajigi.mall.user.action;

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

import net.javajigi.mall.Constants;
import net.javajigi.mall.user.form.UserForm;
import net.javajigi.mall.user.UserDAO;
import net.javajigi.mall.user.UserDAOFactory;
import net.javajigi.mall.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 {
		
		UserForm loginForm = (UserForm)form;
		
		String inputId = loginForm.getId().trim();
		String inputPassword = loginForm.getPassword();

		UserDAOFactory udf = UserDAOFactory.instance();
		UserDAO userDAO = udf.getUserDAO(UserDAOFactory.DATABASE_MYSQL);
		UserVO userVO = userDAO.getUser(inputId);
		
		ActionErrors errors = new ActionErrors();

		if ( userVO == null ) {
			//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"));
			}			
		}
		
		//로그인 과정에서 에러가 있을 경우 Action클래스의 saveErrors에 에러를 저장하고
		//로그인 폼페이지로 반환하게된다.
		//mapping의 getInputForward()에서 반환하게 되는 정보는 struts-config.xml의 action태그의
		//input attribute에 정의된 페이지로 이동하게 된다.
		if (!errors.isEmpty()) {
	    	saveErrors(request, errors);
           	return (mapping.getInputForward());
		}
		
		//로그인 과정이 정상적으로이루어 지면 userVO객체를 세션에 저장.
		HttpSession session = request.getSession();
		session.setAttribute(Constants.USER_KEY, userVO);
		
		//로그인 과정이 정상적으로이루어진 다음 메인페이지로 이동한다.
		//mapping의 findForward에서 사용하는 key값은 struts-config.xml의 forward태그의 
		//name attribute를 이용한다.
		return(mapping.findForward("mainpage"));
	}
}

지금까지 스터르츠의 Action클래스와 데이터베이스의 데이터를 가져와 JSP에서 사용하는 방법에 대하여 살펴보았다. 사용자 관리를 각각의 작업들이 모두 위와 같은 과정으로 구현되어 있기 때문에 모든 소스를 보여주지는 않았다. 이 섹션에서 살펴본 예제 이외의 소스는 첨부되는 소스를 참고하여 구현 과정을 살펴보기 바란다.

강좌에 대하여

작성자 : [Javajigi](박재성)
작성일 : 2005년 2월 20일

문서이력 :

  • 2005년 2월 20일 박재성 문서 최초 생성

참고 자료

출처 : http://wiki.javajigi.net/pages/viewpage.action?pageId=79

Posted by 1010
반응형

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 ) {
			//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"));
			}			
		}
		
		//로그인 과정에서 에러가 있을 경우 Action클래스의 saveErrors에 에러를 저장하고
		//로그인 폼페이지로 반환하게된다.
		//mapping의 getInputForward()에서 반환하게 되는 정보는 struts-config.xml의 action태그의
		//input attribute에 정의된 페이지로 이동하게 된다.
		if (!errors.isEmpty()) {
	    	saveErrors(request, errors);
           	return (mapping.getInputForward());
		}
		
		//로그인 과정이 정상적으로이루어 지면 userVO객체를 세션에 저장.
		HttpSession session = request.getSession();
		session.setAttribute(Constants.USER_KEY, userVO);
		
		//로그인 과정이 정상적으로이루어진 다음 메인페이지로 이동한다.
		//mapping의 findForward에서 사용하는 key값은 struts-config.xml의 forward태그의 
		//name attribute를 이용한다.
		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 "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" 
                               "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<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 {

		// Is there a valid user logged on?
		boolean valid = false;
		HttpSession session = pageContext.getSession();
		if ((session != null) && (session.getAttribute(name) != null))
			valid = true;

		// Forward control based on the results
		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등 스트러츠 프레임?의 근간이 되는 클래스들에 대하여 살펴보도록 하겠다.

참고 자료

Posted by 1010
반응형

action-mappings

<action-mappings>설정

  • 컨트롤러가 요청을 받았을 때 어떤 Action 인스턴스를 실행할 것인가에 대한 설정 정보
  • <action-mappings>하위에 <action>을 이용해서 여러 개의 action 설정 가능
  • <action> : 특정 request URI와 대응하는 Action 매핑 정의

<action>의 주요 attribute

  • path : 확장자를 제외한 "/"로 시작하는 경로명
  • type : action클래스의 이름
  • scope : form bean이 저장되어 있는 context의 scope
  • name : action과 연결된 form bean의 name
  • role : Action 객체에 접근할 수 있는 권한을 설정
  • input : form bean에서 validation error가 발생한 경우 되돌아 가거나 상황을 표시할 수 있는 경로

다음은 <action>의 attribute들이다.
Name Description
attribute Form bean에 접근하기 위한, request-scope 또는 session-scope attribute의 name 값이다. 사용할 form bean을 다른 attribute의 이름으로 사용하고자 할 때 사용한다. Form bean이 name attribute에 기술되어 있을 때에만 기술될 수 있다.
className Action들의 configuration 정보를 담고 있을 객체이다. 반드시 org.apache.struts.config.ActionMapping 또는 이를 상속 받은 클래스여야 한다.
디폴트 값 : org.apache.struts.config.ActionMapping
forward 요청된 request를 Action 클래스 대신하여 수행할 resource(*.do, *.jsp 등)의 상대(module-relative) 경로를 나타낸다. [required: 반드시 forward, include, type attribute 중의 하나만 기술되어야 한다.]
include 요청된 request를 Action 클래스 대신하여 수행할 resource(*.do, *.jsp 등)의 상대(module-relative) 경로를 나타낸다. [required: 반드시 forward, include, type attribute 중의 하나만 기술되어야 한다.]
input Form bean에서 validation error가 발생했을 때, 이를 나타낼 resource(*.do, *.jsp 등)의 상대(module-relative) 경로를 가리킨다. Form bean이 name attribute에 기술되어 있을 때에만 기술될 수 있다. [required: form bean이 name attribute에 기술되어 있고 validation error들을 리턴할 경우]
name 이 action 매핑 사용하는 form bean의 이름을 나타낸다.
path Submit된 request의 상대(module-relative)경로를 나타낸다. 이 attribute는 반드시 "/"으로 시작해야 하고, filename의 확장자 없이 기술되어야 한다. 예를 들어, "/main.do"은 적절한 path attribute의 기술 방법이 아니다. 왜냐하면 이미 do라는 확장자가 action 매핑에 사용되고 있는 것을 알고 있기 때문에, "/main"이라고만 기술하는 것이 옳다. [required]
parameter Action 객체에 특별한 어떤 값을 넘겨주기 위한 설정 parameter이다. 현 Action 클래스에서는 이 attribute를 이용하지 않고 있기 때문에, 값을 넣는다 해도 처리되지 않는다. 만약 이 attribute를 사용하고자 하면, Action 클래스의 서브클래스를 만들어 구현해야 한다.
prefix Request parameter name을 form bean property name에 매치시키는 데 사용되는 prefix를 나타낸다. Form bean이 name attribute에 기술되어 있을 때에만 설정할 수 있다.
roles Action 객체에 접근할 수 있는 권한을 설정한다. 여러 role 이름들은 콤마(,)로 구분하여 쓸 수 있다. 예를 들어, "admin, master, user"라고 써주면 admin, master, user의 세 가지 권한 중 어느 한가지 권한이라도 가진 사용자는 이 action을 사용할 수 있게 된다.
scope 이 action이 사용하는 form bean이 저장되어 있는 context의 scope를 나타낸다. request 또는 session.
디폴트 값 : session
suffix Request parameter name을 form bean property name에 매치시키는 데 사용되는 suffix를 나타낸다. Form bean이 name attribute에 기술되어 있을 때에만 설정할 수 있다.
type 요청된 request를 수행할 Action 클래스를 나타낸다. 이 클래스는 org.apache.struts.action.Action의 서브클래스여야 한다. [required: 반드시 forward, include, type attribute 중의 하나만 기술되어야 한다.]
unknown 설정 파일에 정의되지 않은 request를 처리하는 default action 매핑인지 여부를 나타낸다. 요청된 request를 수행할 action 매핑 객체가 없을 경우에, unknown이 true로 설정된 action 매핑 객체에게 이 request를 넘겨 처리하게 한다. 각각의 module마다 unknown이 true인 action 매핑은 하나만 있을 수 있다.
디폴트 값 : false
validate Form bean에서 validation을 수행할지 여부를 나타낸다. 이 값이 true이면, form bean의 validate 메소드가 실행된다.
디폴트 값 : true
cancellable Struts 1.3에 추가된 attribute로 Struts 1.2.9에서 <set-property>로 설정했던 것이 1.3부터 바뀌었다. Cancel Process를 사용하기 위해서 설정해야 한다.

Samples

다음은 struts-config-login.xml 파일에서 action-mappings 설정에 대한 예제이다.
<action-mappings>
<action
	path="/login"
	type="anyframe.sample.struts.web.action.LoginAction"
	name="userForm"
	scope="request"
	input="/basic/login.jsp">
	<exception key="error.password.mismatch" path="/basic/login.jsp" 
	type="javax.security.auth.login.FailedLoginException" />
	<forward name="success" path="/basic/main.jsp" />
</action>
	...
</action-mappings>
'/login.do' 의 request에 대해 LoginAction 이 처리하도록 매핑되어 있으며, 이 때 action에 연결된 form bean은 UserForm 이다. request scope 동안 form bean 이 유지되며 forward 경로는 Action클래스에서 "success"라는 이름으로 forward name을 세팅 했기 때문에 /basic/main.jsp로 forwarding한다. exception 발생 시 /basic/login.jsp로 돌려진다.

<action>의 작성은 개발자가 반드시 숙지해야할 부분으로, request의 처리를 담당하는 Action을 매핑하고 페이지 네비게이션을 제어하는 등 웹 어플리케이션 개발의 중요한 작업이다.

global-forwards

<global-forwards> 설정

  • 실제 forward 또는 redirect 할 수 있는 URI를 논리적인 이름으로 맵핑
  • <global-forwards>하위에 <forward>를 이용해서 여러 개의 URI 매핑 설정
  • 하나의 <forward>는 하나의 논리적인 이름을 module-relative 또는 context-relative URI 경로로 매핑함 (URI 경로를 직접 사용하는 것 보다 logic 내부적으로 정해진 이름을 사용함으로써, view로부터 controller와 model을 분리)
  • 모든 action에서 사용할 수 있는 global level의 forward를 정의
forward 의 우선순위
<forward>은 전역(global) level과 action level 에서 정의될 수 있는데 action level 에서 선언된 것이 더 우선순위가 높다.
다음은 <forward>의 attribute들이다.
Name Description
className Forward들의 configuration 정보를 담고 있을 객체이다. org.apache.struts.config.ActionForward 또는 이를 상속 받은 클래스여야 한다.
디폴트 값 : org.apache.struts.config.ActionForward
name 현재 forward의 이름이고, 다른 forward들과 구분될 수 있는 identifier이다. [required]
path Forward 또는 redirect할 resource(*.do, *.jsp 등)의 상대(module-relative or context-relative)경로를 나타낸다. [required]
redirect RequestProcessor가 이 forward에 대해 redirect할 필요가 있을 때, true로 설정한다.
디폴트 값 : false

Samples

다음은 struts-config.xml 파일에서 global-forwards 설정에 대한 예제이다.
<global-forwards>
        <forward name="login" path="/login.jsp"/>
        <forward name="main" path="/main.do" redirect="true"/>
</global-forwards>
Posted by 1010
반응형
Struts를 이용한 간단한 로그인 예제

이번엔 웹 개발의 전형적인 예로써 사용자의 ID와 Password를 입력 받아 이를 인증 후 결과 페이지로 Forward 시키는 것을 Struts Framework을 이용하여 만들어 보겠습니다.

구성요소들은  다음과 같습니다.

1.        로그인  HTML 페이지
2.        Controller&Model 역할을 하는 LoginAction
3.        ActionForm, 폼빈 역할을 하는 LoginForm
4.        로그인이 성공 이었을 때 분기하는 success.html
5.        로그인이 실패했을 경우 분기하는 fail.html

사용자는 HTML 웹페이지를 이용하여 ID/Password를 입력 후 로그인 버튼을 누릅니다. 이 요청을 Controller 와 Model의 역할을 동시에 수행 하는(이 예제에서…)LoginAction이 이를 받아 폼빈(LoginForm, 사용자가 HTML 폼에서 입력한 정보들이 들어 있다.)을 통해 사용자가 입력 한 정보를 추출 후 인증을 수행 합니다.

폼빈(LoginForm)이 존재 하므로 LoginAction에서는 요청 파라미터를 추출 하는 일은 없습니다. 최근에 발표된 RC2 버전인 경우 매번 폼빈을 생성 할 필요 없이 동적으로 파라미터를 추출 할 수 있도록 했는데 이를 동적 폼빈 이라고 하며 DynaActionForm 클래스를 이용 합니다.

주)이 예제를 이클립스에서 실행 하기 위해서 이전 강좌(Eclipse에서 Struts(HelloWorld))와 같이 struts-test 라는 Tomcat 프로젝트에서 작성을 합니다. 그리고 프로젝트에서 마우스 우측 버튼을 누른 후 속성 - Java Build Path - library - add jars를 하신 후 WEB-INF/lib 아래의 struts를 다운 받아서 푼 jar 파일들을 선택 해 주신 후 아래의 예제를 돌리셔야 합니다.

1.        로그인을 위한 login.jsp를 만듭니다. (struts-test 라는 프로젝트에서 마우스 우측버튼을 누른 후 new , file 한 후 만드세요)

<%@ page language="java" contentType="text/html;charset=euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html:form action="/login">
        ID : < html:text property="id"/><br>
        Password : <html:password property="pwd"/><br>
        <html:submit value="로긴"/>
</html:form>

2.        로그인이 성공 했을 때 분기할 페이지인 success.html을 만듭니다. (struts-test 라는 프로젝트에서 마우스 우측버튼을 누른 후 new , file 한 후 만드세요)
3.        

<html>
        <head><title>로그인 성공 화면</title></head>
        <body>
                로그인이 성공 되었습니다...
        </body>        
</html>

4.        로그인이 실패 했을 경우 분기 할 페이지인 fail.html을 만듭니다. (struts-test 라는 프로젝트에서 마우스 우측버튼을 누른 후 new , file 한 후 만드세요)

<html>
        <head><title>로그인 실패 화면</title></head>
        <body>
                로그인 실패!!
        </body>        
</html>

5.        사용자가 요청 시 넘기는 값들을 빈으로 저장 해 놓기 위해 Request Parameter와 같은 구조를 가지는 폼빈을 만듭니다. LoginForm.java (이클립스인 경우 WEB-INF/src에서 마우스 우측 버튼을 클릭 후 new, class 하신 후 만드세요)

이 클래스는 Form빈으로서 사용자가 입력 한 값을 적절한 변수(프로퍼티)로 설정 해 주는 역할을 합니다. getXXX, setXXX 메소드등을 이용하여 적절한 변수 값으로 설정 되는 것입니다.


package login;
import org.apache.struts.action.*;

public class LoginForm extends ActionForm {
        protected String id;
        protected String pwd;
       
        public String getId() {
                return id;
        }
       
        public String getPwd() {
                return pwd;
        }
       
        public void setId(String id) {
                this.id = id;
        }
       
        public void setPwd(String pwd) {
                this.pwd = pwd;
        }       
}

6.        이번에는 MVC 모델의 컨트롤러 및 모델의 역할을 동시에 수행하는 LoginAction.java 파일을 만듭니다. 아래 파일에서 AuthUser라는 모듈을 자바빈으로 빼 낸다면 이것이 모델에 해당 하게 될겁니다. 본예제에서는 편의상 컨트롤러와 모델의 기능을 통합 시켰습니다.

이 클래스는 사용자가 입력 한 파라미터(id, pwd)를 LoginForm(폼빈)으로부터 받아 이
값을 이용하여 인증을 한 후 적절한 View를 선택 하여 포워딩을 하는 것입니다.

예제의 간결함을 위해 본 예제에서는 AuthUser 메소드는 무조건 인증이 성공됨을 알
리는 true를 리턴 합니다. 이 부분을 Oracle과 같은 DB와 JDBC등을 이용한 적절한
인증을 구현 하시는 것은 여러분들의 몫으로 넘기겠습니다.


package login;

import org.apache.struts.action.*;
import javax.servlet.http.*;
import java.io.*;

public class LoginAction extends Action {
        public ActionForward execute(ActionMapping mapping, ActionForm form,
                                              HttpServletRequest req, HttpServletResponse res) {
                String id = ((LoginForm)form).getId();
                String pwd = ((LoginForm)form).getPwd();
               
                boolean isOK = authUser(id, pwd);
               
                if (isOK) {
                        return (mapping.findForward("success"));
                }
                else {
                        return (mapping.findForward("fail"));
                }               
        }
       
        public boolean authUser(String id, String pwd) {
                //이곳에서 디비에 접속해서 ID와 Password가 맞는지 확인 한다.
                //본 예제에서는 무조건 인증이 된것으로 true를 리턴 합니다
                return true;
        }
}


7.        이번에는 web.xml을 만듭니다. (WEB-INF에서 마우스 우측 버튼을 누른 후 new, file 하신 후 만드세요)

web.xml 파일에서 추가 한 내용은 URL창에 확장자가 .do인 요청이 들어 오면 ora.apache.struts.action.ActionServlet으로 요청을 보내도록 설정을 했습니다. 그런다음 스트럿츠의 설정 파일인 struts-config.xml의 위치를 알려주고 있습니다.

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>Welcome to Tomcat</display-name>
  <description>
     Welcome to Tomcat
  </description>
        <!-- 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>    
       
        <!-- ActionServlet Congif =====================================-->
        <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/struts-config.xml</param-value>
                </init-param>
                <load-on-startup>1</load-on-startup>                
        </servlet>
       
       
        <!-- ActionServlet Mapping ====================================-->
        <servlet-mapping>
                <servlet-name>action</servlet-name>
                <url-pattern>*.do</url-pattern>
        </servlet-mapping>


</web-app>


8.        이번에는 struts 설정을 위한 struts-config.xml을 만듭니다. . (WEB-INF에서 마우스 우측 버튼을 누른 후 new, file 하신 후 만드세요)

이 파일에서는 프로그램에서 사용 할 폼빈을 LoginForm으로 설정 하고 프로그램에서 사용 할 포워딩 이름을 설정 하는데(물론 이 부분은 action 설정에서 할 수도 있지만 전역적으로 설정 하기 위해 global-forwards 로 설정을 했습니다.

그리고 실제 요청을 처리 할 action 부분을 설정 하는데 예제 에서는 URL에 “/login”이 포함되어 있는 경우에 처리하라고 지정 했는데 LoginAction이 그 일을 하는거죠… 또한 폼빈으로 LoginForm을 설정 했습니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
     
    <!-- ========== Form Bean Definitions ================================== -->
    <form-beans>
        <form-bean name="LoginForm" type="login.LoginForm">          
        </form-bean>        
    </form-beans>
   
  
    <!-- ========== Global Forward Definitions =============================== -->
    <global-forwards>
        <forward name="success" path="/success.html" />
        <forward name="fail" path="/fail.html" />
    </global-forwards>
   
    <!-- ========== Action Mapping Definitions =============================== -->
    <action-mappings>
        <action          
                path="/login"
                type="login.LoginAction"
            name="LoginForm"                        
            validate="false"
        >
        </action>        
    </action-mappings>  
</struts-config>

위에서 주석 만드실 때 주의하세요… <!—다음에 줄 두개짜립니다. “====” 만약 다음과 같이 하시면 Tomcat 시작시 오류 뜹니다.<!--------------  

그리고 이클립스에서 만드시면 별도로 LoginForm.java나 LoginAction.java를 별도로 컴파일 하실 필요는 없습니다. 브라우저에서 다음과 같이 입력 하신면 로그인 화면이 나타나며 적절한 ID/PWD를 입력하시면 인증이 성공 했다는 success.html이 실행 될겁니다.

실행 후 화면을 보시면 주소창에 /login.do 라고 나타나 있을 것입니다. 이 같이 함으로써 사용자의 입장에서는 마지막 결과가 어느 페이지에서 나오는지를 알 수가 없습니다.(멋지죠^^) 당연히 직접 그 페이지로 접근 한다는 것은 불가능 합니다.

그러므로 success.jsp와 같은 결과 페이지를 만드는 사람과 Action등을 만드는 사람의 업무가 분리가 가능 하다는 이야기 입니다. 결국 이러한 장점이 MVC 모델의 장점이 되는거구요…

다음 강좌부터는 이 로그인 예제를 하나씩 기능을 추가해 가도록 하죠^^

먼저 HTML을 이용해서  Form양식을 만들어야만 한다.

Form 양식은 id와 pwd를 입력받는 일반 Web상의 login소스를 생각하면 된다.


<%@ page language="java" pageEncoding="EUC-KR"%>

<%@ taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html" %>

<html:html>

<html:form action="/login">

<table width="200" height="138" border="0" cellpadding="0" cellspacing="0">
  <tr>
    <td height="8" colspan="3"><img src="images/login_title.gif" width="200" height="40"></td>
  </tr>
  <tr>
    <td width="79" align="left" class="style36 style16"><img src="images/id.gif" width="70" height="20"></td>
    <td width="89" height="20">
   
     <html:text property="uid" value="" size="12" style="width:75px;height:17px; border: #4882B2 1px solid"/>
      
    </td>
    <td width="38" rowspan="2">
    <html:img src="images/icon/btn_left_login.gif" width="35" height="46"
     onclick="loginCheck()" onmouseover="this.style.cursor='hand'"/>

    </td>
  </tr>
  <tr>
    <td height="20" align="left" class="style37"><img src="images/pwd.gif" width="70" height="20"></td>
    <td>
      <p>
     
      <html:password property="password" value="" size="12" style="width:75px;height:17px; border: #4882B2 1px solid"/>
     
    </p></td>
  </tr>
  <tr align="left" valign="middle">
    <td height="16" colspan="3">&nbsp;&nbsp;<a href="#" class="style16 style25" onMouseOver="MM_swapImage('Image17','','images/icon_left02.gif',1)" onMouseOut="MM_swapImgRestore()"><strong><img src="images/icon/icon1.gif" width="11" height="11">&nbsp;
    <html:link action="registerAgree">
     회원가입
    </html:link>
    </strong></a></td>
  </tr>
  <tr align="left">
    <td height="8" colspan="3">&nbsp;&nbsp;<a href="#" class="style26" onMouseOver="MM_swapImage('Image18','','images/icon_left02.gif',1)" onMouseOut="MM_swapImgRestore()"><strong><img src="images/icon/icon1.gif" width="11" height="11">&nbsp;
    아이디/ 비밀번호 찾기
    </strong></a></td>
  </tr>
 
</table>

</html:form>


</html:html>



위와 같은 양식을 이용해서 Struts-config.xml파일에선 2개의 property를 생성후

parameter를 받을수 있도록 설정을 해준다.


<form-bean name="loginForm" type="com.chestnut.logins.LoginForm">
           <form-property name="uid"  type="java.lang.String" />
            <form-property name="password"  type="java.lang.String" />
</form-bean>


위의 설정이 끝났으면 Action을 설정해준다.

<action
         attribute="loginForm"
         path="/login"
         name="loginForm"
            input="/sources/user/guest/Pindex.jsp"
            type="com.chestnut.logins.LoginAction"
            validate="false"
            scope="request">
            <forward name="success" path="/home.do"/>
            <forward name="fail" path="/sources/user/guest/PregisterAgree.jsp"/>
        </action>


로그인이 완료 됬을경우 home으로 실패 할경우 회원 가입 페이지로 forward를 시켜주는 부분이다.


Form을 통한 Action처리이기 때문에 크게 3개의 클래스가 필요하다.

Form클래스 Action처리 클래스 회원 정보를 저장해서 가지고 있을 DTO명칭 대신 단순하게UserInfo라는 클래스.


Class들을 만들자.

먼저 Login Form처리 클래스이다.

/*
 * Created on 2005. 7. 23
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.chestnut.logins;

/**
 * @author 한진석
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class LoginForm extends ActionForm {
 
 private String password;
 private String uid;
 
  public void reset( ActionMapping mapping, HttpServletRequest request ) {
   this.password= "";
    this.uid= "";
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

public String getUid(){
  try{
   return new String( this.uid.getBytes("ISO8859_1"), "EUC_KR" );
  }catch( Exception e){
   return new String("");
  }
 }
 public String getPassword(){
  try{
   return new String( this.password.getBytes("ISO8859_1"), "EUC_KR" );
  }catch( Exception e){
   return new String("");
  }
 }
}

getter와 setter 함수 작성시 잘 모르는 사람들은 고생을 많이 한다.

Getter와 setter작성시 항상 주의를 해야만 한다. Form에서 넘겨주는 name값이 반드시 getter와

setter에 들어가야만 한다.

위에서 보면 uid와 password로 넘겨 주고 ActionForm을 상속받은 클래스에선 getPassword(...

getUid라는 이름으로 작성을 하였다.

틀릴경우 Log파일을 보면 Error내용이 나온다. getter 또는 setter를 찾을수 없다는 내용의 ..


마지막으로 Action클래스를 작성.

/*
 * Created on 2005. 7. 23
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.chestnut.logins;

import java.sql.*;

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

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.chestnut.logins.LoginForm;

/**
 * @author 한진석
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class LoginAction extends Action {
 
   public ActionForward execute(
     ActionMapping mapping,
     ActionForm form,
     HttpServletRequest request,
     HttpServletResponse response)
     throws Exception {

    String forwards = "";

    String uid         = "";
    String pwd       = "";
    
    LoginForm loginform = (LoginForm)form;
    
    uid  = loginform.getUid().trim();
    pwd  = loginform.getPassword().trim();
  
    javax.sql.DataSource src = getDataSource( request, "ora");
    Connection dbcon = null;
    PreparedStatement pstmt = null;
    
    ResultSet rs = null;
    ResultSet rs1 = null;
    
    UserInfo ui = null;
    
    try{
     dbcon = src.getConnection();
     String query ="SELECT * FROM USERS WHERE " +
     " USERID='"  + uid +"'"+
   " AND PWD='" + pwd +"'";
     pstmt = dbcon.prepareStatement(query);
     rs = pstmt.executeQuery();    
     String userId ="";
    
       if( rs.next() ){
        userId = rs.getString("USERID");
        HttpSession session = request.getSession();
       
        ui =
         new UserInfo(
           userId,
       rs.getString("NAME"),
       0,
       rs.getInt("USERTYPE"),
       rs.getString("UPYUN"),
       rs.getString("ADDRESS"),
       rs.getString("PHONE"),
       rs.getString("CPHONE")
     );
                       
        String query2 ="UPDATE USERS SET LASTLOGIN=SYSDATE" +
          " WHERE USERID='"+userId+"'";

        pstmt.executeUpdate( query2 );
               
        String query3 = "select * from mileage " +
          " where userid='"+userId+"'";
              
        pstmt = dbcon.prepareStatement(query3);
        rs1 = pstmt.executeQuery();
       
        if(rs1.next()){
         ui.setMileage(rs1.getInt("MILEAGE"));
        }
        rs1.close();
       
        dbcon.commit();
       
        session.setAttribute("user", ui );
        forwards = "success";
       
       }else{
        request.setAttribute("notuser",new String("1"));
        forwards= "fail";
       }
       
  }catch( Exception e){

   System.out.println("(SQL Error( Login ERROR ): "+ e );
   
  }finally{

   if(pstmt != null){pstmt.close();}
   if(dbcon != null){ dbcon.close();}
   if(rs != null){rs.close();}


    }
 
    return mapping.findForward( forwards );
   }

 }

Posted by 1010
반응형

Struts
웹 애플리케이션을 구현할 때 사용하는 오픈 소스 프레임웍인 Struts는 대중적인 디자인 패러다임에 근거하고 있다. 이 프레임웍은 Java Servlets, JavaBeans, ResourceBundles, XML 같은 표준 기술을 바탕으로 구현되어 유연하고 확장성 있는 컴포넌트를 제공한다. Struts는 Controller 레이어를 ActionServlet 의 형태로 구현하고 JSP 태그 라이브러리를 사용하여 View 레이어를 구현할 것을 권장한다. 또한, Struts는 Action 클래스를 통해 Model 레이어 주변에 래퍼를 제공한다. 그림 1은 Model-View-Controller 디자인에 기반한 Struts 프레임웍이다.

그림 1. Struts와 MVC
Struts and MVC

Struts 컴포넌트 개관
우선 Struts 컴포넌트를 설명하고 웹 애플리케이션 개발시 역할을 설명하겠다.


Action

애플리케이션의 모든 Action 은 Struts의 org.apache.struts.action.Action를 를 확장한다. 이 Action 클래스는 애플리케이션의 Model 레이어로의 인터페이스를 제공하면서 비즈니스 로직 주변의 래퍼로서 작동한다. 각 Action 클래스는 각자의 케이스 스팩(case-specific) 구현을 perform() 메소드에 제공해야 한다. perform() 메소드는 언제나 ActionForward의 값을 리턴한다.


ActionForm

애플리케이션의 모든 ActionForm은 Struts의 org.apache.struts.action.ActionForm을 확장한다. ActionForms 은 요청 매개변수들을 캡슐화 및 유효화하는 단순한 JavaBean이다. 요청 데이터를 유효화하기 위해 ActionFormvalidate() 메소드는 케이스 스팩의 구현을 제공해야 한다. ActionForm은 요청 데이터의 캐리어로서 Action 클래스에 작용한다. JSP 객체는 각각의 ActionForm 을 합하여 애플리케이션의 View 레이어를 만든다. JSP 객체의 거의 모든 폼(form) 필드는 이에 상응하는 ActionForm의 애트리뷰트로 매핑한다.

JSP 커스텀 태그 라이브러리
JSP 커스텀 태그 라이브러리는 태그로 표현되는 액션들의 모음이다. 이것은 JSP Specification 1.1의 강력한 기능이다. 프리젠테이션 티어를 다른 애플리케이션 티어들과 분리할 수 있다. 이 라이브러리는 사용이 쉽고 XML과 같은 방식으로 읽을 수 있다. 자바 스크립틀릿의 사용을 최소화 함으로서 JSP 컴포넌트를 쉽게 관리할 수 있다. Struts가 제공하는 JSP 태그에는 HTML, 로직, 빈 태그들이 포함된다.


ActionErrors

예외 핸들링을 지원할 때 ActionError를 사용한다. ActionError는 애플리케이션 예외를 잡아 View 레이어로 보낸다. 각각은 ActionError의 모음이다. ActionError는 에러 메시지들을 캡슐화 하면서 Presentation 레이어의 </html:errors>ActionError 컬렉션의 모든 에러 메시지들로 렌더링한다.


Best Practice 1. 다중 ActionForm을 통한 데이터 재사용

Struts 컴포넌트에 익숙해졌으니 이제 프레임웍을 활용하는 방법을 설명하겠다. 우선 Struts는 모든 JSP 객체를 ActionForm과 연합할 것을 권장한다. 스크린에 나타난 데이터를 캡슐화 하는 것이다.ActionForm에서 찾은 부속 메소드를 사용하여 JSP 객체에 있는 폼(form) 데이터에 액세스한다. Listing 1은 View 레이어에서 ActionForm 의 일반적이 사용법이다.

Listing 1. JSP에서 ActionForm 사용하기
   <html:form action="/bp1">   <html:text  property="attrib1" />   </html:form >

"BP1AForm" 이라고하는ActionForm에는 attrib1 애트리뷰트를 비롯하여 게터(getter)와 세터(setter) 메소드도 포함되어 있다. 설정 파일인 struts-config.xml에서 "/bp1" 액션은 name 애트리뷰트를 사용하여 bp1AForm 으로 매핑된다. 이것은 JSP에 데이터 디스플레이를 돕는다.

Best Practice 1을 구현할 때 Struts는 다음의 두 가지를 권장한다:

  1. BP1AForm의 애트리뷰트 서브셋을 형성하고있는 애트리뷰트로 JavaBean (BP1BForm) 을 만들고 아울러 이 애트리뷰트의 게터와 세터 메소드도 만든다.
  2. 빈과 BP1AForm을 결합하여 BP1AForm 의 애트리뷰트를 BP1BForm 빈으로 대체한다. 이제 BP1BForm을 통해 BP1AForm 의 애트리뷰트 서브셋에 액세스 할 수 있다. (Listing 2)
Listing 2. JSP의 폼(form) 애트리뷰트에 액세스하기
   <html:form action="/bp1">   
<bean:define name="bp1AForm" property="bp1BForm" id="bp1B"
type="com.ibm.dw.webarch.struts.BP1BForm" />
<html:text name="bp1B" property="subsetAtt1" />
</html:form >

기억하세요!
이 Best Practice 1의 주요 장점은 애트리뷰트 세트에 액세스하기 위해 다중 ActionForm이 필요할 때 이를 사용할 수 있다는 것이다. 다음은 기억해야 할 점이다:

  • Struts는<bean:define/> 태그를 구현한다.
  • <%@ taglib uri="struts-bean.tld" prefix="bean" %> 코드가 struts-bean.tld를 가르킬때 <bean:define/> 태그는 JSP 컴포넌트에서 작동을 시작한다.
  • ActionForm을 확장하는 BP1AForm의 밸리데이션 프레임웍은 BP1BForm의 데이터를 반드시 유효화해야 한다.

애플리케이션에서 Action 클래스를 만들 때, org.apache.struts.action.Action을 직접 확장하는 대신org.apache.struts.action.Action을 확장하여 Action 클래스 (IntermediateAction)를 만든다. 다른 모든 Action 클래스들은 이 IntermediateAction 클래스를 확장한다.

Best Practice 2. Action 클래스를 사용하여 요청 핸들하기
일반적으로 Struts 프레임웍을 사용할 때 JSP 컴포넌트가 애플리케이션을 실행하기위해 요청하는 모든 액션의 경우, 애플리케이션은 Struts의 org.apache.struts.action.Action 을 확장하여 Action 클래스를 만들어야 한다. 이 개별적인 Action 클래스는 요청을 처리하면서 애플리케이션의 Model 레이어와 인터페이싱한다.

Best Practice 2를 구현할 때 Struts는 다음과 같은 절차를 권장한다:

  1. org.apache.struts.action.Action을 확장하여 BP2Action이라고 하는Action 클래스를 만든다.
  2. BP2Action 을 확장하여 웹 애플리케이션에서 다른 모든 Action 클래스들을 만든다.
  3. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException에서 그랬던 것처럼, BP2Action에서 performTask()메소드를 만든다.
  4. BP2Action에서 한 개 이상의 일반적인 메소드를 애플리케이션에 추가한다. 예를 들면 serverSideValidate()과 같은 것이다. 다음 요소들을 고려하여 메소드의 액세스 변경자를 결정할 수 있다:
    • 모든 Action 클래스들이 이 메소드를 구현한다면 이를 추상화하라.
    • 어떤 Action 클래스가 케이스 스팩 구현을 제공한다면 메소드 보호를 선언하고 여기에 디폴트 구현을 제공한다.
  5. BP2Action에서 마지막으로 perform() 메소드를 선언한다. 위에 언급된 일반 메소드를 선언하라. 이는 요청 프로세스 전에 항상 호출되어야 한다. 이제 3단계에서 만들어진 performTask() 메소드를 호출한다.
  6. BP2Action을 확장하는 모든 Action 클래스에서 performTask() 와 케이스 스팩 구현을 추가하라.

장점
Best Practice 2는 두 가지 주요 장점이 있다. 첫째, 웹 애플리케이션의 모든 Action 클래스에서 과잉의 코드를 피할 수 있다. 둘째, 하나의 Action 클래스에 작동을 중앙화 함으로서 애플리케이션의 일반적인 태스크에 대한 제어력을 높인다.

Best Practice 3. ActionForm을 세션 데이터에 작동시키기
Struts 기반의 웹 애플리케이션에서 각ActionFormorg.apache.struts.action.ActionForm을 확장한다. 이 ActionForm은 페이지 데이터를 캡슐화하고 밸리데이션 프레임웍을 제공하여 요청 매개변수의 타당성 검사를 한다.

대부분의 웹 애플리케이션들은 세션에서 데이터를 관리하여 애플리케이션을 통해 사용할 수 있도록 한다. Best Practice 3은 웹 애플리케이션 기능을 다룬다. toSession()fromSession() 메소드가 세션 데이터를 폼(form) 데이터 간 이동할 수 있도록 한다.

  1. org.apache.struts.action.ActionForm을 확장하여 추상 클래스 BP3Form을 만든다.
  2. BP3Form에서 퍼블릭 앱스트랙트void toSession(SessionData sessionData)void fromSession(SessionData sessionData)처럼 메소드와 액세스 변경자를 추가한다.
  3. 모든 ActionForm에서BP3Form을 확장하고 추상 메소드를 구현한다. 이 곳에서 폼 데이터가 세션으로(부터) 옮겨진다.
  4. 상응하는 Action 클래스는 이들 메소드가 호출되는 순서를 결정할 수도 있다. 예를 들어. actionForward가 결정되기 전에 ActionForm에서 toSession() 메소드를 호출할 수 있다.

Best Practice 3을 사용할 때는
Best Practice 3은 세션 데이터들이 하나의 객체로서 관리될 때 가장 유용하다. 또는 모든 페이지가 세션 데이터를 조작하거나 사용할 때도 유용하다.

Best Practice 4.효과적인 예외 핸들링
전통적으로 애플리케이션 예외는 Action 클래스에서 발생한다. 이 예외는 첫 번째로 기록된다. 그런 다음 Action 클래스는 ActionError를 만들고 이를 애플리케이션 범위 안에 저장한다. 이 클래스는 콘트롤을 적절한 ActionForward에 전달한다. Listing 3은 Action 클래스가 예외를 핸들하는 방법을 보여준다.

Listing 3. Action 클래스에서의 예외 핸들링
   try {   //Code in Action class   }   
catch (ApplicationException e) {
//log exception
ActionErrors actionErrors = new ActionErrors();
ActionError actionError = new ActionError(e.getErrorCode());
actionErrors.add(ActionErrors.GLOBAL_ERROR, actionError);
saveErrors(request, actionErrors); }

일반적인 예외 핸들링 절차가 모든 Action 클래스에 예외 정보를 저장하는 반면, Best Practice 4의 목적은 예외 핸들링을 하면서 과잉의 코드를 막는 것이다:

  1. org.apache.struts.action.Action을 확장하여 BP4Action라는 Action 클래스를 만든다.
  2. BP4Action을 확장하여 웹 애플리케이션에 다른 모든 Action 클래스들을 만든다.
  3. BP4Action에서, ActionErrors actionErrors = new ActionErrors(); 변수를 선언한다.
  4. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, ActionErrors actionErrors) throws IOException, ServletException처럼 BP4ActionperformTask() 메소드를 만든다.
  5. BP4Action에서 마지막으로 perform() 메소드를 선언한다. 그런 다음 일반적인 메소드를 호출한다. 이들은 항상 요청 프로세스 전에 호출되어야 한다. 이제 이전 단계에서 만들어진 performTask() 메소드를 호출할 수 있다.
  6. BP4Action을 확장하여 모든 Action 클래스에서 performTask()메소드를 구현하면서 애플리케이션 예외를 처리한다. (Listing 4)
Listing 4. ActionErrors를 효과적으로 사용하기
   try		 {   //Code in Action class   }   
catch(ApplicationException appException) {
//Log exception
//Add error to actionErrors
actionErrors.add(ActionErrors.GLOBAL_ERROR,
new ActionError(appException.getErrorCode())); }

BP4Action에서 performTask() 메소드를 호출한 후에 saveErrors(request, errors)를 사용하여 ActionErrors를 저장한다.

장점
Best Practice 4의 큰 장점은 ActionErrors를 핸들하는 Action 클래스에서 코드 과잉을 막는다.

Posted by 1010
반응형
MVC 패턴은 웹뿐 아니라 어플리케이션 개발에서도 굉장히 많이 사용하는 디자인 패턴이다.
예를들어 MFC가 그렇다.

1.1.1 Classic MVC becomes outdated

  • MVC 3개의 레이어로 구성되어 레이어별 역할이 뚜렷하기 때문에 협업이 용이하고, 디자인과 코딩의 분리가 자연스럽다.
  • JSP 페이지에 비즈니스 로직이 제외되므로 가독성이 증가하고 유지보수가 용이하다.
  • M(Model) : BO (Business Object), DO(Domain Object), VO (Value Object) ...
  • V(View) : HTML(정적인 컨텐츠) , JSP(동적인 컨텐츠)..
  • C(Controller) : (ex) Servlet

첫번째는 MVC의 가장 기본적인 패턴이다

View에 있는 Buttons을 클릭하면, Controller는 View로부터 이벤트를 받아 Model에서 로직을 수행하고
View는 Model로부터 전달된 model-change 이벤트를 받아 데이터를 갱신한다.

예를 들어 회원이 로그인을 한다를 세 파트로 나눠서 보면,
View    : 웹페이지에 떠있는 ID와 Password입력용 텍스트박스와 Log-in 버튼 정도가 되겠습니다.
Model  : ID와 Password를 입력하면 이 값을 자바에서 사용가능한 자료형으로 변환을 해서 보관을 합니다.
             이 역활을 담당해주는게 Model입니다.
Control : 실질적인 역활 수행을 하게 됩니다.
             입력된 값을 받아서 DB의 내용을 조회해보게 되겠죠.

자 다시 얘기하자면, 웹페이지에 텍스트 박스와 로그인버튼이 있는 화면을 제공하는 부분과 Log-in버튼을 눌렀을때 웹페이지의 텍스트박스안에 입력한 값을 넘겨 받는 부분 그리고 이 값을 가지고 로그인 과정을 수행한뒤 결과를 반환하는 과정. 이렇게 3부분으로 나눈게 바로 MVC패턴이 되겠습니다.

하지만 오랜 경험상 다들 아시겠지만, 변하지 않는 패턴은 죽은 패턴입니다.
이 MVC패턴도 변화하게 되는데, 다음은 그 2번째 모형입니다.



새로 추가된게 보이시나요?
Dispatcher가 추가된게 새 MVC 패턴입니다.

1.1.2 Classic MVC gets an update: the Front Controller

  • 웹 버전의 MVC는 Front Controller 패턴을 구현한다.
  • 웹 버전의 MVC에서 View는 Controller를 직접 호출하지 않고, web request기반의 URL 매핑을 이용하여 호출한다.
  • request URL을 수행될 command instance로 보내기 위해 dispatcher를 사용한다.
  • 모든 request는 dispatcher를 통해 Controller에 전달된다.
  • Model에서 View에 직접 model-change 를 notify할 수 없고, Controller를 통해서 View에 오직 최종 요청의 결과를 보내준다.


    기본적인 MVC패턴은 하나의 로직은 하나의 모델과 하나의 컨트롤을 가졌습니다.
    MVC 패턴의 목적은 M-V-C를 나눔으로서 웹디자이너와 프로그래머 사이의 분업을 보다 쉽게 하기 위함이었습니다.
    마찬가지로 어플리케이션 개발에서도 MVC패턴을 사용합니다. GUI 따로 프로그래밍 따로죠.

    여기서 문제점이 발생하게 되는데, 기본MVC모델은 프로그래머가 뭔가 바꿧을 경우...
    예를 들자면, 메소드의 이름을 하나 바꿨을 경우라던가 파일명을 바꿨다라던가 했을 때
    Control을 직접 view에서 호출하기 때문에 디자이너와의 썸씽이 불가피하게 됩니다.

    그래서 Dispatcher가 추가된 새 모델이 나오게 됩니다.
    이 Dispatcher는 말 그대로 분류해주는 역활을 하게 됩니다. WINAPI를 해보셨다면 이해가 쉬우실겁니다.

    비교해서 말하자면 기존에는 view에서 bean을 부르고 id와 password에 매치시켜준뒤 해당 메소드를 호출함으로서 결과를 얻게 되고 이 결과에 따라 웹페이지를 사용자에게 보여주면서 하나의 로직이 끝나게 됩니다.
    새 모델에서는 view에서는 일체의 bean과 연동되는 작업이 없습니다. 단지 이 Dispatcher에게 post든 get이든 값을 넘겨줄때 파라미터를 하나 더 추가해줍니다.

    "LOGIN"이라는 파라미터지요.
    네 이제 감이 잡히시나요? Dispatcher에선 이 파라미터를 분석해서 어떤 서비스를 호출할 것인가를 결정하는 겁니다.

    다시 짧게 정리하자면
    기존 모델 : view에서 직접 로직을 호출. (예) LOGIN에 관련된 메소드를 호출한다.
    새    모델 : view에서 약속된 파라미터를 추가한 값을 Dispatcher에게 넘겨주면 이 Dispatcher에서 로직을 호출.
                    (예) view에서 "LOGIN"이라는 파라미터가 추가된 값을 post or get으로 넘겨준다. Dispatcher에서
                          LOGIN이라는 값을 가지고 LOGIN에 관련된 메소드를 호출한다.

    이리하여 진정한! MVC패턴이 나타나기 시작합니다.
    프로그래머는 웹마스터에게 단지 로긴할땐 Login이란 값을 파라미터로 넘겨라 라고 주문만 하고 로긴에 관련된 메소드는
    boolean 웹마스터귀는당나귀귀(String id, String pass){...} 라고 만들어도 아무런 문제가 없게 된겁니다.



    다음은 마지막 MVC패턴 차례가 되겠습니다.

    1.1.3 MVC evolves: the Page Controller

    • 수행 될 controller를 dispatcher를 이용하여 찾지 않고 view를 직접 호출하면, view가 렌더링되기 전에 controller를 직접 호출한다.
    • Page Controller는 X,Y,Z 작업을 분리하여 좀 더 모듈화가 된 것으로 보인다.
    • WebWork에서는<webwork:action> 커스텀 태그를 이용해 Page Controller를 지원한다.
    • Struts에 익숙하다면 Front Controller 패턴이 매우 익숙하지만, 몇 몇 프레임워크는 캡슐화가 용이하기 때문에 Page Controller를 수용한다.


    예, 이번엔 Dispatcher가 사라졌군요? 그리고 조금 복잡해 보입니다.
    하지만 모든 일이 그렇듯 알고 보면 별게 아니...ㄹ겁니다.

    사실 Dispatcher가 사라진거 같이 보이지만, 그건 눈속임 입니다.
    View.jsp를 감싸고 있는 Page Controller라는게 추가가 됩니다.
    이녀석은 로그인과 로그아웃이라는 작업 형태를 Action으로 분리해서 가지고 있습니다.
    사용자가 로그인을 선택하면 로그인Action을 로그아웃을 선택하면 로그아웃Action을 호출합니다.

    어떤가요? 하는 일이 Dispatcher와 똑같죠.


    네...그럼 차이점을 한번 살펴보겠습니다.
    Front controller 1.1.2 방식에서는 파라미터를 가지고 요청의 성질을 결정했습니다.
    이런 접근 방식은 웹디자이너와 프로그래머의 일을 확실히 나누기 위함 입니다.

    Page Controller에서는 커스텀 테그를 이용하여 이 과정을 거치게 됩니다.
    즉, 태그를 사용함으로서 직관적으로 서비스와의 연동을 이룰 수 있으며 약속된 태그를 사용함으로서 분업을 이룰 수 있게 해줍니다.

    이 패턴은 한번도 적용해 본 적이 없어서 확실히 말씀드리기 어렵습니다만... 추측하기에 Struts의 그것과 비슷하지 않을까 싶습니다.

    MVC패턴은 웹에서는 굉장히 요긴한 패턴입니다. ㅇ_ㅇ/
  • Posted by 1010
    반응형
    struts2  package란 !

    Struts1의 모듈(Module)의 개념
    클라이언트 요청 처리에 필요한 것을 논리적으로 그룹핑하는데 사용한다.
    result-typles
    interceptors
    default-interceptor-ref
    default-action-ref
    global-results
    global-exception-mappings
    action

    패키지는 다른 패키지를 상속한다.
    중복된 설정을 줄여준다.
    모든 패키지는 디폴트 설정 파일인 struts-default.xml에 선언된
     struts-default 패키지를 상속해야 한다.


    1.package 선언

     <package name=“패키지명“ namespace=“/패키지명" extends="struts-default">
       </package>
    name (필수 속성)
    패키지를 구분하는 이름
    namespace
    Action의 URL 경로로 사용한다.
    http://~/contextName/패키지명/~.action
    WebContent/패키지명 폴더가 존재해야 한다.
    extends
    상속할 패키지 이름을 지정한다.
    기본적으로 struts-default 패키지를 지정해야 한다.

    2.default 패키지

    namespace  속성이 생략되거나, namespace 속성 값이 “”로 지정된 패키지를 말한다.
    Struts2는 액션을 특정 패키지에서 찾을 수 없을 경우 최종적으로 디폴트 패키지에서 액션을 찾는다.
    -root 패키지나 user패키지의 action이 없을 경우에 default 패키지로 찾아가서
    결과를 찾는다 . 오류 검사시에 반듯이 필요하게 될 것이다

    <!-- 디폴트 패키지 선언 -->
    <package name="default" namespace="" extends="struts-default">
    </package>

    3.root 패키지


    루트 패키지는 namespace 속성 값이 “/”로 지정된 패키지를 말한다.
    요청 URL이 컨텍스트 패스 바로 뒤에 액션명.action 형태로 들어 왔을 때, 사용되는 패키지이다.


    4.외부 xml 패키지 삽입
    형태 및 위치
    struts.xml 파일과 동일한 DTD 사용
    클래스 패스의 어느 곳에나 존재 가능
    목적
    기능별로 패키지화해서 독립적으로 개발하고자 할 경우 (Struts1에서의 모듈 개념)
    <include file="struts-root.xml"/>
    Posted by 1010
    반응형

    스트럿츠의 역할

    - 모델과 뷰 사이의 공백을 연결해주는 역할을 담당

    - 개발자들이 데이터베이스나 웹 페이지와 같은 가공되지 않은 재료로부터 각 요소들이 서로 긴밀히 연결된 하나의 애플리케이션을 만드는 데 도움을 주는 보이지 않는 토대들의 집합

    - 개발자가 애플리케이션과 사용자 사이의 상호 작용 방식을 구체적으로 정의하기 위한 프로그램 가능한 컴포넌트들의 집합

    *하이퍼링크: 애플리케이션에 있는 특정한 리소스(웹 페이지, 커스텀 액션)로 연결하는 일종의 경로

    스트럿츠에서는 ActionForward를 사용하여 하이퍼링크를 정의

     

     

    각 컴포넌트의 역할

     

    ActionForward

    논리적인 이름과 path프로퍼티를 가진다.

    - XMl 컨피규레이션 파일로 정의

    - 스트럿츠가 웹 애플리케이션을 로딩할 때 읽어들인다.

    - 해당 링크를 참조하는 모든 컴포넌트들을 변경하지 않고도 링크에 대한 목적지를 쉽게 변경할 수 있다.

    ActionForward 자바빈 생성 예

    <forward name =welcomepath=/pages/index.jsp”/>

     

    ActionForm

    검증과 수정 사이클을 관리하도록 몇 가지 표준 메소드들이 추가된 자바빈즈

    - 프로퍼티들을 HTML 컨트롤의 속성과 일치시키는 작업은 스트럿츠에서 자동으로 처리된다.

    - 스트럿츠 컨피규레이션은 일련의 디스크립터(<form-beans>와 <form-bean>요소)를 통해 생성된 ActionForm 클래스를 참조한다.

    - 스트럿츠 1.1에서는 각각의 프로퍼티들을 직접 정의하는 대신 java.util.Map을 사용하여 어트리뷰트 이름으로 저장할 수 있다. (DynaBean)

    ActionForm 자바빈 생성 예

    package app;

    import org.apache.struts.action.*;

    public class loginform extends actionform{

    private string username = null;

    public string getusername(){

    return (this.username);

    }

    public void setusername(string username){

    this.username = username;

    }

    <form-beans>

    <form-bean name=LoginFormtype=app.LoginForm”/>

    </form-beans>

     

    Action(커스텀 액션)

    - HTML 폼은 action 파라미터를 이용하여 폼의 데이터를 보낼 위치를 브라우저에게 알려준다.

    - 프레임워크가 알아서 ActionForm을 생성하고, 적절한 값을 설정하고, 검사한 다음 ActionForm을 Action 객체에게 전달한다.

    - Action은 ActionForward 객체를 컨트롤러에게 전달한다.

    - Action은 시스템 경로가 아닌 논리적인 이름을 사용하여 정의를 선택할 수 있게 된다.

    - 컨트롤러는 확장성을 보장하기 위해서 현재 요청과 응답 객체도 함께 전달한다.



    스트럿츠 핵심 클래스 정의

     

    • ActionServlet : MVC 컨트롤러이며 요청 디스패터의 역할을 한다. 스트럿츠 프레임워크 내에 단 하나의 서블릿 인스턴스가 존재한다.
    • ActionMapping : URL 패턴과 비즈니스 로직(Action)간의 매핑을 표현한다. 입력과 출력 빛 비즈니스 로직에 기반한 '보내지는'대상을 지정한다.
    • ActionForm : MVC모델이다. MVC뷰로부터의 입력을 표현하는 자바빈처럼 작동한다. ActionServlet은 자동적으로 빈이 인스턴스화된 이후에 프로퍼티를 지정해 주며, 빈에 validate()메소드를 가지고 있으면 사용자가 만든 Action클래스를 호출하기 이전에 이 메소드를 호출한다. ActionForm은 JSP 뷰를 표현하기 위해 확장되었다.
    • ActionForward :이동할 페이지 정보와 포워드(forward), 리다이렉트(redirect)중 어느 방식으로 다음 페이지로 이동할 것인지에 대한 정보를 담고 있다.
    • Action : 비즈니스 로직을 나타내며 MVC모델이다. 특정한 요청에 대한 비즈니스 로직을 처리하기 위해 확장된다. 예를 들어 /login URI를 위해 LoginAction을 만든다.

     ActionServlet
    처음으로 논의할 스트럿츠 프레임워크의 컴포넌트는 ActionServlet이다. org.apache.struts.action 패키지에 있는
    ActionServlet은 추상클래스인 javax.servlet.HttpServlet 추상클래스를 상속받았다.
     클라이언트로 부터 호출 받으면 가장 먼저 호출되는 클래스이다.

    • process() : doGet() 이나 doPost()로부터 요청과 응답을 넘겨받는다. process()메소드는 요청을 처리하기 위해, ActionServlet안에 있는 다른 메소드를 호출한다. process()메소드는 요청을 처리하기 위해,
      ActionServlet안에 있는 다른 메소드를 호출한다. 여기에서는 중요한 9개의 메소드를 살펴볼 것이다.

          1.pocessMultipart() : request content 타입 multipart data를 확인하여 새로운
            request Wrapper만듬

          2.processPath() : 요청 URI 인자에서 접미사나 접두사를 제거하여 URI를 추출한다.
            예를 들어,/login.do라면 /login을 반환한다.

          3.processLocale() : Locale이 요청에 설정되어 있으면 검사하고,
             없으면 하나 만들어서 세션에 저장한다.

          4.processMapping() : ActionMapping객체를 얻기 위해 path를 키로 이용한다.
            만약 path가 맞는 ActionMapping객체가 ActionMappings collection안에 있으면
            process()메소드로 반환된다.
          5.processContent() : 요청 콘텐트 차입과 인코딩 결정 디폴트 (text/html)
          6.ProcessPreprocess() : 서블릿 필터와 같은 역할 요청이 처리되기정에 할작업처리해줌
          7.processRoles() : 사용자가 요청한 request에서 처리할수 있는 role 있는지 판별
          8. processActionForm() : ActionMapping의 name속성을 이용하여 이런 종류의
             ActionForm이 이미 만들어졌는지를 확인한다. 그렇다면,
             그 ActionForm이 process()메소드로 반환되며, 그렇지 않으면 ActionMapping에
             정의된 새로운 ActionForm을 만들어서 반환한다.

          9.processPopulate() : ActionForm내에 ActionServlet의 참조를 설정하며,
            ActionForm()의 reset()메소드를 호출하여 값들을 디폴트 값으로 만든다.

          10.processValidate() : ActionForm에 확인작업이 필요한지를 검사한다.
          11.processForward() : action 태그에서 forward 속성이 설정되어 있다면
              RequestDispatcher forward()메소드 호출
          12.processInclude() : action 태그에 include속성이 설정되어있다면        
              RequestDispatcher include()메소드 호출
          13.processActionCreate() : Action 객체를 생성한다 Action 객체가
              이미 존재한다면 재사용 하게 된다.

          14.processActionPerform() : processActionCreate()에서 반환된 Action객체의
              perform()메소드가 호출된다. 비즈니스 로직을 수행한 후에, Action객체는
              ActionServlet이 요청을 보낼 타겟을 가지고 있는 ActionMapping을 반환한다.

          15.processActionForward() : RequestDispatcher나 Request.sendRedirect()를
              이용하여 원하는 뷰를 만들고,요청을 ActionServlet에 의해 완전히 처리한다.

    • initApplication() : 애플리케이션을 위해 MessageResource를 로딩한다.
    • initMapping() : ActionServlet에 pageckage.ClassforName을 등록하도록 한다.
      formBean의 디폴트 클래스는 org.apache.struts.action.ActionFormBean이다.
    • initDigester() : org.apache.struts.digester.Digester클래스를 써어
      struts-config.xml 파일로부터 설정을 읽어들인다.
    • initOter() : 초기화에 필요한 작업을 처리한다. struts-config.xml에 있는
      initOther()는 content , locale , cache를 찾는다.


    ActionMapping
    비즈니스 로직을 처리할 Action클래스에 요청을 보내기 위해 ActionServlet이 알아야 할 처리 정보를 가지고 있다.
    ActionMapping은 <action-mappings> 요소 내의 <action>정보를 이용하여 만들어진다.
    <action-mappings>

       <action name="loginForm" path="/login" type="kr.co.a.LoginAction" input="/login.jsp"> </action>

    </action-mappings>
    /login의 경로를 가진 요청을 받으면, 액션 클래스는 LoginAction이고, 입력은 loginForm이
    표현하며 입력 폼의 경로는 /login.jsp라는 것을 의미한다.

    • name : 이 액션이 사용할 struts-config.xml에 정의된 폼 빈의 이름
    • path : 이 매핑을 선택하는 데 매칭되는 요청 URI. URI는 '/'로 시작해야 한다.
    • type : 이 매핑에서 사용되는 Action 구현의 완전한 자바 클래스 이름
    • input : 입력 페이지를 매핑한다. ActionServlet이 ActionForm의 확인작업을 수행하는데,
      만일 에러가 있다면 input변수가 forward나 sendRedirect의 목표가 될 것이다.


    ActionForm
    ActionForm클래스는 struts-config.xml에 의해 정의된다.

    <form-beans> <form-bean name="loginForm" type="kr.co.a.LoginForm" /> </form-beans> 


    모든 ActionForm 구현은 <form-beans>태그의 몸체에 등록되어야 한다. ActionForm이 추상 클래스이므로,
     개발자는 이를 확장하여 사용자의 입력 데이터를 저장하는 클래스를 만들어야 한다.

    • name : ActionMapping에서 정의된 범위 안에서 이를 얻기 위해 사용된느 변수명
    • type : 사용자가 구현한 ActionForm에 대한 패키지의 올바른 참조


    Action
    어떤 URI에 특정 액션을 제공하기 위해 확장된다. 이것의 목적은 요청을 위한 비즈니스 로직을 실행하는 것이다.
    Action클래스는 ActionServlet에 의해 인스턴스로 만들어진다. Action클래스는 기본 Locale을 저장하며,
    애플리케이션에 대한 MessageResources의 참조를 저장한다.


    ActionForward
    ActionForward클래스는 struts-config.xml 파일에서 설정된다. 두 종류의 ActionForward가 있다.

     

     

    Posted by 1010
    반응형

    Struts
    웹 애플리케이션을 구현할 때 사용하는 오픈 소스 프레임웍인 Struts는 대중적인 디자인 패러다임에 근거하고 있다. 이 프레임웍은 Java Servlets, JavaBeans, ResourceBundles, XML 같은 표준 기술을 바탕으로 구현되어 유연하고 확장성 있는 컴포넌트를 제공한다. Struts는 Controller 레이어를 ActionServlet 의 형태로 구현하고 JSP 태그 라이브러리를 사용하여 View 레이어를 구현할 것을 권장한다. 또한, Struts는 Action 클래스를 통해 Model 레이어 주변에 래퍼를 제공한다. 그림 1은 Model-View-Controller 디자인에 기반한 Struts 프레임웍이다.

    그림 1. Struts와 MVC
    Struts and MVC

    Struts 컴포넌트 개관
    우선 Struts 컴포넌트를 설명하고 웹 애플리케이션 개발시 역할을 설명하겠다.


    Action

    애플리케이션의 모든 Action 은 Struts의 org.apache.struts.action.Action를 를 확장한다. 이 Action 클래스는 애플리케이션의 Model 레이어로의 인터페이스를 제공하면서 비즈니스 로직 주변의 래퍼로서 작동한다. 각 Action 클래스는 각자의 케이스 스팩(case-specific) 구현을 perform() 메소드에 제공해야 한다. perform() 메소드는 언제나 ActionForward의 값을 리턴한다.


    ActionForm

    애플리케이션의 모든 ActionForm은 Struts의 org.apache.struts.action.ActionForm을 확장한다. ActionForms 은 요청 매개변수들을 캡슐화 및 유효화하는 단순한 JavaBean이다. 요청 데이터를 유효화하기 위해 ActionFormvalidate() 메소드는 케이스 스팩의 구현을 제공해야 한다. ActionForm은 요청 데이터의 캐리어로서 Action 클래스에 작용한다. JSP 객체는 각각의 ActionForm 을 합하여 애플리케이션의 View 레이어를 만든다. JSP 객체의 거의 모든 폼(form) 필드는 이에 상응하는 ActionForm의 애트리뷰트로 매핑한다.

    JSP 커스텀 태그 라이브러리
    JSP 커스텀 태그 라이브러리는 태그로 표현되는 액션들의 모음이다. 이것은 JSP Specification 1.1의 강력한 기능이다. 프리젠테이션 티어를 다른 애플리케이션 티어들과 분리할 수 있다. 이 라이브러리는 사용이 쉽고 XML과 같은 방식으로 읽을 수 있다. 자바 스크립틀릿의 사용을 최소화 함으로서 JSP 컴포넌트를 쉽게 관리할 수 있다. Struts가 제공하는 JSP 태그에는 HTML, 로직, 빈 태그들이 포함된다.


    ActionErrors

    예외 핸들링을 지원할 때 ActionError를 사용한다. ActionError는 애플리케이션 예외를 잡아 View 레이어로 보낸다. 각각은 ActionError의 모음이다. ActionError는 에러 메시지들을 캡슐화 하면서 Presentation 레이어의 </html:errors>ActionError 컬렉션의 모든 에러 메시지들로 렌더링한다.


    Best Practice 1. 다중 ActionForm을 통한 데이터 재사용

    Struts 컴포넌트에 익숙해졌으니 이제 프레임웍을 활용하는 방법을 설명하겠다. 우선 Struts는 모든 JSP 객체를 ActionForm과 연합할 것을 권장한다. 스크린에 나타난 데이터를 캡슐화 하는 것이다.ActionForm에서 찾은 부속 메소드를 사용하여 JSP 객체에 있는 폼(form) 데이터에 액세스한다. Listing 1은 View 레이어에서 ActionForm 의 일반적이 사용법이다.

    Listing 1. JSP에서 ActionForm 사용하기
       <html:form action="/bp1">   <html:text  property="attrib1" />   </html:form >

    "BP1AForm" 이라고하는ActionForm에는 attrib1 애트리뷰트를 비롯하여 게터(getter)와 세터(setter) 메소드도 포함되어 있다. 설정 파일인 struts-config.xml에서 "/bp1" 액션은 name 애트리뷰트를 사용하여 bp1AForm 으로 매핑된다. 이것은 JSP에 데이터 디스플레이를 돕는다.

    Best Practice 1을 구현할 때 Struts는 다음의 두 가지를 권장한다:

    1. BP1AForm의 애트리뷰트 서브셋을 형성하고있는 애트리뷰트로 JavaBean (BP1BForm) 을 만들고 아울러 이 애트리뷰트의 게터와 세터 메소드도 만든다.
    2. 빈과 BP1AForm을 결합하여 BP1AForm 의 애트리뷰트를 BP1BForm 빈으로 대체한다. 이제 BP1BForm을 통해 BP1AForm 의 애트리뷰트 서브셋에 액세스 할 수 있다. (Listing 2)
    Listing 2. JSP의 폼(form) 애트리뷰트에 액세스하기
       <html:form action="/bp1">   
    <bean:define name="bp1AForm" property="bp1BForm" id="bp1B"
    type="com.ibm.dw.webarch.struts.BP1BForm" />
    <html:text name="bp1B" property="subsetAtt1" />
    </html:form >

    기억하세요!
    이 Best Practice 1의 주요 장점은 애트리뷰트 세트에 액세스하기 위해 다중 ActionForm이 필요할 때 이를 사용할 수 있다는 것이다. 다음은 기억해야 할 점이다:

    • Struts는<bean:define/> 태그를 구현한다.
    • <%@ taglib uri="struts-bean.tld" prefix="bean" %> 코드가 struts-bean.tld를 가르킬때 <bean:define/> 태그는 JSP 컴포넌트에서 작동을 시작한다.
    • ActionForm을 확장하는 BP1AForm의 밸리데이션 프레임웍은 BP1BForm의 데이터를 반드시 유효화해야 한다.

    애플리케이션에서 Action 클래스를 만들 때, org.apache.struts.action.Action을 직접 확장하는 대신org.apache.struts.action.Action을 확장하여 Action 클래스 (IntermediateAction)를 만든다. 다른 모든 Action 클래스들은 이 IntermediateAction 클래스를 확장한다.

    Best Practice 2. Action 클래스를 사용하여 요청 핸들하기
    일반적으로 Struts 프레임웍을 사용할 때 JSP 컴포넌트가 애플리케이션을 실행하기위해 요청하는 모든 액션의 경우, 애플리케이션은 Struts의 org.apache.struts.action.Action 을 확장하여 Action 클래스를 만들어야 한다. 이 개별적인 Action 클래스는 요청을 처리하면서 애플리케이션의 Model 레이어와 인터페이싱한다.

    Best Practice 2를 구현할 때 Struts는 다음과 같은 절차를 권장한다:

    1. org.apache.struts.action.Action을 확장하여 BP2Action이라고 하는Action 클래스를 만든다.
    2. BP2Action 을 확장하여 웹 애플리케이션에서 다른 모든 Action 클래스들을 만든다.
    3. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException에서 그랬던 것처럼, BP2Action에서 performTask()메소드를 만든다.
    4. BP2Action에서 한 개 이상의 일반적인 메소드를 애플리케이션에 추가한다. 예를 들면 serverSideValidate()과 같은 것이다. 다음 요소들을 고려하여 메소드의 액세스 변경자를 결정할 수 있다:
      • 모든 Action 클래스들이 이 메소드를 구현한다면 이를 추상화하라.
      • 어떤 Action 클래스가 케이스 스팩 구현을 제공한다면 메소드 보호를 선언하고 여기에 디폴트 구현을 제공한다.
    5. BP2Action에서 마지막으로 perform() 메소드를 선언한다. 위에 언급된 일반 메소드를 선언하라. 이는 요청 프로세스 전에 항상 호출되어야 한다. 이제 3단계에서 만들어진 performTask() 메소드를 호출한다.
    6. BP2Action을 확장하는 모든 Action 클래스에서 performTask() 와 케이스 스팩 구현을 추가하라.

    장점
    Best Practice 2는 두 가지 주요 장점이 있다. 첫째, 웹 애플리케이션의 모든 Action 클래스에서 과잉의 코드를 피할 수 있다. 둘째, 하나의 Action 클래스에 작동을 중앙화 함으로서 애플리케이션의 일반적인 태스크에 대한 제어력을 높인다.

    Best Practice 3. ActionForm을 세션 데이터에 작동시키기
    Struts 기반의 웹 애플리케이션에서 각ActionFormorg.apache.struts.action.ActionForm을 확장한다. 이 ActionForm은 페이지 데이터를 캡슐화하고 밸리데이션 프레임웍을 제공하여 요청 매개변수의 타당성 검사를 한다.

    대부분의 웹 애플리케이션들은 세션에서 데이터를 관리하여 애플리케이션을 통해 사용할 수 있도록 한다. Best Practice 3은 웹 애플리케이션 기능을 다룬다. toSession()fromSession() 메소드가 세션 데이터를 폼(form) 데이터 간 이동할 수 있도록 한다.

    1. org.apache.struts.action.ActionForm을 확장하여 추상 클래스 BP3Form을 만든다.
    2. BP3Form에서 퍼블릭 앱스트랙트void toSession(SessionData sessionData)void fromSession(SessionData sessionData)처럼 메소드와 액세스 변경자를 추가한다.
    3. 모든 ActionForm에서BP3Form을 확장하고 추상 메소드를 구현한다. 이 곳에서 폼 데이터가 세션으로(부터) 옮겨진다.
    4. 상응하는 Action 클래스는 이들 메소드가 호출되는 순서를 결정할 수도 있다. 예를 들어. actionForward가 결정되기 전에 ActionForm에서 toSession() 메소드를 호출할 수 있다.

    Best Practice 3을 사용할 때는
    Best Practice 3은 세션 데이터들이 하나의 객체로서 관리될 때 가장 유용하다. 또는 모든 페이지가 세션 데이터를 조작하거나 사용할 때도 유용하다.

    Best Practice 4.효과적인 예외 핸들링
    전통적으로 애플리케이션 예외는 Action 클래스에서 발생한다. 이 예외는 첫 번째로 기록된다. 그런 다음 Action 클래스는 ActionError를 만들고 이를 애플리케이션 범위 안에 저장한다. 이 클래스는 콘트롤을 적절한 ActionForward에 전달한다. Listing 3은 Action 클래스가 예외를 핸들하는 방법을 보여준다.

    Listing 3. Action 클래스에서의 예외 핸들링
       try {   //Code in Action class   }   
    catch (ApplicationException e) {
    //log exception
    ActionErrors actionErrors = new ActionErrors();
    ActionError actionError = new ActionError(e.getErrorCode());
    actionErrors.add(ActionErrors.GLOBAL_ERROR, actionError);
    saveErrors(request, actionErrors); }

    일반적인 예외 핸들링 절차가 모든 Action 클래스에 예외 정보를 저장하는 반면, Best Practice 4의 목적은 예외 핸들링을 하면서 과잉의 코드를 막는 것이다:

    1. org.apache.struts.action.Action을 확장하여 BP4Action라는 Action 클래스를 만든다.
    2. BP4Action을 확장하여 웹 애플리케이션에 다른 모든 Action 클래스들을 만든다.
    3. BP4Action에서, ActionErrors actionErrors = new ActionErrors(); 변수를 선언한다.
    4. 퍼블릭 앱스트랙트 ActionForward performTask(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, ActionErrors actionErrors) throws IOException, ServletException처럼 BP4ActionperformTask() 메소드를 만든다.
    5. BP4Action에서 마지막으로 perform() 메소드를 선언한다. 그런 다음 일반적인 메소드를 호출한다. 이들은 항상 요청 프로세스 전에 호출되어야 한다. 이제 이전 단계에서 만들어진 performTask() 메소드를 호출할 수 있다.
    6. BP4Action을 확장하여 모든 Action 클래스에서 performTask()메소드를 구현하면서 애플리케이션 예외를 처리한다. (Listing 4)
    Listing 4. ActionErrors를 효과적으로 사용하기
       try		 {   //Code in Action class   }   
    catch(ApplicationException appException) {
    //Log exception
    //Add error to actionErrors
    actionErrors.add(ActionErrors.GLOBAL_ERROR,
    new ActionError(appException.getErrorCode())); }

    BP4Action에서 performTask() 메소드를 호출한 후에 saveErrors(request, errors)를 사용하여 ActionErrors를 저장한다.

    장점
    Best Practice 4의 큰 장점은 ActionErrors를 핸들하는 Action 클래스에서 코드 과잉을 막는다.


    이 글의 출처 : http://www.ibm.com/developerworks/kr/library/wa-struts/

    Posted by 1010
    반응형
    struts와 ibatis 를 이용한 게시판 생성
    **작업 순서

    1. Dynamic Web project 생성(struts)

    2. WEB-INF -> lib 에
           
            struts,ibatis,JSTL 관련 jar 파일 복사        

                antlr-2.7.2.jar
                commons-beanutils-1.8.0.jar
                commons-chain-1.2.jar
                commons-digester-1.8.jar
                commons-fileupload-1.1.1.jar
                commons-io-1.1.jar
                commons-logging-1.0.4.jar
                commons-validator-1.3.1.jar
                oro-2.0.8.jar
                struts-config.xml
                struts-core-1.3.10.jar
                struts-taglib-1.3.10.jar
                struts-tiles-1.3.10.jar

                ibatis.jar

                jstl.jar
                standard.jar

    3. WEB-INF 에

            struts-config.xml 생성(struts 설정 파일)

    4. META-INF 에

            context.xml 생성(connection pool 설정 파일)

    5. WEB-INF의
       
            web.xml에 struts 관련 설정 및
            connection pool 설정

    6. 패키지 및 jsp 파일용 폴더 생성

            패키지 : action    InsertAction, DeleteAction
                        dao       BoardDao, ibatis 설정 파일(Board.xml SqlMapConfig.xml)
                        vo         BoardVO
                        form      BoardForm
            폴더    : board    list.jsp, write.jsp, ...

    7. jsp -> action -> dao        (or        dao-> action -> jsp )

       jsp , XXXAction 생성
       Dao에 메소드 추가 순서로 작업

    8. struts-config.xml 에
        mapping 정보 추가

    9. server Restart    다시 7번부터 반복


    **실습

    1.  web-inf 에서 board 폴더를 만들고 write.jsp 파일을 생성한다.
        (web-inf 폴더 아래 있는 jsp 파일은 url 입력으로는 절대 접근 불가능하다.)

    2. lib에 board_config.xml을 추가한다.

    3. web.xml에 다음과 같이 config 값을 바꿔준다.

        <init-param>
          <param-name>config</param-name>
          <param-value>/WEB-INF/board-config.xml</param-value>
        </init-param>

    4. board-config.xml에서 다음과 같이 action-mapping 를 추가하면 외부에서 접근할수 없는 페이지에 접근 가능하다.
       
        <action-mappings>
          <action path="/write" forward="/WEB-INF/board/write.jsp"></action>
        </action-mappings>

        http://localhost/struts/write.do
        이런식으로 주소를 입력해주면 web-inf 내부에 있는 페이지로 이동되게 된다.

    5. write.jsp 에 html 코드를 사용하여 화면을 만들어준다.

    6. 또 글 목록을 보여주기 위한 list.jsp를 만들고 html 코드를 작성해 준다.

    7. 기존의 프로젝트에 이미 struts 관련 라이브러리가 있기 때문에
            jstl, ibatis 관련 라이브러리만 추가시켜준다.
       
            apache-tomcat-6.0.18\webapps\examples\WEB-INF\lib
                jstl.jar
                standard.jar

            ibatis-2.3.4.726\lib
                ibatis-2.3.4.726.jar

    8. META-INF 폴더에 context.xml 를 생성하고 connection pool 을 설정해 준다.

                <?xml version="1.0" encoding="UTF-8"?>
                <!-- Connection Pool 설정 파일  -->
                <Context>
                 <Resource name="jdbc/myoracle"
                     auth="Container"
                              type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
                              url="jdbc:oracle:thin:@localhost:1521:orcl"
                              username="scott" password="tiger" maxActive="20"
                              maxIdle="10" maxWait="-1"/>
                </Context>

    9. web.xml에서 자원참조 설정을 해준다.

             <resource-ref>
                  <res-ref-name>jdbc/myoracle</res-ref-name>
                  <res-type>javax.sql.DataSource</res-type>
                  <res-auth>Container</res-auth>
             </resource-ref>

    10. src 에서 패키지를 생성해준다.
            action, form, dao, vo

    11. 게시문의 정보를 전달하기 위한 BoardVO 를 생성한다.
       
             private int no;
             private String title;
             private String writer;
             private String content;
             private int hit;
             private int groupNo;
             private Date regdate;
             private String fileName;


    12. 기존의 테이블에서 첨부파일 이름을 저장하기 위한 속성을 추가한다.

            alter table board2
            add filename varchar2(50)

    13. BoardDao 클래스를 작성

            dao 클래스는 single tone 으로 작성한다.
            static 멤버변수로 자기 자신을 생성하고, static 함수를 통해 객체를 리턴해 주도록 작성한다.

    14. BoardDao 에 ibatis 설정을 해 준다.
           
            dao 패키지에 Board.xml 과 SqlMapConfig.xml 파일을 생성하고

            Board.xml 에 쿼리문을 추가시켜준다.
                 <select id="list" resultClass="vo.BoardVO">
                      select no, title, writer, hit, groupNo, regdate
                      from board2
                 </select>

            SqlMapConfig.xml 파일에 Connection Pool 설정을 해 준다.
                 <!-- Connection Pool 설정 -->
                 <transactionManager type="JDBC" commitRequired="false">
                   <dataSource type="JNDI">
                        <property name="DataSource" value="java:comp/env/jdbc/myoracle"/>
                   </dataSource>
                 </transactionManager>  
            dataSource type="JNDI" 으로 설정해 두명 ibatis 가 tomcat 에서 생성한 Connection Pool을 가져와서 사용한다.
            그리고 쿼리를 실행할 mapper 파일을 설정해 준다.
                    <!-- mapper 파일 등록 -->
                    <sqlMap resource="dao/Board.xml"/>

    15. BoardDao 클래스에서 쿼리를 실행할 메소드를 생성한다.

         우선 sql 을 얻어오기 위해 SqlMapClient 객체를 얻어온다.

             private SqlMapClient mapper;
     
             private BoardDao() {
              try{
                   Reader reader = Resources.getResourceAsReader("dao/SqlMapConfig.xml");
                   mapper = SqlMapClientBuilder.buildSqlMapClient(reader);
              }catch(IOException e){
                   e.printStackTrace();  
              }
       
            반환할 자료가 다수이기 때문에 queryForList() 메소드를 사용한다.
            public List<BoardVO> getList() throws SQLException{
                return mapper.queryForList("list");
             }

    16. action 패키지에 Action을 상속받는 ListAction 클래스를 만들고
            excute() 메소드를 오버라이드 해 준다.
            메소드 내부에서 BoardDao 객체를 얻어오고 getList() 메소드를 호출하여 리스트를 얻어온다.
            그리고 request 의 어트리뷰트로 리스트를 세팅 하고
            /WEB-INF/board/list.jsp 페이지로 포워드 한다.

    17. list.jsp 에서 jstl 을 이용하여 넘어온 리스트를 화면에 그려주도록 한다.

            jstl 을 사용하기 위해 taglib 를 추가시킨다.
           <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

            다음과 같이 forEach 를 사용하여 리스트를 반복하여 그려준다.

            <c:forEach var="board" items="${boardList}">
           
            <!-------------그린게시판 첫번째줄시작 번호. 제목. 작성일. 조회------------>
            <tr bgcolor="f7f7f7">
              <td height="24" align="center">${board.no }</td>
              <td><a href="freeboard_view.htm" class="purple_bbs">${board.title }</a></td>
              <td align="center">${board.writer }</td>
              <td align="center">${board.regdate }</td>
              <td align="center">${board.hit }</td>
            </tr>
            <!-------------그린게시판 두번째줄시작 ----->
            <tr bgcolor="e4e4e4">
              <td height="1" colspan="5"></td>
            </tr>
           
            </c:forEach>
           
    18. board-config.xml 에 다음과 같이 action-mapping 을 추가시켜준다.

             <action-mappings>
                  <action path="/write" forward="/WEB-INF/board/write.jsp"></action>
                  <action path="/list" type="action.ListAction"></action>
             </action-mappings>

    ListAction.java
    Board.xmlBoardDao.javaSqlMapConfig.xml
    BoardVO.java
    board-config.xml
    struts-config.xml
    web.xml
    list.jsp
    write.jsp
    Posted by 1010
    반응형

    MySQL5

    Database/MySQL 2007/08/13 04:41
    작성자 : shin-gosoo(hchshin@chol.com)
    작성일 : 2007.04.10


    새창 보기

    윈도우 자바개발환경을 위한 기본적인 Mysql 5 설치방법입니다.

    목차
    1. 데이터베이스 설치
    2. Mysql 환경설정 - 한글개발환경(euckr)일 경우
    3. Mysql 환경설정 - 다국어 개발환경(utf-8)일 경우
    1. 데이터베이스 설치
    • 설치 버전 : 5.0.37 (2007.04.10 현재 최신 버전)
    • 다운로드 URL : http://dev.mysql.com/downloads/mysql/5.0.html#win32
    • Without installer (unzip in C:\) : mysql-noinstall-5.0.37-win32.zip (45.6M) 를 선택해서 다운로드 받는다.
      개인 취향이겠지만 필자는 인스톨 버전은 싫어함.
      또한, 필자는 개발환경은 하드드라이브가 C, D로 나눠서 있을 경우 D 드라이브에 설치한다. 가끔씩 윈도우를 재설치 할 경우를 대비해서.
    • mysql-noinstall-5.0.37-win32.zip 를 풀면 mysql-5.0.37-win32 폴더가 생긴다. mysql-5.0.37로 이름변경해서 아래와 같이 설치하자.
    • 설치 예)
      • 설치디렉토리 : D:\dev\mysql-5.0.37
      • 윈도우 시스템환경변수 설정
        • Path : D:\dev\mysql-5.0.37\bin; 추가
      • 윈도우 서비스로 설정
        • 도스프롬프트 : D:\dev\mysql-5.0.37\bin> mysqld --install ( 서비스 제거는 mysqld --remvoe )
        • 제어판 - 관리도구 - 서비스를 통해 Mysql 서비스 시작
      • 도스 프롬프트에서 C:/>mysql -uroot 로 접속되면 설치 성공.
    2. Mysql 환경설정 - 한글개발환경(euckr)일 경우
    • my.ini 설정
    • C:\Windows 밑에 my.ini 파일 생성
      1. [mysql]  
      2. default-character-set = euckr 
      3.  
      4. [mysqld]  
      5. character-set-client-handshake=FALSE 
      6. init_connect="SET collation_connection = euckr_korean_ci" 
      7. init_connect="SET NAMES euckr" 
      8. default-character-set = euckr 
      9. character-set-server = euckr 
      10.  
      11. collation-server = euckr_korean_ci 
      12.  
      13. [client]  
      14. default-character-set = euckr 
      15.  
      16. [mysqldump]  
      17. default-character-set = euckr 
    • Mysql Restart
    • root 계정으로 mysql 접속후
    • mysql>status
    • 아래와 같이 나오면 설정 OK.
      1. mysql> status  
      2. --------------  
      3. mysql  Ver 14.12 Distrib 5.0.37, for Win32 (ia32)  
      4.  
      5. Connection id:          1  
      6. Current database:  
      7. Current user:           root@localhost  
      8. SSL:                    Not in use  
      9. Using delimiter:        ;  
      10. Server version:         5.0.37-community MySQL Community Edition (GPL)  
      11. Protocol version:       10  
      12. Connection:             localhost via TCP/IP  
      13. Server characterset:    euckr  
      14. Db     characterset:    euckr  
      15. Client characterset:    euckr  
      16. Conn.  characterset:    euckr  
      17. TCP port:               3306  
      18. Uptime:                 10 sec  
      19.  
      20. Threads: 1  Questions: 4  Slow queries: 0  Opens: 12  Flush tables: 1  Open tabl  
      21. es: 6  Queries per second avg: 0.400  
      22. --------------  
      23.  
      24. mysql> 
    • root 계정 초기 비밀번호 지정하기
      1. C:>mysql -uroot mysql  
      2.  
      3. mysql>update user set password=password('새비밀번호') where user='root';  
      4. mysql>flush privileges;  
      5. mysql>exit  
      6.  
      7. C:>mysql -uroot -p새비밀번호  
    • 데이터베이스 생성 및 사용자 생성
      1. C:>mysql -uroot -p비밀번호  
      2.  
      3. mysql>CREATE DATABASE myproject_kr DEFAULT CHARACTER SET euckr COLLATE euckr_korean_ci;  
      4.  
      5. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'localhost' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      6.  
      7. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'%' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      8.  
      9. mysql>FLUSH PRIVILEGES;  
      10.  
      11. mysql>exit  
      12.  
      13. C:>mysql -ujavamaster -p1234 myproject_kr  
      14.    
      4라인 : euckr 환경으로 myproject_kr 이라는 데이터베이스 생성
      6라인 : 아이디 javamaster, 비밀번호 1234로 로컬에서만 접속권한이 있는 사용자 생성
      8라인 : 아이디 javamaster, 비밀번호 1234로 원격에서도 접속권한이 있는 사용자 생성
      10라인 : 권한 적용
      14라인 : 새로 생성한 계정으로 접속

    3. Mysql 환경설정 - 다국어 개발환경(utf-8)일 경우
    • my.ini 설정
    • C:\Windows 밑에 my.ini 파일 생성
      1. [mysql]  
      2. default-character-set = utf8 
      3.  
      4. [mysqld]  
      5. character-set-client-handshake=FALSE 
      6. init_connect="SET collation_connection = utf8_general_ci" 
      7. init_connect="SET NAMES utf8" 
      8. default-character-set = utf8 
      9. character-set-server = utf8 
      10. collation-server = utf8_general_ci 
      11.  
      12. [client]  
      13. default-character-set = utf8 
      14.  
      15. [mysqldump]  
      16. default-character-set = utf8 
    • Mysql Restart
    • root 계정으로 mysql 접속후
    • mysql>status
    • 아래와 같이 나오면 설정 OK.
      1. mysql> status  
      2. --------------  
      3. mysql  Ver 14.12 Distrib 5.0.37, for Win32 (ia32)  
      4.  
      5. Connection id:          1  
      6. Current database:  
      7. Current user:           root@localhost  
      8. SSL:                    Not in use  
      9. Using delimiter:        ;  
      10. Server version:         5.0.37-community MySQL Community Edition (GPL)  
      11. Protocol version:       10  
      12. Connection:             localhost via TCP/IP  
      13. Server characterset:    utf8  
      14. Db     characterset:    utf8  
      15. Client characterset:    utf8  
      16. Conn.  characterset:    utf8  
      17. TCP port:               3306  
      18. Uptime:                 10 sec  
      19.  
      20. Threads: 1  Questions: 4  Slow queries: 0  Opens: 12  Flush tables: 1  Open tabl  
      21. es: 6  Queries per second avg: 0.400  
      22. --------------  
      23.  
      24. mysql> 
    • root 계정 초기 비밀번호 지정하기
      1. C:>mysql -uroot mysql  
      2.  
      3. mysql>update user set password=password('새비밀번호') where user='root';  
      4. mysql>flush privileges;  
      5. mysql>exit  
      6.  
      7. C:>mysql -uroot -p새비밀번호  
    • 데이터베이스 생성 및 사용자 생성
      1. C:>mysql -uroot -p비밀번호  
      2.  
      3. mysql>CREATE DATABASE myproject_utf8 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;  
      4.  
      5. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'localhost' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      6.  
      7. mysql>GRANT ALL PRIVILEGES ON *.* TO 'javamaster'@'%' IDENTIFIED BY '1234' WITH GRANT OPTION;  
      8.  
      9. mysql>FLUSH PRIVILEGES;  
      10.  
      11. mysql>exit  
      12.  
      13. C:>mysql -ujavamaster -p1234 myproject_utf8  
      14.  
      15. mysql>set names euckr;  
      16.    
      4라인 : euckr 환경으로 myproject_utf8 이라는 데이터베이스 생성
      6라인 : 아이디 javamaster, 비밀번호 1234로 로컬에서만 접속권한이 있는 사용자 생성
      8라인 : 아이디 javamaster, 비밀번호 1234로 원격에서도 접속권한이 있는 사용자 생성
      10라인 : 권한 적용
      14라인 : 새로 생성한 계정으로 접속
      16라인 : utf8 환경일 경우 도스프롬프트에서 테이블에 한글문자를 등록(insert)하거나 한글을 조회(select)시 깨져보임. set names euckr; 로 설정을 바꾸면 한글이 제대로 보임(Mysql 5일 경우)


    'Database > MySQL' 카테고리의 다른 글

    MySQL - JDBC Source  (0) 2007/10/29
    Connecting to a MySQL Database using Connector/J JDBC Driver  (0) 2007/10/29
    MySQL - create table examples  (0) 2007/10/29
    MySQL - JDBC  (0) 2007/10/29
    MySQL5  (0) 2007/08/13
    MySQL for JDBC Driver  (0) 2007/06/13
    Posted by 1010