DispatcherServlet 동작 방식

dispatcherservlet


DispatcherServlet이란?
프론트컨트롤러(=Front Controller)이다. 자바 서버의 Servlet 컨테이너에서 HTTP 프로토콜을 통해 들어오는 
모든 요청을 프리젠테이션 계층의 제일앞에 둬서 중앙집중식으로 처리할 수 있는 컨트롤러이다.

DispatcherServlet은 서블릿 컨테이너가 생성하고 관리하는 오브젝트이다.
즉, 스프링이 관여하는 오브젝트가 아니므로 직접 DI를 해줄 방법이 없다. 대신 web.xml에서 설정한 웹어플리케이션 컨텍스를 참고하여 필요한 전략을 DI하여 사용할 수 있다. <참고:기본7가지 DI전략> 



Spring MVC의 기본 핵심인 DispatcherServlet에 대해 알아보자~!

1. DispatcherServlet의 HTTP 요청접수
▶ 자바 서버의 서블릿 컨테이너의 요청을 받는다.

2~3. DispatcherServlet은 URL이나 parameter정보, HTTP 명령등을 참고하여 Handler Mapping에게 어떤 컨트롤러에게 작업을 위임할지 결정받는다.

4~5. DispatcherServlet이 모든 웹 요청 정보가 담긴 HttpServletRequest 타입의 오브젝트를 핸들러어댑터에게 전달하면 핸들러어댑터는 컨트롤러의 메소드가 받을 수 있는 파라미터로 적절히 변환하여 해당 컨트롤러에게 전달해준다.

6. 컨트롤러가 작업을 하고 최종적으로 DispatcherServlet에게 돌려줄 정보는 모델과 뷰이다.

▶ 컨트롤러의 작업 순서
   6.1 사용자 요청 분석.
   6.2 실제 비즈니스로직 수행을 위해 서비스 계층 오브젝트에게 작업 위임.
   6.3 결과를 받아 모델 생성.
   6.4 어떤 뷰를 사용할지 결정.


7~8. 컨트롤러가 직접 뷰오브젝트를 리턴할 수도 있지만, 보통은 뷰의 논리적인 이름을 리턴해주면 DispatcherServlet의 전략인 뷰 리졸버가 이를 이용해 뷰 오브젝트를 생성해준다.

9~10. 뷰 리졸버를 통해 받은 뷰(=뷰오브젝트)에게 모델을 전달해주고 클라이언트에게 돌려줄 최종 결과물을 생성해달라고 요청한다.

11. 뷰작업을 통해 전달받은 최종 결과물은 HttpServletResponse 오브젝트에 담아 넘긴다.

<출처: 토비의 스프링3 p1021~p1026>

저작자 표시 비영리 변경 금지





viralpatel.net 이라는 사이트에서 스프링3 MVC 에 대한 간략한 소개 및 예제를 보게 되었다.
이해하기 쉽도록 설명하고 있고, 예제 또한 따라하기 쉽고 잘 설명되어 있어서 나의 발번역으로 
좀 더 보기 쉽게 ( 나중에 또 볼때 더 빨리 볼 수 있게 ) 블로그에 남겨 놓고자 한다.
혹시라도 이 글을 보시는 분들께서는 영어 번역이 허접이라 많이 미숙하다는 점을 알고 봐주시면 감사하겠습니다.ㅋ

Spring3 MVC Framework 소개
Spring MVC는 Spring framework의 웹 컴포넌트 이다. 이것은 풍부한 기능을 제공하고, 견고한 웹 어플리케이션을 만드는데 도움을 준다.
Spring MVC framework 는 모든 조각의 로직과 기능들이 highly configurable 하도록 디자인되고 설계되었다.
그리고 스프링은 Struts, Webwork, Java Server Faces, Tapestry 와 같은 다른 유명한 웹 프레임 워크와도 효과적으로 통합된다.
이말은 이중 하나의 프레임워크를 이용하기 위해 Spring 프레임워크를 사용할 수 도 있다는 말이다.
또한 스프링은 클라이언트에게 렌더링 하여 화면을 보여주는 데 있어서 Servlet 또는 JSP 와 결합이 강하지 않다.
다른 View 기술들인 Velocity, Freemaker, Excel 또는 Pdf 까지 통합하는것이 가능하다.

요청 처리의 생명주기 ( Request Processing LifeCycle )
 


이미지 출처 : Springsrouce

Spring web MVC framework 는 다른 웹 프레임워크 들과 마찬가지로 요청들에 대해서 컨트롤러 들로 분배하고, 프레임워크의 여러가지 기능을 제공하는 servlet 중심으로 설계되어 있다. 이게 바로 Spring에서 DispatcherServlet 인데 완전하게 Spring IoC container 와 통합되어 있으며, Spring의 다른 모든 기능들을 사용 할 수 있도록 해준다.

Spring 3.0 MVC 의 요청 처리 절차는 아래와 같다.

1. 클라이언트가 http request 형태로 web container 에 요청을 보낸다.

2. 들어오는 request 는 Front Controller (DispatcherServlet) 에 의해서 가로채 지고, 적절한 HandlerMapping을 찾는다.

3. Handler Mapping의 도움으로, DispatcherServlet 은 들어온 요청을 적절한 Controller 로 보낸다.

4. Controller 는 들어온 요청을 처리하고(비즈니스로직수행) 그 결과로 Model과 View 를 ModelAndView 라는 객체의 인스턴스 형태로 Front Controller 에 다시 전달한다.

5. Front Controller 는 ViewResolver 오브젝트를 이용하여 이 view 를 분석한다 (이것은 JSP일 수도 있고, velocity, freemaker 등이 될 수 있다).

6. 선택된 view 는 변환되어 다시 클라이언트에게 보여진다.

Spring 3.0 의 기능들

- Spring 3.0 framework 는 Java 5.0 을 지원 : annotation 기반의 설정을 지원한다. Java 5의 기능인 generics, annotations, vargs 등을 스프링에서 사용 가능하다.

- 새로운 expression language 인 Spring Expression Language SpEL 이 소개되었다.

- REST web service 를 지원한다.

- 데이터 변환이 매우 쉽다. 어노테이션 기반의 데이터 포멧팅을 지원한다. @DateTimeFormat(iso=ISO.DATE)와 @NumberFormat(style=STYLE.CURRENCY) 어노테이션을 이용하여 날짜형식 및 통화형식으로 형변환이 가능하다.

- JPA 2.0 지원이 시작됐다.

Spring 3.0 설정하기

Spring 3.0 MVC 의 시작점은 바로 DispatcherServlet 이다. DispatcherServlet은 HttpServlet 을 부모클래스로하여 구현하고 있는 일반적인 servlet 클래스 이다. 따라서 web.xml 에 설정을 해줄 필요가 있다.

  1. <web-app>     
  2.       
  3. <servlet>           
  4.   <servlet-name>example</servlet-name>           
  5.   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>           
  6.   <load-on-startup>1</load-on-startup>       
  7. </servlet>   
  8.        
  9. <servlet-mapping>           
  10.   <servlet-name>example</servlet-name>           
  11.   <url-pattern>*.html</url-pattern>       
  12. </servlet-mapping>   
  13.   
  14. </web-app>   

위 코드를 통해 web.xml 에 DispatcherServlet 을 설정해 주었다.
예제의 DispatcherServlet 에서 *.html 의 url패턴의 요청에 대해 매핑을 해준것을 기억 하길 바란다.
따라서 *.html 형태의 요청이 들어왔을 때, Spring 3.0 MVC 의 Front Controller 가 호출 될 것이다.



DispatcherServlet 이 초기화 되고 나면, [서블릿이름]-servlet.xml 이란 파일을 웹 어플리케이션의 WEB-INF 폴더 하위에서 찾게 된다.
위의 예에서는 프레임워크가 example-servlet.xml 이란 파일을 찾을 것이다.

다음 글 [Spring3 MVC (2)] Spring 3.0 MVC 로 Hello world 프로젝트 만들기 에서 실제로 동작하는
Hello world 어플리케이션을 만들어 보도록 한다. 


출처 -  http://choija.com/212 








Spring MVC 기초

Java 2007/04/25 05:38
Spring MVC를 이용하여 MVC 기반의 웹 어플리케이션을 개발하는 방법을 살펴본다.

Spring MVC

비록, Spring이 스트러츠를 비롯하여 다양한 웹 프레임워크와 비교적 잘 연동되는 편이긴 하지만, 서로 다른 두 프레임워크를 연동하기 위해서는 설정의 중복 등 개발 과정에서 불편함이 존재한다. 서로 잘 맞지 않는 단추를 억지로 끼워 맞추는 것 같다.

Spring 자체적으로 제공하는 MVC 프레임워크를 사용하면, Spring이 제공하는 AOP, 트랜잭션 처리, DI 등의 기능을 그대로 사용하면서 MVC 패턴에 기반하여 웹 어플리케이션을 개발할 수 있다. 또한, 스트러츠와 Spring을 연동하기 위해 설정의 중복과 같은 개발 과정상의 불편을 해소할 수도 있다.

본 글에서는 Spring MVC의 구성에 대해서 살펴보고, 실제로 Spring MVC를 사용하여 웹 어플리케이션을 개발하는 기본적인 방법을 살펴보도록 하겠다. 그외 Spring MVC를 이용한 웹 어플리케이션에 대해 좀더 자세한 내용이 알고 싶다면 http://www.springframework.org/documentation 사이트를 참고하기 바란다.

Spring MVC의 구성 및 실행 흐름

다른 MVC 기반의 프레임워크와 마찬가지로 Spring MVC도 컨트롤러를 사용하여 클라이언트의 요청을 처리하게 된다. 이 컨트롤러의 역할을 하는 것이 DispatcherServlet인데, DispatcherServlet을 비롯하여 Spring MVC의 주요 구성 요소는 표 1과 같다.

구성 요소 설명
DispatcherServlet 클라이언트의 요청을 전달받는다. Controller에게 클라이언트의 요청을 전달하고, Controller가 리턴한 결과값을 View에 전달하여 알맞은 응답을 생성하도록 한다.
HandlerMapping 클라이언트의 요청 URL을 어떤 Controller가 처리할지를 결정한다.
Controller 클라이언트의 요청을 처리한 뒤, 그 결과를 DispatcherServlet에 알려준다. 스트러츠의 Action과 동일한 역할을 수행한다.
ViewResolver Commander의 처리 결과를 보여줄 View를 결정한다.
View Commander의 처리 결과를 보여줄 응답을 생성한다.

이들 주요 구성 요소간의 메시지 흐름은 그림 1과 같다.


각 흐름을 좀더 자세하게 설명하면 다음과 같다.

  1. 클라이언트의 요청이 DispatcherServlet에 전달된다.
  2. DispatcherServlet은 HandlerMapping을 사용하여 클라이언트의 요청이 전달될 Controller 객체를 구한다.
  3. DispatcherServlet은 Controller 객체의 handleRequest() 메소드를 호출하여 클라이언트의 요청을 처리한다.
  4. Controller.handleRequest() 메소드는 처리 결과 정보를 담은 ModelAndView 객체를 리턴한다.
  5. DispatcherServlet은 ViewResolver로부터 처리 결과를 보여줄 View를 구한다
  6. View는 클라이언트에 전송할 응답을 생성한다.
여기서 개발자가 직접 개발해주어야 하는 부분은 클라이언트의 요청을 처리할 Commander 클래스와 클라이언트에 응답 결과 화면을 전송할 JSP나 Velocity 템플릿 등의 View 코드이다. 나머지, DispatcherServlet이나 HandlerMapping, ViewResolver 등은 Spring이 제공하는 기본 구현체를 사용하면 된다.

Spring MVC를 이용한 웹 어플리케이션 개발

Spring MVC를 이용하여 웹 어플리케이션을 개발하는 과정은 다음과 같다.

  1. 클라이언트의 요청을 받을 DispatcherServlet을 web.xml 파일에 설정한다.
  2. 요청 URL과 Controller의 매핑 방식을 설정한다.
  3. Controller의 처리 결과를 어떤 View로 보여줄 지의 여부를 결정하는 ViewResolver를 설정한다.
  4. Controller를 작성한다.
  5. 뷰 영역의 코드를 작성한다.
DispatcherServlet 설정 및 Spring 콘텍스트 설정

Spring MVC를 사용하기 위해서는 가장 먼저 web.xml 파일에 DispatcherServlet 설정을 추가해주어야 한다. DispatcherServlet은 서블릿 클래스로서 다음과 같이 서블릿 설정 및 서블릿 매핑 설정을 web.xml 파일에 추가해주면 된다.

<?xml version="1.0" ?>

<web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <servlet>
        <servlet-name>example</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>example</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

위 설정은 모든 *.do로 들어오는 요청을 DispatcherServlet이 처리하도록 하고 있다.

DispatcherServlet은 설정 파일에서 지정한 서블릿 이름을 사용하여 Spring 설정 파일을 로딩한다. 예를 들어, 위 코드의 경우 DispatcherServlet의 이름이 "example"인데, 이 경우 사용되는 Spring 설정 파일은 'WEB-INF/example-servlet.xml' 이다.

만약 기본적으로 사용되는 Spring 콘텍스트 설정 파일이 아닌 다른 이름의 파일을 사용하고 싶다면 다음과 같이 contextConfigLocation 초기화 파라미터에 사용할 설정 파일의 목록을 지정해주면 된다.

    <servlet>
        <servlet-name>multipleConfig</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/mvc1.xml,
                /WEB-INF/mvc2.xml
            </param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>

이때 각 설정 파일은 콤마를 사용하여 구분한다. 만약 각 설정 파일에서 동일한 이름의 빈을 지정했다면 더 뒤에 위치한 설정 파일에 명시된 빈이 우선순위를 갖는다.

HandlerMapping 설정

클라이언트의 요청을 Spring의 DispatcherServlet이 처리하도록 설정했다면, 다음으로 해야 할 작업은 어떤 HandlerMapping을 사용할지의 여부를 지정하는 것이다. HandlerMapping은 클라이언트의 요청을 어떤 Commender가 수행할 지의 여부를 결정해주는데, 표 2와 같이 두 개의 구현체가 주로 사용된다.

구현체 설명
BeanNameUrlHandlerMapping 요청 URI와 동일한 이름을 가진 Controller 빈을 매핑한다.
SimpleUrlHandlerMapping Ant 스타일의 경로 매핑 방식을 사용하여 URI와 Controller 빈을 매핑한다.

BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping은 요청 URI와 동일한 이름을 갖는 Controller 빈으로 하여금 클라이언트의 요청을 처리하도록 한다. 예를 들어, http://some.com/hello.do 와 같은 요청 URL에 대해 "/hello.do" 라는 이름을 가진 Controller 빈이 해당 요청을 처리하도록 한다. 아래 코드는 BeanNameUrlHandlerMapping를 사용하는 경우의 Spring 설정 파일의 예를 보여주고 있다.

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="beanNameUrlMapping" 
        class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    
    <bean name="/hello.do"
        class="net.daum.ts.techReport.report2.springmvc.HelloController" />
    
    <bean name="/login.do"
        class="net.daum.ts.techReport.report2.springmvc.LoginController" />
    
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/view/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

만약 요청 URL이 http://some.com/login.do 였다면, 이름이 "/login.do"인 LoginController가 해당 요청을 처리하게 된다. 비슷한 이유로, http://some.com/hello.do 요청은 HelloController가 처리하게 된다.

HandlerMapping 빈을 따로 생성하지 않은 경우 DispatcherServlet은 기본적으로 HandlerMapping의 구현체로 BeanNameUrlHandlerMapping을 사용한다. 따라서, 위 코드에서 HandlerMapping과 관련된 설정은 생략할 수 있다.

SimpleUrlHandlerMapping

SimpleUrlHandlerMapping은 Ant 스타일의 매핑 방식을 사용하여 Controller를 매칭한다. 아래 코드는 SimpelUrlHandlerMapping을 사용하여 클라이언트의 요청 URL과 매핑될 Controller를 지정하는 예를 보여주고 있다.

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="beanNameUrlMapping" 
        class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>
                /content/*.html=contentController
                /**/help.html=helpController
            </value>
        </property>
    </bean>
    
    <bean name="contentController" 
        class="net.daum.ts.techReport.report2.springmvc.ContentController" />
    
    <bean name="helpController" 
        class="net.daum.ts.techReport.report2.springmvc.HelpController" />
    
    <bean id="viewResolver" 
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/view/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

SimpleUrlHandlerMapping은 mappings 프로퍼티를 통해서 매핑 설정 목록을 입력받는다. 이때 설정의 이름은 Ant 스타일의 URL 경로를 의미하며, 값은 매핑될 Controller 빈의 이름을 의미한다. 예를 들어, 위 예제의 경우 http://some.com/content/1.html 이나 http://some.com/content/mvc.html 요청이 들어오면 contentController가 처리하도록 매핑하였다.

SimpleUrlHandlerMapping에서 사용되는 규칙은 다음과 같다.

  • ? - 한 글자에 매칭된다.
  • * - 0 개 이상의 글자에 매칭된다.
  • ** - 0개 이상의 디렉토리에 매칭된다.
아래는 org.springframework.util.AntPathMatcher API 문서에 예시로 나온 몇 가지 매핑 설정의 예이다.

  • com/t?st.jsp - com/test.jsp, com/tast.jsp 또는 com/txst.jsp 등에 매칭된다.
  • com/*.jsp - com 디렉토리에 있는 모든 .jsp에 매칭된다.
  • com/**/test.jsp - com 경로 아래에 위치한 모든 test.jsp와 매칭된다.
  • org/springframework/**/*.jsp - org/springframework 경로 아래에 위치한 모든 jsp와 매칭된다.
두 HandlerMapping 구현체의 공통 프로퍼티

앞서 살펴본 두 HandlerMapping 구현 클래스는 AbstractUrlHandlerMapping 클래스를 상속받고 있으며, AbstractUrlHandlerMapping 클래스는 AbstractHandlerMapping 클래스를 상속받고 있다. 이 두 클래스는 공통으로 사용되는 몇 가지 프로퍼티를 제공하고 있는데, 먼저 AbstractHandlerMapping 클래스는 다음과 같은 프로퍼티를 제공하고 있다.

프로퍼티 설명
interceptors 사용할 인터셉터의 목록
defaultHandler 매칭되는 핸들러를 찾지 못할 때 사용할 기본 핸들러
order 하나의 서블릿 콘텍스트에서 다수의 HandlerMapping을 사용할 수 있는데, 이 경우 HandlerMapping 간의 우선순위를 지정한다.

AbstractUrlHandlerMapping 클래스가 제공하는 프로퍼티 목록은 표 4와 같다.

프로퍼티 설명
alwaysUseFullPath 웹 어플리케이션 콘텍스트의 경로를 포함할 지의 여부를 결정한다. 예를 들어, 콘텍스트 경로가 /main 일때, http://some.com/main/a.html 에 대해, 이 값이 true면 /main/a.html 이 사용된다. 이 값이 false이면 /a.html이 사용된다. 기본값은 false이다.
urlDecode HandlerMapping에 요청 URL을 전달하기 전에 특정 인코딩을 사용해서 디코딩할지의 여부를 결정한다. 이 값을 true로 지정하면 요청에 명시된 인코딩이나 ISO-8859-1을 사용하여 디코딩한다. 기본값은 false이다.
lazyInitHandlers 싱글톤 타입의 핸들러에 대해 lazy initialization을 적용할지의 여부를 지정한다. 기본 값은 false이다.

ViewResolver 설정

HandlerMapping을 설정했다면, 이제 ViewResolver를 설정할 차례이다. ViewResolver는 Controller의 실행 결과를 어떤 뷰를 보여줄지의 여부를 결정하는 기능을 제공한다. 주로 사용되는 ViewResolver에는 다음의 두 가지가 존재한다.

  • InternalResourceViewResolver - JSP를 사용하여 뷰를 생성한다.
  • VelocityViewResolver - Velocity 템플릿 엔진을 사용하여 뷰를 생성한다.
InternalResourceViewResolver

InternalResourceViewResolver는 JSP를 사용하여 Commander의 결과 뷰를 생성할 때 사용된다. 아래 코드는 InternalResourceViewResolver의 설정 예를 보여주고 있다.

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="beanNameUrlMapping"
        class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    
    <bean name="/hello.do"
        class="net.daum.ts.techReport.report2.springmvc.HelloController" />

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/view/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

InternalResourceViewResolver는 다음과 같이 두 개의 프로퍼티를 입력받는다.

  • prefix - Controller가 리턴한 뷰 이름 앞에 붙을 접두어
  • suffix - Controller가 리턴한 뷰 이름 뒤에 붙을 확장자
예를 들어, HelloController가 처리 결과를 보여줄 뷰의 이름으로 "hello"를 리턴했다고 하자. 이 경우 InternalResourceViewResolver에 의해 사용되는 뷰는 "/view/hello.jsp"가 된다. 따라서, /view/hello.jsp가 생성한 응답 결과 화면이 클라이언트에 전달된다.

VelocityViewResolver

VelocityViewResolver는 Velocity 템플릿을 사용하여 뷰를 생성할 때 사용된다. 아래 코드는 VelocityViewResolver의 설정 예를 보여주고 있다.

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean name="/hello.mul"
        class="net.daum.ts.techReport.report2.springmvc.HelloController" />
    
    <bean id="velocityConfig" 
        class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <property name="resourceLoaderPath" value="/view_vm/" />
        <property name="velocityProperties">
            <value>
                input.encoding=UTF-8
                output.encoding=UTF-8
            </value>
        </property>
    </bean>
    
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
        <property name="contentType" value="text/html; charset=UTF-8" />
        <property name="cache" value="true" />
        <property name="prefix" value="" />
        <property name="suffix" value=".vm" />
    </bean>
</beans>

VelocityViewResolver를 사용하기 위해서는 먼저 Velocity의 설정 정보를 관리해주는 VelocityConfigurer를 생성해주어야 한다. VelocityConfigurer에서 사용가능한 주요 프로퍼티는 다음과 같다.

  • resourceLoaderPath - Velocity 템플릿이 위치하는 경로 (웹 콘텍스트 루트 기준)
  • velocityProperties - Velocity 설정 프로퍼티
예를 들어, 위 코드의 경우는 웹 어플리케이션 루트에 위치한 /view_vm/ 디렉토리에 Velocity 템플릿 파일이 위치한다는 것을 의미한다.

VelocityViewResolver를 설정할 때에는 다음의 프로퍼티를 알맞게 지정해준다.

  • contentType - 응답의 컨텐트 타입을 지정한다.
  • prefix - InternalResourceViewResolver와 동일
  • suffix - InternalResourceViewResolver와 동일
위 설정의 경우, Controller가 선택한 뷰가 "hello"라고 한다면 /view_vm/hello.vm 파일이 템플릿으로 선택된다. 또한 생성되는 뷰의 컨텐트 타입은 "text/html" 이고 캐릭터셋은 UTF-8이 된다.

Controller 구현

지금까지는 환경 설정과 관련된 부분에 대해서 설명했는데, 이제 본격적으로 클라이언트의 요청을 처리하는 데 사용되는 Controller의 구현에 대해서 살펴보도록 하자. Controller를 구현하는 가장 간단한 방법은 Controller 인터페이스를 implements 하는 것이다. 하지만, Controller 인터페이스를 직접적 implements 하기 보다는, Controller 인터페이스를 implements 하고 몇 가지 추가적인 기능을 구현하고 있는 클래스들을 상속받아 Controller를 구현하는 것이 일반적이다.

Controller 인터페이스

org.springframework.web.servlet.mvc.Controller 인터페이스는 다음과 같이 정의되어 있다.

public interface Controller {
    
    ModelAndView handleRequest(HttpServletRequest request,
                               HttpServletResponse response)
                               throws Exception

}

Controller를 구현하는 가장 단순한 방법은 Controller 인터페이스를 구현한 뒤, handleRequest() 메소드를 알맞게 구현하는 것이다. 아래 코드는 구현 예이다.

package net.daum.ts.techReport.report2.springmvc;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class SimpleController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        // request를 사용하여 클라이언트 요청 분석 후
        // 클라이언트 요청에 따른 알맞은 처리
        ModelAndView mav = new ModelAndView("hello");
        mav.addObject("greeting", "안녕하세요");
        return mav;
    }

}

handleRequest() 메소드는 파라미터로 전달받은 request 객체와 response 객체를 사용하여 클라이언트가 요청한 기능을 알맞게 구현하면 된다.

handleRequest() 메소드는 ModelAndView 객체를 리턴한다. ModelAndView 객체는 클라이언트의 요청을 처리한 결과를 보여줄 뷰 페이지와 관련해서 다음과 같은 정보를 제공한다.

  • 결과를 보여줄 뷰 이름 지정 : 위 코드의 경우 "hello"를 뷰로 선택
  • 뷰에서 사용할 모델 데이터 : 위 코드의 경우 addObject() 메소드로 추가한 객체, 뷰에서는 "greeting" 이라는 이름을 사용하여 데이터에 접근할 수 있다.
예를 들어, ViewResolver를 다음과 같이 설정했다고 해 보자.

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/view/" />
        <property name="suffix" value=".jsp" />
    </bean>

위 viewResolver는 "/view/" + 뷰이름 + ".jsp"를 뷰로 사용하게 된다. SimpleController는 뷰 이름으로 "hello"를 리턴하므로, "/view/hello.jsp"가 뷰로 사용된다. 또한, 이 뷰에서는 다음과 같이 ModelAndView에 저장된 객체를 사용할 수 있게 된다.

<%@ page contentType="text/html; charset=UTF-8" %>

<strong>${greeting}</strong> ..

AbstractController를 이용한 구현

Controller를 구현할 때에는 Controller 인터페이스를 직접 구현하기보다는, AbstractController 추상 클래스를 상속 받아 구현하는 경우가 더 많다. AbstractController 클래스는 Controller 인터페이스를 implements 하여 추가적인 기능을 제공하고 있다.

AbstractController 클래스는 표 5와 같은 프로퍼티를 제공하고 있으며, 이 프로퍼티를 사용하여 HTTP Session, 캐시 등의 설정을 제어할 수 있다.

프로퍼티 설명
supportedMethods Controller가 처리할 수 있는 메소드를 지정한다. GET과 POST 등 처리할 수 있는 메소드를 지정할 수 있다. 만약 지원되지 않는 메소드를 사용하여 요청이 들어오면 ServletException 예외를 발생시킨다.
requiresSession Controller가 HTTP Session을 필요로 하는 지의 여부를 지정한다. 이 값이 true인 경우, 클라이언트와 관련된 세션이 존재하지 않으면 ServletException 예외를 발생시킨다.
synchronizeSession HTTP Session을 사용하여 Controller에 대한 처리를 동기화 할지의 여부를 지정한다.
cacheSeconds HTTP 응답에 캐시와 관련된 디렉티브를 생성할지의 여부를 지정한다. 기본 값은 -1이며, 이 경우 캐시 디렉티브가 응답 결과에 포함되지 않는다.
useExpiresHeader HTTP 1.0에 호환되는 "Expires" 헤더의 사용 여부를 지정한다. 기본값은 true 이다.
useCacheHeader HTTP 1.1에 호환되는 "Cache-Control' 헤더의 사용 여부를 지정한다. 기본값은 true 이다.

AbstractController 클래스를 상속받아 Controller를 구현하는 클래스는 아래 코드와 같이 handleRequestInternal() 메소드를 구현해주면 된다.

package net.daum.ts.techReport.report2.springmvc;

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

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class HelloController extends AbstractController {

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ModelAndView view = new ModelAndView("hello");
        view.addObject("greeting", "안녕하세요");
        return view;
    }

}

AbstractController 클래스의 handleRequest() 메소드는 내부적으로 필요한 작업을 수행한 뒤, handleRequestInternal() 메소드를 호출한다. 따라서, AbstractController 클래스를 상속받는 경우에는 handleRequest() 메소드가 아닌 handlerRequestInternal() 메소드를 구현해주어야 한다.

AbstractCommandController를 이용한 파라미터 값 전달

요청 파라미터의 값을 특정한 객체에 저장하고 싶다면 AbstractCommandController 클래스를 사용하면 된다. AbstractCommandController 클래스를 상속받은 클래스는 다음과 같이 handle() 메소드를 알맞게 구현해주면 된다.

package net.daum.ts.techReport.report2.springmvc;

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

import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractCommandController;

public class LoginController extends AbstractCommandController {

    @Override
    protected ModelAndView handle(HttpServletRequest request,
            HttpServletResponse response, Object command, BindException errors)
            throws Exception {
        LoginCommand loginCommand = (LoginCommand) command;
        authService.authenticate(loginCommand.getId(), loginCommand.getPassword());
        return new ModelAndView("loginSuccess", "loginCommand", loginCommand);
    }

}

handle() 메소드의 세 번째 파라미터는 파라미터의 값을 저장한 command 객체이다. 이 command 객체의 타입을 지정하기 위해서는 다음과 같이 Spring 설정 파일에서 commandClass 프로퍼티의 값으로 클래스의 이름을 지정해주면 된다.

    <bean name="/login.do"
        class="net.daum.ts.techReport.report2.springmvc.LoginController">
        <property name="commandClass" 
            value="net.daum.ts.techReport.report2.springmvc.LoginCommand" />
    </bean>

handle() 메소드에 전달되는 command 파라미터는 commandClass 프로퍼티에 지정한 타입의 객체가 된다. 이때 파라미터와 생성할 command 객체의 프로퍼티 사이의 매핑은 자바빈 스타일을 따른다. 예를 들어, 파라미터 이름이 id 인 경우 setId() 메소드를 통해 파라미터 값이 전달되며, 파라미터 이름이 address 인 경우 setAddress() 메소드를 통해 파라미터 값이 전달된다.

SimpleFormController를 이용한 폼 전송 처리

SimpleFormController는 AbstractCommandController와 마찬가지로 파라미터의 값을 객체에 저장할 때 사용된다. 차이점이 있다면, SimpleFormController는 폼 전송 개념이 적용되어 있다는 것이다. SimpleFormController는 POST 방식으로 요청이 들어올 경우 doSubmitAction() 메소드를 통해 요청을 처리하는 반면에, GET 방식으로 요청이 들어오면 "formView" 프로퍼티로 지정한 뷰를 출력한다.

SimpleFormController는 클라이언트가 POST로 전송한 데이터는 commandClass 프로퍼티로 지정한 타입의 객체에 저장되어 doSubmitAction() 메소드에 전달된다. 아래 코드는 SimpleFormController의 구현 예를 보여주고 있다.

package net.daum.ts.techReport.report2.springmvc;

import org.springframework.web.servlet.mvc.SimpleFormController;

public class LoginFormController extends SimpleFormController {

    @Override
    protected void doSubmitAction(Object command) throws Exception {
        LoginCommand loginCommand = (LoginCommand) command;
        if (!loginCommand.getId().equals("madvirus")) {
            throw new LoginFailException("invalid id: "+loginCommand.getId());
        }
    }

}

클라이언트가 요청한 작업을 성공적으로 수행하지 못한 경우, doSubmitAction() 메소드는 예외를 발생시켜서 올바르게 처리하지 못했음을 알리게 된다. 이 경우, 예외 타입에 따라 알맞게 예외 처리를 해 주어야 한다.

GET 방식으로 데이터를 전송하는 경우에는 doSubmitAction() 메소드가 호출되지 않는다. 대신, formView 프로퍼티로 지정한 뷰를 보여준다. 아래 코드는 SimpleFormController 클래스를 상속받은 클래스의 구현 예를 보여주고 있다.

    <bean name="/loginUsingForm.do"
        class="net.daum.ts.techReport.report2.springmvc.LoginFormController">
        <property name="commandClass" 
            value="net.daum.ts.techReport.report2.springmvc.LoginCommand" />
        <property name="formView" value="loginForm" />
        <property name="successView" value="loginSuccess" />
    </bean>

뷰 영역에 데이터를 전달하고 싶다면 다음과 같이 onSubmit() 메소드를 추가적으로 구현해주면 된다.

package net.daum.ts.techReport.report2.springmvc;

import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

public class LoginFormController extends SimpleFormController {

    @Override
    protected void doSubmitAction(Object command) throws Exception {
        LoginCommand loginCommand = (LoginCommand) command;
        if (!loginCommand.getId().equals("madvirus")) {
            throw new LoginFailException("invalid id: "+loginCommand.getId());
        }
    }
    
    @Override
    protected ModelAndView onSubmit(Object command, BindException errors) 
    throws Exception {
        try {
            ModelAndView mav = super.onSubmit(command, errors);
            mav.addObject("loginCommand", command);
            return mav;
        } catch(Exception e) {
            throw e;
        }
    }
}

위 코드에서 super.doSubmit() 메소드를 호출하면 doSubmitAction() 메소드가 호출된다. 따라서, doSubmitAction() 메소드가 성공적으로 수행되면 ModelAndView.addObject() 메소드를 사용하여 뷰에 데이터를 전달할 수 있게 된다.

뷰의 구현

뷰에서는 ModelAndView 객체에 저장한 객체를 사용할 수 있다. 예를 들어, Controller에서 다음과 같이 ModelAndView에 데이터를 저장했다고 해 보자.

    protected ModelAndView handle(HttpServletRequest request,
            HttpServletResponse response, Object command, BindException errors)
            throws Exception {
        LoginCommand loginCommand = (LoginCommand) command;
        ModelAndView mav = new ModelAndView("loginSuccess");
        mav.addObject("loginCommand", loginCommand);
        return mav;
    }

이 경우 "loginSuccess" 뷰에 해당되는 JSP는 다음과 같이 EL(Expression Language)이나 request.getAttribute() 등의 메소드를 사용하여 ModelAndView 객체에 저장된 값을 사용할 수 있다.

로그인 정보: ${loginCommand.id}
<%
    LoginCommand cmd = (LoginCommand) request.getAttribute("loginCommand");
%>

Velocity 템플릿에서도 다음과 같이 Velocity의 템플릿 언어를 사용하여 ModelAndView 객체에 저장된 값을 사용할 수 있다.

<body>
인사말: ${greeting}
</body>


출처 -  http://javacan.tistory.com/entry/130 









Added by kim su young, last edited by 이윤정 on 2월 23, 2006  (view change) 

오픈 소스 스터디

Table of Contents

Spring Introduction

Spring에 대한 전반적인 내용은 아래를 참조하기 바란다.
http://wiki.javajigi.net/pages/viewpage.action?pageId=280
http://martinfowler.com/articles/injection.html

Spring MVC

Spring's web MVC framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and theme resolution as well as support for upload files.
The default handler is a very simple Controller interface, just offering a ModelAndView handleRequest(request,response) method. 
This can already be used for application controllers, but you will prefer the included implementation hierarchy, consisting of, for example AbstractController, AbstractCommandController and SimpleFormController. 
Application controllers will typically be subclasses of those. Note that you can choose an appropriate base class: If you don't have a form, you don't need a FormController. This is a major difference to Struts.

A flexible MVC web application framework, built on core Spring functionality. 
This framework is highly configurable via strategy interfaces, and accommodates multiple view technologies like JSP, Velocity, Tiles, iText, and POI. Note that a Spring middle tier can easily be combined with a web tier based on any other web MVC framework, like Struts, WebWork, or Tapestry.

Spring의 웹 MVC framework는 설정가능한 핸들러 맵핑들, view분석, 로케일과 파일 업로드를 위한 지원같은 테마분석과 함께 핸들러에 요청을 할당하는 DispatcherServlet을 기반으로 디자인되었다.
디폴트 핸들러는 ModelAndView handleRequest(request,response)메소드를 제공하는 매우 간단한 컨트롤러 인터페이스이다. 
이것은 이미 애플리케이션 컨트롤러를 위해 사용될수 있지만 AbstractController, AbstractCommandController그리고 SimpleFormController같은 것들을 포함한 구현 구조를 포함한것을 더 선호할것이다. 
애플리케이션 컨트롤러들은 전형적으로 이것들의 하위 클래스가 된다. 당신이 선호하는 빈 클래스를 선택하도록 하라. 
만약에 당신이 form을 가지지 않는다면 당신은 FormController가 필요없다. 이것이 Struts와 가장 큰 차이점이다.

유연한 MVC 웹 어플리케이션 프레임웍은 Core Spring과 상호작용하여 만들어진다.
이 프레임웍은 전략적인 인터페이스를 거쳐 높은수준으로 만들어진다. 그리고, JSP, Velocity, Tiles, iText, POI처럼 다양한 view 기술을 수용한다.
Spring middle tier는 Struts, WebWork, Tapestry와 같은 다른 웹 MVC 프레임워크를 기반으로 한 웹티어와 결합할수 있다.
Spring프레임웍에서 Model-View-Controller를 지원하는 기능이다.

강좌가 올라와있네요...
Spring MVC에서 클라이언트 요청의 처리 과정을 먼저 살펴보시면 이해가 되실거 같습니다.
http://wiki.javajigi.net/pages/viewpage.action?pageId=1133

참 고 : http://www.springframework.org/docs/reference/
번역본 : http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/

설정 방법

Spring MVC를 사용하기 위해서는

  • web-app/WEB-INF/lib 아래로 spring.jar 또는 spring-webmvc.jar파일을 복사한다.
  • web-app/WEB-INF/web.xml에 DispatcherServlet를 설정한다.
  • <web.xml>
     
    
    <servlet>
    	<servlet-name>action</servlet-name>
    	<servlet-class>
    		org.springframework.web.servlet.DispatcherServlet</servlet-class>
    	<load-on-startup>1</load-on-startup>
    </servlet>	
    <servlet-mapping>
    	<servlet-name>action</servlet-name>
    	<url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    	<servlet-name>action</servlet-name>
    	<url-pattern>*.html</url-pattern>
    </servlet-mapping>
    

    .do, .html 로 끝나는 모든 요청은 DispatcherServlet에서 담당한다.

  • DispattcherServlet이 초기화 할 때 디폴트로 사용하는 빈 설정파일의 이름은 "서블릿 이름-servlet.xml"이 된다.
  • web-app/WEB-INF/web.xml에서 설정한 <servlet-name>인 action-servlet.xml 파일을 생성한다.

즉 spring.jar 와 web.xml에 DispatcherServlet 을 사용한다는 선언 그리고 서블릿 이름-servlet.xml 을 작성하면 사용할 수 있다.

다양한 예제들

Spring MVC를 사용한 초간단 예제

링크된 강좌를 살펴보고 대략적인 것을 파악한다면 Spring MVC 사용하기 위해서 개발자들이 만들어야 하는 것은 Controller 라는 것을 알게 될 것이다. 물론 화면에 보여주는 jsp(여기선 jsp 를 연결) 파일, 빈 설정 파일을 만드는 것은 당연한 것이다.

Controller를 생성하고 빈 설정파일에 맵핑 하는 방법 등의 관계를 보여주기 위한 데이터를 가져와서 List 형태로 보여주는 예제이다

  • Controller
     
    package com.tmax.spring.web;
    
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    import com.tmax.spring.service.iface.BbsServiceIface;
    
    public class BbsController implements Controller {
    	
    	private BbsServiceIface bbsService;
    	
    	public void setBbsService(BbsServiceIface newBbsService){
    		this.bbsService = newBbsService;
    	}
    	
    	public ModelAndView handleRequest(HttpServletRequest arg0,
    			HttpServletResponse arg1) throws Exception {
    	    
            List list = bbsService.findBbsList();
            
            return new ModelAndView("bbs/list","bbsList", list);
       }
    
    }
    

위에서 보면 직접적으로 Controller 인터페이스를 구현해서 ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; 를 구현한다. Spring MVC에서 제공하는 다양한 Controller 들은 모두 Controller 인터페이스를 구현하기 때문에 가장 기본적인 구조라고 생각할 수 있다.

Service 객체는 setter Injection을 통해 생성된 것이다. 이것은 이미 앞에서 다 이해했을 것이다.
handleRequest(... ) 안에서 실제 메소드를 호출한 뒤 ModelAndView 객체를 통해 리턴을 하였다.

링크된 Spring MVC의 요청에 대한 생명주기를 보게 되면 비지니스 계층에서 전달된 모델 데이타와 클라이언트에게 보여줄 뷰화면에 대한 정보를 ModelAndView 클래스에 담아서 반환을 하게 되어 있다. 즉 전달 데이타 모델, 화면이름, 화면에서 사용될 객체 이름 등을 담아서 ModelAndView객체를 생성, 리턴한다.

ModelAndView 객체의 View정보가 논리적인 view이름(String) 인 경우 빈 설정 파일에 정의되어 있는 ViewResolver클래스를 이용하여 클라이언트에세 출력할 화면이름을 만들어 낼 수 있다. ModelAndView의 첫번째 인자인 "bbs/list"가 view 이름이기 때문에 설정파일의 ViewResolver를 만나서 리턴되는 화면의 전체이름이 완성되는 것이다.

  • action-servlet.xml
      
        <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    	
        <bean name="/bbs/listBbs.do" class="com.tmax.spring.web.BbsController">
    	<property name="bbsService">
    		<ref bean="bbsService" />
    	</property> 
        </bean>
    
        <!-- View Resolver for JSPs -->
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
          		<property name="prefix" value="/jsp/"/>
                      <property name="suffix" value=".jsp"/>
        </bean>
    

설정파일을 보면 ViewResolver로 InternalResourceViewResolver클래스를 사용한다. 
그리고 JSTLView를 사용해서 jsp 로 화면을 출력할 것이라는 것을 알 수 있다. 위에 설정된 것과 ModelAndView객체에 넘긴 view이름을 조합해 보면 /jsp/bbs/list.jsp 가 되는 것이다. (prefix+view 이름+suffix )

설정파일을 보면 BeanNameUrlHandlerMapping 클래스를 사용해서 Controller와 URL을 맵핑해주는 것을 알 수 있다. 
만일 빈 설정파일에 HandlerMapping이 설정되어 있지 않으면 default 로 BeanNameURLHandlerMapping을 설정한다. 
맵핑 방법에는 BeanNameURLHandlerMapping, SimpleUrlHandlerMapping,CommonsPathMapHandlerMapping 등이 있다.

  • BeanNameURLHandlerMapping
    빈 이름과 URL을 mapping 하는 방법이다.
        <bean name="/bbs/listBbs.do" class="com.tmax.spring.web.BbsController">
    	<property name="bbsService">
    		<ref bean="bbsService" />
    	</property> 
        </bean>
     

즉 요청이 ..../bbs/listBbs.do가 오면 BbsController 클래스를 호출하겠다는 의미이다. name에는 <bean name="/bbs/listBbs.do /bbs/listBbs2.do".. /> 로 여러 개를 기술할 수 있다.

  • SimpleUrlHandlerMapping
    매핑에 대한 정보를 각각의 Controller에서 설정하는 것이 아니라 하나의 저장소에서 관리하는 것이다. 
    Controller를 개발하는 개발자들은 빈을 정의하기만 하고 이 Controller가 어떻게 맵핑되어서 사용하는지에 대해서는 몰라도 된다.
    위의 설정파일을 SimpleUrlHandlerMapping으로 바꾸어 보면
       
        <bean id="bbsController" class="com.tmax.spring.web.BbsController">
    	<property name="bbsService">
    		<ref bean="bbsService" />
    	</property> 
        </bean>
        
       <bean id="urlMapping" 
            class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="mappings">
                <props>
                    <prop key ="/bbs/listBbs.do">bbsController</prop>
    	   </props>
            </property>
        </bean>
    
     

실제 Controller에 대한 정의는 일반 빈을 정의 할 때와 다르지 않게 된다. 맵핑에 대한 정보는 별도의 빈(urlMapping)에서 관리되고 있다. 만일 더욱 많은 Controller가 생성되고 맵핑을 해 줘야 할 때 모든 매핑 정보는 한곳에서 관리를 할 수 있게 되는 것이다. Controller 마다 맵핑정보까지 나열할 필요가 없게 된다.

urlMapping 빈에서 Controller와 URL을 맵핑할 때 <props/>를 사용해서 기술할 수 있다. 또 다른 방법은 <props/>를 사용하지 않고 별도의 Properties 파일에서 관리 할 수도 있다.

  • CommonsPathMapHandlerMapping

Spring MVC를 사용하여 다중요청 처리하기

개발자들이 Controller 만 만들면 되지만 개발하면서 페이지, 기능이 추가 될 때마다 새로운 Contoller를 만드는 것은 굉장히 짜증이 나는 작업이다. 게다가 점점 관리해야 하는 파일의 수가 많아져서 빈 설정파일도 점점 복잡해질 것이다.이 같은 상황을 해결하기 위하여 Spring MVC는 MultiActionController를 제공한다. MultiActionController은 하나의 Controller를 구현함으로써 다수의 요청을 처리하는 것이 가능하다.

아래 예는 회원가입,수정을 통해 MultiActionController 구현을 보여준다.

Controller 객체인 MemberController.java

MemberController.java
package net.javajigi.member.web;

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

import net.javajigi.member.model.Member;
import net.javajigi.member.service.MemberService;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

public class MemberController extends MultiActionController{
	
	private MemberService memberService = null;
	public void setMemberService(MemberService memberService){
		this.memberService = memberService;
	}
	
	public ModelAndView add(HttpServletRequest request, HttpServletResponse reponse) throws Exception{
		
		Member command = new Member();
		bind(request,command);
		
                  memberService.addMember(command);
		
		return new ModelAndView("main");		
		
	}
	
	public ModelAndView update(HttpServletRequest request, HttpServletResponse reponse) throws Exception{
		
		Member command = new Member();
                  bind(request,command);
		memberService.updateMember(command);
		
		return new ModelAndView("list");
		
		
	}

}

위의 예제는 add(가입), update(수정)을 구현하기 위해 MultiActionController를 상속받은 Controller 이다. 
각각의 메소드에 로직이나 서비스 호출 부분들의 소스를 구현하다.
add 메소드를 보면 화면에서 입력받은 내용을 bind()를 사용해 domain 객체에 넘기고 있다.(이 내용은 나중에..)
그 뒤 서비스의 addMember()를 호출한다.
처리 후 리턴될 페이지를 설정한다.(add->main.jsp, update->list.jsp)

Controller를 작성 후 webapps/WEB-INF/action-servlet.xml 파일에 빈을 매핑한다.

action-servlet.xml
<bean id="memberService" class="net.javajigi.member.service.MemberService"/>

<bean name="/member/addMember.do /member/updateMember.do" class="net.javajigi.member.web.MemberController">
	<property name="methodNameResolver">
		<ref local="memberControllerMethodNameResolver"/>
	</property>	
	<property name="memberService">
		<ref local="memberService"/>
	</property>			
</bean>	

<bean id="memberControllerMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
	<property name="mappings"> 
		<props>
			<prop key="/member/addMember.do">add</prop>
			<prop key="/member/updateMember.do">update</prop>
		</props>
	</property>
</bean>

MemberController는 두 개의 URL(/member/addMember.do,/member/updateMember.do)에 맵핑이 되었다.각각의 URL이 호출될 때 MemberController 안의 어떤 메소드가 호출될 지는 "memberControllerMethodNameResolver"빈에서 PropertiesMethodNameResolver를 통해 매핑이 된다. 결국 /member/addMember.do 가 호출되면 MemberController의 add 메소드가 /member/updateMember.do가 호출되면 update 메소드가 호출된다.

결국 위의 예는 빈 설정파일에서 URL을 다르게 함으로써 메소드에 접근할 수 있도록 설정한 것이다. (PropertiesMethodNameResolver 클래스를 통해서)

<bean name="/member/member.do" class="net.javajigi.member.web.MemberController">
	<property name="methodNameResolver">
		<ref local="methodNameResolver"/>
	</property>
</bean>

<bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
	<property name="paramName"> 
		<value>method</value>
	</property>
	<property name="defaultMethodName"> 
		<value>add</value>
	</property>
</bean>

위의 예를 보면 MemberController는 하나의 URL에 맵핑이 되었다. 그럼 어떻게 MemberController의 다른 메소드를 호출할 수 있을까? 그것은 "methodNameResolver" 빈의 "ParameterMethodNameResolver" 를 이용해서 호출이 가능하다. 
URL을 통해 전달되는 인자중에 "paramName"인 method라는 이름을 가지는 인자의 값에 해당하는 메소드를 호출해 주는 것이다. 
즉 /member/member.do?method=add 를 하게 되면 MemberController의 add()를 호출하는 것이다. /member/member.do?method=update를 하면 update()를 호출하게 될 것이다. 만일 아무 값도 없다면 default 으로 add()를 호출하게 된다. 호출하는 URL은 같지만 뒤 인자의 인자값을 변경해 줌으로써 다른 메소드를 호출할 수 있다.

ParameterMethodNameResolver를 이용하면 MultiActionController를 상속받은 Controller 에 메소드가 추가되더라도 설정파일에 추가적인 설정이 필요 없을 것이다. 하지만 호출할 때 추가된 메소드 이름을 알아야 한다.

데이터 바인딩(Data Binding) 
웹 애플리케이션 개발에서 입력, 수정과 관련된 기능을 구현하기 위해서 입력하는 값에 대한 유효성 체크와 입력 값을 도메인 모델로 바인딩 하는 기능이 필요하다.그 동안 개발자들은 유효성 체크를 위해서 화면단에는 많은 자바스크립트를 데이타 바인딩을 위해서는 HttpServletRequest에 담긴 인자를 꺼내서 다시 도메인 모델로 set하는 코드들이 난무했을 것이다.

Spring MVC 에서는 입력화면(또는 수정화면)에서 전달되는 입력 데이터를 비즈니스 계층에 전달하기 위하여 HttpServletRequest에 담긴 인자와 도메인 모델의 속성을 자동적으로 연결하는 데이터 바이팅을 지원하고 있다.

위에 나온 bind()가 그 역할을 하는 것이다.

bind(request,command);

Bind request parameters onto the given command bean
@param request request from which parameters will be bound
@param command command object, that must be a JavaBean
@throws Exception in case of invalid state or arguments

대신 클라이언트가 입력한 데이터를 도메인 모델과 데이터 바인딩 하기 위해서는 입력 화면에서 사용한 속성이름과 도메인 모델의 속성이름이 같아야 한다.

  • 회원가입 입력폼인 memberEntry.jsp
    <%--
    작성자 : 난다
    일자 : 2006.02.13
    설명: 회원가입
    --%> 
    <%@page contentType="text/html; charset=euc-kr"%>
    <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
    
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>    
        <title>사용자 관리 - 회원 가입</title>     
        <link rel="stylesheet" type="text/css" href="../css/style.css" media="screen">
      </head>  
     <body>
     <div id="member">
     <form name="thisform" method="post" action="${pageContext.request.contextPath}/member/addMember.do">   
    	<table summary="회원가입">
    		<caption>회원 정보 </caption>
    		<tr>
    			<th id="id">* 아이디</th>
    			<td headers="id">
    				<input type="text" id="id" name="id" />
    			</td>
    		</tr>
    		<tr>
    			<th id="password">* 비밀번호</th>
    			<td headers="password">
    				<input type="text" id="password" name="password" />
    			</td>
    		</tr>
    		<tr>
    			<th id="passwordRepeat">* 비밀번호</th>
    			<td headers="passwordRepeat">
    				<input type="text" id="passwordRepeat" name="passwordRepeat" />
    			</td>
    		</tr>	
    		<tr>
    			<th id="password_hint">* 비밀번호 힌트 </th>
    			<td headers="passwordHint">
    				<input type="text" id="passwordHint" name="passwordHint" />
    			</td>
    		</tr>
    		<tr>
    			<th id="passwordAnswer">* 비밀번호 답 </th>
    			<td headers="passwordAnswer">
    				<input type="text" id="passwordAnswer" name="passwordAnswer" />
    			</td>
    		</tr>
    		<tr>
    			<th id="email">* e-mail</th>
    			<td headers="email">
    				<input type="text" id="email" name="email" />
    			</td>
    		</tr>						
    	</table>
    	<center>	
    		<input type="submit" value="확인" tabindex="4" />
    	</center>
    </form>
    </div>
    </body>
    </html>
    
    
  • 도메인 모델 Member.java
Member.java
package net.javajigi.member.model;

import org.apache.commons.lang.builder.ToStringBuilder;

public class Member {
	private String userId = null;
	private String password = null;
	private String passwordRepeat = null;
	private String passwordHint = null;
	private String passwordAnswer = null;
	private String email = null;
	
         변수들에 대한 getter,setter 메소드...
              .
              .
              .
        public String toString() {
          return ToStringBuilder.reflectionToString(this);
        }
	
}

memberEntry.jsp의 입력필드의 이름은 id,password,passwordRepeat,passwordHint,passwordAnswer,email로 정의 되어 있다. 
그리고 바인딩되는 도메인 모델 Member도 id,password,passwordRepeat,passwordHint,passwordAnswer,email 가 정의되어 있다.
즉 같은 속성이름에 대해서는 가져올 수 있지만 만일 다르다면 가져오지 못하게 된다.

위의 예제에서 사용한 MultiActionController는 입력데이터에 대한 유효성 체크 기능을 지원하지 않는다. 만일 유효성 체크도 원한다면 MultiActionContorller를 상속받아서는 사용할 수 없다. 
유효성 체크와 바인딩 기능까지 모든 기능을 사용하길 원한다면 AbstractCommandControllder 클래스를 상속받아야 한다.

AbstractCommandControllder를 상속받은 Controller

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractCommandController;

import com.tmax.spring.domain.Member;
import com.tmax.spring.service.app.MemberService;

public class MemberAbstractCommandController extends AbstractCommandController{
    private static transient Log log = LogFactory.getLog(MemberAbstractCommandController.class);
	
    private MemberService memberService = null;
	
    public void setMemberService(MemberService memberService){
	this.memberService = memberService;
    }
	
    public MemberAbstractCommandController(){
	setCommandClass(Member.class);
    }
	
    protected ModelAndView handle(HttpServletRequest request, 
		HttpServletResponse response, Object command, BindException errors) 
                 		throws Exception {
	Member member = (Member)command;
		
	//memberService.addBbs(command);
	log.error("가져오는지..."+member.toString());
		
	return new ModelAndView("../index");	
     }
 }

AbstractCommandController를 상속받아 구현 하려면 바인딩하는 클래스를 디폴트 생성자 안에 setCommandClass()를 통해서 설정해야 한다. 위의 예를 보면

public MemberAbstractCommandController()

Unknown macro: { setCommandClass(Member.class); }

그러면 들어오는 입력 값들과 설정한 클래스와의 바인딩이 이전에 보았던 bind() 라는 메소드 없이 Casting 만으로 이루어 진다.

하지만 AbstractCommandController는 요청에 대해 각각의 Controller를 만들어야 한다.결국 데이터 바인딩 및 데이터에 대한 유효성 체크 기능을 지원하고 있지만 AbstractCommandController를 상속받은 Controller로는 다수의 요청을 처리 할 수 없는 것이다.즉 Controller 를 이용하면 요청에 따라 추가되고 빈 설정 정보또한 수정해 주어야 한다.

결국 큰 주제로 했던 다수의 요청을 한 Controller에서 처리하는 방법은 아니지만 좀 더 손쉽게 유효성체크와 데이터 바인딩을 할 수 있다는 점에서는 유용하다. 간단한 CRUD 같은 작업은 MultiController를 구현해 한 Controller에서 사용할 수도 있고( 물론 유효성체크는 스크립트나 다른 방법을 통해) 복잡하고 많은 입력 필드를 가지고 있는 화면이라면 AbstractCommandController를 사용해 좀 더 손쉽게 처리 할 수 있을 것이다.

이것은 각각의 프로젝트, 요구사항등에 맞추어 결정하면 될 것이다.

action-servlet.xml 추가사항
	<bean name="/member/addMemberWithAbstract.do"   class="com.tmax.spring.web.MemberAbstractCommandController">
	  <property name="memberService">
		<ref bean="memberService" />
	 </property>	
        </bean>
  • 실제로직을 담당하는 비지니스 객체인 MemberService.java 파일을 생성한다.
MemberService.java
package net.javajigi.member.service;

import net.javajigi.member.model.Member;

public class MemberService {
	
	public void addMember(Member member){
		//로직처리
	}
	
	public void updateMember(Member member){
		//로직처리
	}
}

하나의 Controller만으로 입력,수정처리하기

<ation-servlet.xml>
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="mappings">
			<props>				
				<prop key="/login.do">loginFormController</prop>				
			</props>
		</property>
	</bean>	
	
	<bean id="loginFormController" class="spring.LoginFormController">
		<property name="formView"><value>/login</value></property>
		<property name="successView"><value>/main</value></property>
	</bean>
		
	<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="viewClass">
			<value>org.springframework.web.servlet.view.JstlView</value>
		</property>
		<property name="cache"> 
			<value>false</value>
		</property>
		<property name="prefix">
			<value>/</value>
		</property>
		<property name="suffix">
			<value>.jsp</value>
		</property>		
	</bean>
</beans>

Controller를 URL과 매핑해주어야 한다.
login.do에 대한 요청을 loginFormController가 담당하며, 이 Controller는 로그인을 위한 로직처리를 포함하고, 사용자가 직접 작성해야한다.
입력화면은 /login 이며, 로직을 무사히 처리한후에는 /main으로 이동한다.
JSTL pages , jsp Page를 사용하기위해 JstlView를 선언하였다.

  • web-app/login.jsp 로그인 페이지를 생성한다.
    <login.jsp>
    <%@page contentType="text/html; charset=euc-kr"%>
    <%@ page language="java" import="java.util.*" %>
    <html>
      <head>
        <title>My JSP 'login.jsp' starting page</title>    
      </head>  
      <body>
      <form name="myForm" method="post" action="/spring/login.do">
       	<table>
       		<tr>
       			<td>
       				ID	: <input type="text" name="userId" value="1">   				
       			</td>
       		</tr>
       		<tr>
       			<td>
       				PASSWORD	: <input type="password" name="password" value="2">   				
       			</td>
       		</tr>
       		<tr>
       			<td>
       				<input type="submit" name="login" value="로그인"/>
       			</td>
       		</tr>
       	</table>
       </form>
      </body>
    </html>
    
  • web-app/main.jsp 로그인성공후 이동할 메인 페이지를 생성한다.
    <main.jsp>
    <%@page contentType="text/html; charset=euc-kr"%>
    <%@ page language="java" import="java.util.*" %>
    
    <html>
      <head>
        <title>My JSP 'main.jsp' starting page</title>   
      </head>  
      <body>
      	Login Success~!!
      </body>
    </html>
    
  • web-app/WEB-INF/net.javajigi.user.web.LoginFormController.java 파일을 생성한다.
LoginFormController.java
package net.javajigi.user.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.javajigi.user.model.Authenticate;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

public class LoginFormController extends SimpleFormController {	
	
	public LoginFormController() {
                  // bean 객체
		setCommandName("authenticate");
		setCommandClass(Authenticate.class);
	}
	
	public ModelAndView onSubmit(HttpServletRequest request,
			HttpServletResponse response, Object command, BindException errors)
			throws Exception {
		
		if (request.getParameter("login") != null) {
			Authenticate auth = (Authenticate) command;

			try {
				//로직처리 
                                          //boolean bResult = UserLoginProc.login(auth);

                                    //View 객체 리턴 
				return new ModelAndView(getSuccessView());
				
			} catch (Exception e) {
				request.setAttribute("errorMessage", e.getMessage());

				return new ModelAndView(getFormView());
			} 
			
		} else {
			HttpSession session = request.getSession();

			if (session.getAttribute("loginUser") != null) {
				return new ModelAndView("decorators/logout");
			} else {
				return new ModelAndView("decorators/login");
			}
		}
	}
}

SimpleFormController 을 상속받은 LoginFormController.java 파일을 생성한다.

  • web-app/WEB-INF/net.javajigi.user.model.Authenticate.java 파일을 생성한다.
Authenticate .java
package net.javajigi.user.model;


public class Authenticate {
    private String userId = null;

    private String password = null;
    
	public String getPassword() {
        return password;
    }

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

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}

LoginFormController.java에서 호출되는 bean객체를 생성한다.

참고문헌


출처 -  http://www.javajigi.net/display/OSS/Spring+MVC 








The Spring series, Part 3: Swing into Spring MVC


Spring MVC로 애플리케이션 개발하기 

난이도 : 중급

Naveen Balani, 아키텍트, Webify Solutions

2005 년 9 월 06 일

Naveen Balani의 Spring series, 세 번째 글이다. Spring 프레임웍을 사용하여 MVC 기반 애플리케이션을 개발하는 방법을 설명한다.

이번에는 Spring series의 Spring Model-View-Controller (MVC)에 대해 설명하겠다. 이전 글과 마찬가지로 뱅킹 예제를 통해 애플리케이션의 모델링과 구현 방법을 설명하고자 한다. 이 예제 애플리케이션에는 여러분이 이미 배운 기술(종속관계 투입) 뿐만 아니라 Spring MVC의 기능들까지 함축되어 있다.

시작하기에 앞서 소스를 다운로드하라. 참고자료에는 Spring 프레임웍과 Tomcat 5.0 관련 링크가 있다.

Spring MVC 프레임웍

Spring 프레임웍은 웹 애플리케이션 구현에 사용되는 완전한 MVC 모듈을 제공한다. Spring의 플러거블(pluggable) MVC 아키텍처에서는 빌트인 Spring 웹 프레임웍 또는 Struts 같은 웹 프레임웍을 사용할 지를 결정할 수 있다. 이 프레임웍은 전략 인터페이스를 통해 설정이 가능하며, JavaServer Pages (JSP), Velocity, Tiles, iText, POI 같은 여러 뷰(view) 기술이 포함되어 있다. Spring MVC는 컨트롤러의 역할, 모델 객체의 역할, 디스패쳐(dispatcher)의 역할, 핸들러 객체의 역할을 분리한다. 따라서 커스터마이징이 쉽다.

웹 MVC 프레임웍은 요청을 핸들러에 보내는(dispatch) DispatcherServlet을 중심으로 디자인된다. 여기에 설정 가능한 핸들러 매핑, 뷰 해상도, 로케일, 테마 해상도, 업로드 파일 지원 등이 포함된다. 기본 핸들러는 단 한 개의 메소드 ModelAndView handleRequest(request, response)를 가진 매우 간단한 Controller 인터페이스이다. Spring은 하위 클래스로 분류될 수 있는 컨트롤러 계층을 제공한다. 애플리케이션이 사용자 엔트리 형식을 처리해야 한다면, AbstractFormController를 하위 클래스로 분류한다. 다중 페이지 엔트리를 한 개의 형식으로 처리하려면AbstractFormController를 하위 클래스로 분류한다.

예제 애플리케이션을 통해서 이러한 기능들을 먼저 배우게 될 것이다. 뱅킹 애플리케이션에서는 사용자가 계좌 정보를 검색할 수 있다. 이를 구현하는 과정에서 Spring MVC 프레임웍을 설정하는 방법과 프레임웍의 뷰 레이어를 구현하는 방법을 배우게 된다. 뷰 레이어는 아웃풋 데이터를 렌더링하는 JavaServer Pages와 JSTL 태그들로 구성된다.




위로


Spring MVC 설정

예제 애플리케이션 구현을 시작하려면 Spring MVC의 DispatcherServlet을 설정한다. web.xml 파일에 모든 설정을 등록한다. Listing 1은 sampleBankingServlet의 설정 방법이다.


Listing 1. Spring MVC DispatcherServlet 설정


<servlet>
   <servlet-name>sampleBankingServlet</servlet-name>  
   <servlet-class>
      org.springframework.we.servlet.DispatcherServlet
   <servlet-class>
   <load-on-startup>1<load-on-startup>
<servlet>        

DispatcherServlet은 XML 파일에서 Spring 애플리케이션 컨텍스트를 로딩한다. XML 파일의 이름은 해당 서블릿 이름에 -servlet이 붙어있는 형태이다. 이 경우 DispatcherServletsampleBankingServlet-servlet.xml 파일에서 애플리케이션 컨텍스트를 로딩한다.

애플리케이션 URL 설정

다음에는 sampleBankingServlet이 핸들 할 URL을 설정한다. 다시 강조하지만 web.xml 파일에 이 모든 설정을 등록해야 한다.


Listing 2. URL 설정


<servlet-mapping>
<servlet-name> sampleBankingServlet<servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

설정 파일 로딩

이제 설정 파일을 로딩 할 차례이다. Servlet 2.3 스팩에는 ContextLoaderListener를 Servlet 2.2 이전 스팩에는 ContextLoaderServlet을 등록한다. 백워드 호환을 위해 ContextLoaderServlet을 사용한다. 웹 애플리케이션을 시작하면 ContextLoaderServlet은 Spring 설정 파일을 로딩한다. Listing 3은 ContextLoaderServlet의 등록 모습이다.


Listing 3. ContextLoaderServlet 등록


<servlet>
  <servlet-name>context>servlet-name>
  <servlet-class>
     org.springframework.web.context.ContextLoaderServlet
  </servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


contextConfigLocation 매개변수는 로딩 할 Spring 설정 파일을 정의한다.


<context-param>
<param-value>contextConfigLocation</param-value>
<param-value>/WEB-INF/sampleBanking-services.xml</param-value>
</context-param>

sampleBanking-services.xml 파일은 뱅킹 애플리케이션 예제에 대한 설정 및 서비스의 빈 설정을 파일이다. 다중 Spring 설정 파일을 로딩해야 했기 때문에 <param-value> 태그에 콤마를 사용할 수 있었다.




위로


Spring MVC 예제

이 뱅킹 애플리케이션은 사용자가 고유 아이디와 패스워드를 사용하여 계좌 정보를 볼 수 있다. Spring MVC가 다른 옵션들도 제공하지만 나는 JSP를 사용하겠다. 이 샘플 애플리케이션은 사용자 인풋(아이디와 패스워드)을 위한 한 개의 뷰 페이지와 사용자 계좌 정보를 디스플레이 하는 페이지로 구성된다.

Spring MVC의 LoginBankController를 확장한 LoginBankController로 시작한다. SimpleFormController는 HTTP GET 요청을 받을 때 형식을 디스플레이하고 HTTP POST를 받을 때 같은 형식 데이터를 처리하는 기능이 있다. LoginBankController는 인증 및 계좌 작동에 AuthenticationService와 AccountServices 서비스를 사용한다. "뷰 속성 설정" 섹션의 Listing 5는 AuthenticationServiceAccountServices LoginBankController로 전달하는 방법을 보여준다. Listing 4는 LoginBankController의 코드이다.

뷰 속성 설정

이제 HTTP GET 요청을 받을 때 디스플레이 될 페이지를 등록해야 한다. formView 속성을 사용하여 Spring 설정에 이 페이지를 등록한다. (Listing 5). sucessView 속성은 형식 데이터가 게시되고 로직이 doSubmitAction() 메소드에서 성공적으로 실행된 후에 디스플레이 되는 페이지이다. formView와 sucessView 속성 모두 뷰의 논리적 이름이다. 이것은 실제 뷰 페이지와 매핑된다.


Listing 5. LoginBankController 등록

  
   <bean id="loginBankController"
         class="springexample.controller.LoginBankController">
      <property name="sessionForm"><value>true</value></property>
   <property name="commandName"><value>loginCommand</value></property>
   <property name="commandClass">
      <value>springexample.commands.LoginCommand</value>
   </property>

      <property name="authenticationService">
         <ref bean="authenticationService" />
      </property>
      <property name="accountServices">
         <ref bean="accountServices" />
      </property>
      <property name="formView">
         <value>login</value>
      </property>
      <property name="successView">
         <value>accountdetail</value>
      </property>

   </bean>

commandClass와 commandName 태그는 뷰 페이지에서 작동할 빈을 결정한다. 예를 들어, loginCommand 빈은 애플리케이션의 로그인 페이지인 login.jsp를 통해 접근할 수 있다. 사용자가 로그인 페이지를 제출하면 애플리케이션은 LoginBankController의 onSubmit() 메소드에 있는 명령어 객체에서 형식 데이터를 검색할 수 있다.

뷰 리졸버(resolver)

Spring MVC의 뷰 리졸버(view resolver)는 각 논리적 이름을 실제 리소스(계좌 정보를 포함하고 있는 JSP 파일)로 바꾼다. 여기에서는 Spring의 InternalResourceViewResolver를 사용한다.(Listing 6)

사용자의 로그인 이름은 /jsp/login.jsp로 바뀌고 viewClass는 JstlView로 바뀐다. JSP 페이지에서 JSTL 태그를 사용했기 때문이다.

인증과 계좌 서비스

앞서 언급했듯이, LoginBankController는 Spring의 AccountServices와 AuthenticationService를 갖고 있다. AuthenticationService 클래스는 뱅킹 애플리케이션의 인증을 관리한다.AccountServices 클래스는 일반적인 뱅킹 서비스를 핸들한다. Listing 7은 뱅킹 애플리케이션의 인증 및 계좌 서비스의 설정 모습이다.


Listing 7. 인증 및 계좌 서비스의 설정


<beans>

   <bean id="accountServices" 
      class="springexample.services.AccountServices">

   </bean>

   <bean id="authenticationService" 
      class="springexample.services.AuthenticationService">

   </bean>

</beans>

위 서비스들을 sampleBanking-services.xml에 등록한다. 여기에는, web.xml 파일이 로딩된다. 컨트롤러와 서비스가 설정되면 이 애플리케이션은 완성된다. 이제 전개하여 테스트해보자.




위로


애플리케이션 전개

여기에서는 이 애플리케이션 예제를 Tomcat 서블릿 컨테이너에 전개했다. Tomcat은 Java Servlet과 ServerPages 기술의 공식 레퍼런스 구현에 사용되는 서블릿 컨테이너이다. download jakarta-tomcat-5.0.28.exe를 다운로드하여 Tomcat을 설치한다. (c:\tomcat5.0)

그런 다음, 예제 코드를 다운로드하고 c:\에 압축을 푼다. Spring 프로젝트 폴더를 만들었다면 이것을 열어 spring-banking 하위폴더를 c:\tomvat5.0\webapps에 복사한다. spring-banking 폴더는 Spring MVC 예제 애플리케이션을 포함하고 있는 웹 아카이브이다. lib 폴더에는 Spring 프레임웍, Spring 관련 MVC 라이브러리, JSTL 태그 라이브러리, jar 파일이 들어있다.

Tomcat 서버를 시작하려면 다음 명령어를 실행한다.

cd bin C:\Tomcat 5.0\bin> catalina.bat start

Tomcat 이 시작되고 Spring MVC 예제 애플리케이션을 전개한다.




위로


애플리케이션 테스트

애플리케이션을 테스트하려면 웹 브라우저를 열어 http://localhost:tomcatport/springbanking으로 가서 tomcatport를 Tomcat 서버를 실행하는 포트로 대체한다. 로그인 스크린이 나타난다. (그림 1). 사용자 아이디 "admin"과 "password"를 입력하고 Login 버튼을 누른다. 다른 아이디와 패스워드를 입력하면 에러가 된다.


그림 1. Spring MVC 로그인 스크린
Screenshot of the Spring MVC example login screen 

로그인이 성공하면 계좌 정보를 볼 수 있는 페이지가 디스플레이 된다. (그림 2)


그림 2. Spring MVC 계좌 정보 스크린
Screenshot of the Spring MVC example account detail screen 



위로


결론

지금까지 Spring series의 Spring MVC 프레임웍을 설명했다. Spring MVC 애플리케이션의 설치 및 개발 방법, 종속관계를 설정하여 Spring MVC 컨트롤러로 주입하는 방법, JavaServer Pages를 사용하여 애플리케이션 뷰를 개발하는 방법, 페이지를 Spring 뷰 레이어로 통합하는 방법을 설명했다. Tomcat 서블릿 컨테이너에 애플리케이션을 전개하여 테스트도 해 보았다.

다음 Spring series에서는 JMS 기반 애플리케이션과 Spring 프레임웍을 통합하는 방법을 설명하겠다. 참고자료 섹션의 리소스들을 숙지하기 바란다.





위로


다운로드 하십시오

설명이름크기다운로드 방식
Example source code, spring files, jar files wa-spring3-SpringProjectPart3.zip 1966 KB  FTP
다운로드 방식에 대한 정보 Get Adobe® Reader®



위로


참고자료

교육

제품 및 기술 얻기

토론



위로


필자소개

Naveen Balani, 아키텍트, Webify Solutions


출처 -  http://www-128.ibm.com/developerworks/kr/library/wa-spring3/#download 






'Framework & Platform > Spring' 카테고리의 다른 글

spring MVC 예제 3  (9) 2012.03.21
spring MVC 예제 2_MyBatis 적용  (0) 2012.03.21
Spring Framework 소개  (0) 2012.01.31
Spring AOP(Aspect Oriented Programming) 개념  (0) 2010.12.18
spring - IOC (Inversion of Control)  (0) 2010.12.18
Posted by linuxism
,
12-03-14 19:11 게임메카 김미희 기자 

개인정보보호를 위해 국내 인터넷기업의 주민등록번호 수집을 원칙적으로 금하는 개정 정보통신망법이 올해 8월 18일 시행을 앞두고 있다. 이에 한국인터넷산업협의회(이하 인산협)은 주민등록번호를 대체할 본인인증 대책을 시급히 마련하여 국내 사업자가 불법자로 전락하는 일을 막아야 한다고 강조했다.

인산협은 3월 15일, 주민등록번호 수집 금지를 골자로 한 개정 정보통신망법에 대한 성명서를 발표했다. 2011년 발생한 대형 해킹 사건 등, 이용자의 개인정보보호를 목적으로 한 법의 취지는 이해하겠으나, 셧다운제와 같이 본인인증 및 연령인증이 필요한 서비스를 제공 중인 국내 업체가 취할 마땅한 대체 수단이 없다는 것이 문제라는 것이다.

즉, 명확한 대체 수단이 없이 주민등록수집만 금지할 경우 졸지에 많은 사업자들이 법률위반자가 되는 수모를 겪을 수 있다는 것이 인산협의 주장이다. 법에 명시된 ‘아이핀’의 경우 2011년 국정감사에서 한 곳에 다수의 개인정보가 저장되어 유출 위험도가 높다는 것과 사용 비율이 국내 인터넷 이용인구의 10%에도 못 미친다는 점을 토대로 이미 실패한 수단이라 평가된 점을 근거로 마땅하지 않은 방법이라 설명하고 있다.

해외 업체에 대한 역차별 문제 역시 지적되었다. 인산협이 대표적인 예로 든 것은 ‘페이스북’이다. 13세 미만의 미성년자 가입을 금하고 있는 페이스북은 사실 성명과 이메일주소, 생년월일만 기재하면 이용할 수 있도록 구성되며, 국내에서도 동일하게 서비스 중이다. 이를 두고 인산협은 “그렇다면 연령확인 및 부모 동의 확인의무가 있는 국내 사업자도 페이스북과 같은 정책으로 서비스를 한다면 과연 처벌을 받지 않을 수 있을지 의문이다”라고 전했다.

또한 해외 사업자와의 역차별은 결국 국내 사업자의 경쟁력을 약화시키는 결과를 초래하게 될 것이라는 것이 인산협의 입장이다.

인산협이 원하는 바는 크게 3가지로 나뉜다. △ 주민등록번호를 수집하지 않고도 현행법을 준수하기 위한 실효성 있는 대체수단 마련 △ 주민등록번호 수집/이용이 가능한 경우와 그렇지 않은 경우를 명확히 구분하고, 연령을 위해 사업자가 취할 방안에 대한 가이드라인 확충 △ 법 시행에 필요한 시스템 변경을 준비하기 위해 필요한 6개월 이상의 유예기간 보장이다.

즉, 인산협은 법 제정에 대한 명확한 가이드라인을 정부 측이 하루 빨리 제시해주길 바란다는 뜻을 강하게 전하고 있다


출처 -  http://www.gamemeca.com/news/news_view.html?seq=36&ymd=20120314&page=1&point_ck=&search_ym=&sort_type=&search_text=&send=&mission_num=&mission_seq=



Posted by linuxism
,


log4j.properties 설정법...


로그4J 라이브러리 임포트 과정은 생략함니다.

로그4J가 import된 프로젝트의 Src 폴더에 아래와같은 파일을 생성함니다

log4j.properties

이제 log4j가 업무를 수행할때 해당 파일에 들어있는 환경변수들을 사용하여 출력 함니다.

log4j.properties 파일 내부데이터로 들어가겠습니다.

먼저 출력 객체의 선언 임니다.

두가지 객체 선언 방법이 있습니다.
log4j.rootLogger       
log4j.logger.

전자의 경우 rootLogger로서 해당 프로젝트내에서 발생하는 로그업무를 모두 출력 합니다.
(레벨별 제한은 물론 함니다.)

후자의 경우 logger 뒤에 '.' 가 있는데. 해당 프로젝트내의 특정 패키지를 계속 이어붙임니다.
ex) log4j.logger.baeseulki.seul

이렇게하면 저 패키지에서 일어나는 로그업무에 대해서만 처리함니다.

객체선언의 마지막 할당 입니다.

log4j.rootLogger               =     debug, hoho, haha
log4j.logger.baeseulki.seul      =      info, seul, kikiki

오른쪽에 1번째 변수는 출력해줄 경보레벨의 제한임니다.
info의 경우 debug.info.warn.err.fatal 에서 debug는 출력되지 않습니다.

2번째 이상부터는 객체 명임니다. 아래에서 객체명을 통해서 로그의 출력방법, 출력 형식등을 설정함니다.

즉 현재 루트로거에는 hoho와 haha방식으로 출력한다고 보면 되겠습니다.

두방법 다 사용하면
두방법다 객체에 지정한 방법대로 출력함니다. 이제 출력 객체 설정에 들어감니다.


출력 객체로의 접근방법은 다음과 같습니다.

log4j.appender.객체명.객체의 속성값 = 속성에 적용할 값

객체명은 hoho , seul, 과 같이 우리가 생성한 객체입니다.
속성값은 file 같이 정해진 특정 속성입니다.
file 속성은 출력할 파일명을 말함니다.
ex) log4j.appender.hoho.file = c/log/hoho.log

일단 객체의 출력 방법부터 설정합시다. 출력 방법의 경우 속성값이 없습니다. 객체 자체에 넣습니다.

og4j.appender.객체명 = 출력방식

출력방식은 정해진 값들이 있습니다.
org.apache.log4j.ConsoleAppender     //콘솔창에 출력
org.apache.log4j.DailyRollingFileAppender       //파일에 출력

ex)  log4j.appender.hoho = org.apache.log4j.DailyRollingFileAppender


layout 속성은 출력한 스타일을 표현함니다. 2중으로 사용함니다.
일반적인 PatternLayout을 사용함니다.

세부적인 내용은  layout.ConversionPattern 에 추가로 설정합니다.
아래와같이 함니다.

log4j.appender.hoho.layout=org.apache.log4j.PatternLayout
log4j.appender.hoho.layout.ConversionPattern=[%d] %-5p %l - %m%n

아래 패턴에 들어가는 내용은 프로그램상에서
private static Logger logger = Logger.getLogger(seul.class);
logger.debug("슬슬");

여기 들어간 슬슬 이라는 출력 내용을 ConversionPattern 의 형태로 변경하여 출력 함니다.
Pattern의 %? 값들의 내용은 다음과 같습니다.

%p : 호출명(ex DEBUG, INFO, WARN, ERROR, FATAL)
%n : 줄바꿈
%m : 매개변수 메세지
%t : 스레드
%F : 실행파일명
%M : 수행 메소드
%L : 라인????넘버
%d : 날짜


log4j.appender.hoho.DatePattern='.'yyyy-MM-dd
// 매일 자정에 로그파일을 교체하며 기존파일은 xx.log_2004.07.12

log4j.appender.hoho.Threshold=DEBUG
//출력할 정보의 최소단위.


소스

log4j.rootLogger = info, stdout, dailyfile

log4j.stdout = false
log4j.debug = false

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p ({%t} %F[%M]:%L) [%d] HIHI- %m%n

log4j.appender.dailyfile.Threshold = ERROR
log4j.appender.dailyfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyfile.File = c:\\log\\logfile.log
log4j.appender.dailyfile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyfile.layout.ConversionPattern=%5p ({%t} %F[%M]:%L) [%d]  - %m%n

log4j.appender.dailyfile.DatePattern ='.'yyyy-MM-dd


log4j.logger.log4j.bae=DEBUG, BAE

log4j.appender.BAE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.BAE.File=C:/logs/BAE.log
log4j.appender.BAE.DatePattern='.'yyyy-MM-dd
log4j.appender.BAE.Threshold=DEBUG
log4j.appender.BAE.layout=org.apache.log4j.PatternLayout
log4j.appender.BAE.layout.ConversionPattern=[%d] %-5p %l - %m%n

log4j.logger.log4j.hehe=INFO, HEHE

log4j.appender.HEHE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.HEHE.File=C:/logs/HEHE.log
log4j.appender.HEHE.DatePattern='.'yyyy-MM-dd
log4j.appender.HEHE.Threshold=DEBUG
log4j.appender.HEHE.layout=org.apache.log4j.PatternLayout
log4j.appender.HEHE.layout.ConversionPattern=[%d] %-5p %l - %m%n

http://blog.naver.com/skykingkjs?Redirect=Log&logNo=150152227953
[출처] [개발툴][Log4j] 로그4J properties 설정법.|작성자 카루



출처 - http://yth2626.blogspot.kr/2013/01/log4jproperties.html







Thursday, February 22, 2007

Log4J Logger vs. Category

Logging is the practice of recording sequential data. This is also a low-tech method of debugging and in some cases also the only way as the proper debugging tools may not be always available or applicable. Logging in Java can range from simple System.out.println() statements to usage of sophisticated logging frameworks.

At the time of writing there are several logging frameworks for logging in Java available. There is a popular and probably the most widely used Log4J framework. Younger brother of Log4J is the Java'sLogging API that became part of Java SE in version 1.4. See How does the Java logging API stack up against log4j for comparison. The are many other logging frameworks: SimpleLogjLoProtomatter Syslog, etc.

There are also several frameworks that enable abstraction from logging frameworks, also known as logging bridges(Apache Commons Logginglog-bridge). These allow switching between logging frameworks. These and many others can be found at Open Source Logging Tools in Java at java-source.net.

Log4J was one of the early logging frameworks that gained popularity and is present in many source code bases. In some cases you could encounter usage of Category class rather than Logger. If you work or worked on projects that are built on top of relatively young frameworks, you may not even know that Category class exists as its natural habitat is in old Java code. I hadn't known that Category class existed until I saw it one day. I saw it in a place where I would usually use Logger class. Since that day I kept reminding myself to look at it and see what's different from Logger class and why would one use it.

So what is the difference between Category and Logger classes? The Java documentation in Category class states

This class has been deprecated and replaced by the Logger subclass. It will be kept around to preserve backward compatibility until mid 2003.

Logger is a subclass of Category, i.e. it extends Category. In other words, a logger is a category. Thus, all operations that can be performed on a category can be performed on a logger. Internally, whenever log4j is asked to produce a Category object, it will instead produce a Logger object. Log4j 1.2 will never produce Category objects but only Logger instances. In order to preserve backward compatibility, methods that previously accepted category objects still continue to accept category objects.

Then the following example shows how Category was used and how Logger should be used.

// Deprecated form:
Category cat = Category.getInstance("foo.bar");

// Preferred form for retrieving loggers:
Logger logger = Logger.getLogger("foo.bar");

And that's it. That's all you have to do to replace Category with Logger (apart from renaming the variable).

The documentation also says

There is absolutely no need for new client code to use or refer to the Category class. Whenever possible, please avoid referring to it or using it.

Why is this important? The plan is that from Log4J version 1.3 Category class will be removed! What can we do to prepare our code for Log4J 1.3? Well Preparing for Log4J version 1.3 has it all spelled out. It's worth to read if you want to understand all implications this upgrade may have. I just shamelessly paste the steps here:

  1. Never refer to the Category class directly, refer to Logger instead.
  2. Do not invoke the deprecated Category.getRoot method. Invoke Logger.getRootLogger method instead.
  3. Do not invoke the deprecated Category.getInstance(String) method. InvokeLogger.getLogger(String) method instead.
  4. Do not invoke the deprecated Category.getInstance(Class) method. InvokeLogger.getLogger(Class) method instead.
  5. Never refer to the Priority class, refer to Level class instead.
  6. Do not invoke the deprecated Category.setPriority(Priority) method. InvokeLogger.setLevel(Level) method instead.
  7. Do not invoke the deprecated Priority.toPriority(int) method. Invoke Level.toLevel(int) method instead. The same holds true for the other variants of the Priority.toPriority method.
  8. Never refer to the deprecated Priority.FATALPriority.ERRORPriority.WARN,Priority.INFOPriority.DEBUG fields. Refer to the Level.FATALLevel.ERROR,Level.WARNLevel.INFOLevel.DEBUG fields instead.

  9. If you confiugure appenders programmatically, do not forget to invoke the activateOptions method of an appender after you have instantiated it and set its options.

and as the above mentioned document says

For 99.99% of users, this translates to the following string find-and-replace operations:
  1. Replace the string "Category.getInstance" with the string "Logger.getLogger".
  2. Replace the string "Category.getRoot" with the string "Logger.getRootLogger".
  3. Replace the string "Category" with the string "Logger".
  4. Replace the string "Priority" with the string "Level".

Happy logging!


source - http://hanuska.blogspot.kr/2007/02/log4j-logger-vs-category.html







'Framework & Platform > Common' 카테고리의 다른 글

OSGi(Open Service Gateway initiative)  (0) 2012.03.25
모델1과 모델2의 차이점  (0) 2012.03.21
디자인 패턴  (0) 2012.03.18
Refactoring  (0) 2012.03.18
Struts 2 따라잡기  (0) 2010.12.26
Posted by linuxism
,