|
* Spring MVC
스프링 MVC 프레임워크는 스프링을 기반으로 하고 있기 때문에 스프링이 제공하는 트랜잭션 처리나 DI 및 AOP 적용 등을 손쉽게 사용할 수 있다는 장점이 있다.
1) 스프링 MVC의 주요 구성 요소
- DispatcherServlet : 클라이언트의 요청을 전달받는다. 컨트롤러에게 클라이언트의 요청을 전달하고, 컨트롤러가 리턴한 결과값을 view에 전달하여 알맞은 응답을 생성하도록 한다.
- HandlerMapping : 클라이언트의 요청URL을 어떤 컨트롤러가 처리할지를 결정한다.
- Controller : 클라이언트의 요청을 처리한뒤, 그 결과를 DispatcherServlet 에 알려준다.
- ModelAndView : 컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담는다.
- ViewResolver : 컨트롤러의 처리결과를 생성할 뷰를 결정한다.
- View : 화면 생성
일반적인 Spring Model View Controller 아키텍처 에서 Front Controller 의 개념을 그림으로 보면 아래와 같다 .
주요 역할
- 수신 요청 가로채기
- 요청의 페이로드를 데이터의 내부 구조로 변환
- 추가 처리를 위해 데이터를 모델로 전송
- 모델에서 처리된 데이터를 가져 오고 렌더링을 위해 해당 데이터를 보기로 이동
이 때 DispatcherServlet은 아키텍처에서 Front Controller 역할을 한다 .
**** 실습단계 ******
(1) DispatcherServlet 설정 및 설정파일 지정
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
(2) HandlerMapping 설정 추가 ( 기본은 BeanNameUrlHandlerMapping 이다 : 클라이언트의 요청 URL과 동일한 이름을 갖는 Controller를 선택한다)
: 클라이언트의 요청을 어떤 Controller 가 처리할지에 대한 정보를 제공한다.
(3) 컨트롤러 구현 및 설정
: 실제로는 직접 Controller 인터페이스를 구현하는 경우는 드물며, 스프링이 제공하는 몇 가지 기본 컨트롤러 클래스 중에서 사용한다.
public class DeptActionController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView();
mav.setViewName( "list" );
mav.addObject( "greeting" , "Hello World" );
return mav; // 또는 return new ModelAndView( "list" ) ;
}//end handleRequest
<!-- Controller 설정 -->
<bean name="/shop/index.do" class="test.web.DeptActionController" />
(4) ViewResovler 설정 추가
: 컨트롤러가 리턴하는 ModelAndView는 뷰정보를 담고 있다. 이 정보에 해당하는 뷰를 선택한다.
스프링 MVC에서는 몇 가지 ViewResolver를 제공한다. InternalResourceViewResolver는 JSP를 이용하여 뷰를 구현할 때 사용한다.
<!-- ViewResolver 설정 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/spring/"/>
<property name="suffix" value=".jsp"/>
</bean>
(5) 뷰코드 구현 (list.jsp )
: InternalResourceViewResolver는 ModelAndView 에 저장된 객체를 HttpServletRequest 객체의 속성( attribute)에 저장한다.
<%@ page contentType="text/html;charset=UTF-8" %>
${greeting}
2) DispatcherServlet 설정과 ApplicationContext 관계
DispatcherServlet은 기본적으로 웹 애플리케이션의 \WEB-INF\ 디렉토리의 서블릿이름-servlet.xml 파일로부터 스프링
정보를 읽어온다. 하지만, 경우에 따라 한 개 이상의 설정 파일을 사용해야 하는 경우가 있다. 또는 기본설정 파일이 아닌 다른 이름의 설정 파일을 사용할 경우도 있다. 이렇게 기본 설정파일이 아닌 파일들로부터 설정정보를 읽어오고 싶다면 초기화 파라미터(init-param)를 사용한다. 파라미터의 name값은 contextConfigLocation 으로 설정한다. value값은 여러 값이 올수 있으며 콤마(,) , 공백문자, 세미콜론(;) 등으로 구분한다
<servlet>
<servlet-name>dept</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dept2-servlet.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
하나의 DispatcherServlet 당 하나의 WebApplicationContext가 생성된다.
- 다음은 각각의 WebApplicationContext가 생성되며 서로 간의 빈객체를 사용할 수 없다.
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/hello-servlet.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>world</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
- 서로 다른 DispatcherServlet 에서 공통 빈을 사용하고자 할 때에는 다음과 같이 context-param 과 ContextLoaderListener 을 이용한다.
방법 1 :
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/hello-servlet.xml /WEB-INF/world-servlet.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
방법 2: ( 추천 )
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value> /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>world</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
실제로 ContextLoaderListener (부모:,,root ) 와 DispatcherServlet은 각각 WebApplicationContext ( 자식 )객체를 생성한다.
이 때 생성되는 WebApplicationContext 객체 간의 관계는 상속관계이다. 자식은 root가 제공하는 빈을 사용할 수 있기 때문에
각각의 DispatcherServlet이 공통으로 필요로 하는 빈을 ContextLoaderListener 를 이용하여 설정하는 것이다.
만약 context-param을 설정하지 않으면 ContextLoaderListener 는 기본적으로 /WEB-INF/applicationContext.xml 파일을 찾는다.
3) Spring 필터 사용
: org.springframework.web.filter.CharacterEncodingFIlter 클래스를 이용한다.
: UTF-8 로 설정한다. ( 모든 파일을 UTF-8 로 저장한다 )
: forceEncoding"="true" 설정해야 적용된다.
<!-- Filter 클래스 이용한 한글 인코딩 forceEncoding"="true"-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--enforce this filter's encoding in any case ("forceEncoding"="true"). -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4) HandlerMapping 구현 클래스
: DispatcherServlet 은 클라이언트의 요청이 들어오면 해당 요청을 처리할 컨트롤러를 구하기 위해 HandlerMapping을 이용한다. 스프링이 제공하는 주요 HandlerMapping 구현 클래스들은 AbstractUrlHandlerMapping 추상 클래스를 직, 간접적으로 상속받고 있다.
(1) SimpleUrlHandlerMapping : 패턴과 컨트롤러 이름을 비교하여, URL이 패턴에 매칭될 경우 지정한 컨트롤러를 사용한다.
(2) BeanNameUrlHandlerMapping URL과 일치하는 이름을 갖는 빈을 컨트롤러로 사용한다.
(3) ControllerClassNameHandlerMapping URL과 매칭되는 클래스이름을 갖는 빈을 컨트롤러로 사용한다.
(4) DefaultAnnotationHandlerMapping ( 2.5 추가 ) @RequestMapping 어노테이션을 이용하여 요청을 처리할 컨트롤러를 구한다.
* 여러 개의 HandlerMapping 설정 : order 프로퍼티를 이용하여 사용순서를 설정한다. ( 없으면 정의한 순서대로 지정된다 )
<bean id="mapping1" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="order" value="2"/>
<property name="alwaysUseFullPath" value="true" />
<property name="mappings">
<props>
<prop key="shop/hello.do">helloController</prop>
<prop key="shop/world.do">worldController</prop>
</props>
</property>
</bean>
<bean id="mapping1" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="order" value="1"/>
</bean>
5) Controller 클래스 구현
스프링은 다양한 종류의 컨트롤러 클래스를 제공하는데, 이들 클래스들은 기본적으로 org.springframework.web.servlet.mvc.Controller 인터페이스를 구현하고 있다. ModelAndView 객체의 뷰 이름이 뷰 구현 기술에 의존하지 않는다는 것이다. 즉, 뷰를 JSP로 할지, velocity로 할지는 ModelAndView에 명시되지 않는다. 실제로 어떤 기술을 이용하여 응답 결과를 생성할지는 ViewResolver 를 통해서 결정된다.
컨트롤러에서 직접 HttpServletResponse을 이용해서 응답 데이터를 전송할 경우에 컨트롤러는 null 값을 리턴한다.
* 용도에 따른 Controller 클래스 분류
(1) Controller , AbstractController : 단순 처리
: 별도의 기능을 제공하지 않는 컨트롤러. 요청 파라미터 처리등의 작업을 직접 구현해 주어야 한다.
(2) AbstractCommandController : 파라미터 맵핑
: 요청 파라미터를 객체에 저장해주며, 파라미터 값 검증작업을 제공한다.
(3) SimpleFormController : 입력 폼 처리
: 폼을 출력하고 폼에 입력한 데이터를 처리할 때 사용된다.
(4) AbstractWizardFormController : 다중 페이지 입력폼 처리
: 여러 페이지에 걸쳐서 데이터를 입력하는 경우, 입력폼의 흐름을 제어하고 입력한 데이터를 처리할 때 사용된다.
(5) ParameterizableViewController : 정적 뷰 맵핑
UrlFilenameViewController
: 컨트롤러에서 어떤 기능도 수행하지 않고 단순히 클라이언트의 요청을 뷰로 전달할 때 사용된다.
(6) MultiAtionController : 다중 액션
: 연관되거나 비슷한 로직을 수행하는 다수의 기능을 하나의 컨트롤러에서 구현할 때 사용한다.
6) 유효성 검사 및 BindException 클래스
커맨드 객체의 검증은 Validator ( org.springframework.validation.Validator ) 을 사용하여 커맨드 객체의 유효성을 검사한다.
커맨드 객체를사용하는 컨트롤러 클래스들은 커맨드 객체를 생성한 뒤 Validator에 커맨드 객체를 전달하여 검증을 요청한다.
* Validator 인터페이스의 2 가지 메소드
- boolean supports( Class clazz ) : Validator가 해당 클래스에 대한 값 검증을 지원하는지의 여부를 리턴한다.
- void validate ( Object target , Errors errors ) : target 객체에 대한 검증을 실행한다. 검증결과 문제가 있을 경우에 errors 객체에 예외정보를 저장한다.
7) Errors 인터페이스와 BindException 클래스
앞서 Validator 는 Error 인터페이스가 제공하는 rejectXXX() 메소드를 이용하여 에러 정보를 저장하였다.
이들 간의 상속관계는 다음과 같다.
<<Errors>>
----------- <<BindingResult>>
------------- AbstractBindingResult
-------------- BindException
- Errors : 커맨드 객체의 검증결과를 저장한다.
- BindingResult : 요청 파라미터 값을 커맨드 객체에 복사한 결과를 저장하며, 에러 코드로부터 에러 메시지를 가져온다.
- BindException : 컨트롤러에 에러 정보를 전달할 때 사용된다. 내부적으로 bindingResult 필드를 사용하여 실제 요청처리를 위임한다. Exception 클래스를 상속받는다.
- AbstractBindingResult : BindResult 인터페이스의 기본 구현 클래스. 검증 결과를 저장하고 에러 메시지를 추출하는 등의 기능을 제공한다.
8) HandlerInterceptor 를 통한 요청 가로채기
스프링이 기본적으로 제공하는 HandlerMapping 은 HandlerInterceptor를 이용해서 컨트롤러가 요청을 처리하기 전과 처리한 후에 알맞은 기능을 수행할 수 있도록 하고 있다.
조건에 따라서 컨트롤러에 요청을 전달하지 않고 싶거나 컨트롤러가 요청을 처리한 후에 ModelAndView 객체를 조작하고 싶은 경우에 유용하다. ( 로그인 여부 , 이벤트 종료 처리 등 )
사용은 org.springframework.web.servlet.HandlerInterceptor 인터페이스 또는 org.springframework.web.servlet.handler.HandlerInterceptorAdapter 클래스를 이용할 수 있다.
* HandlerInterceptor 인터페이스의 추상메소드
- boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
: 클라이언트의 요청을 컨트롤러에 전달하기 전에 호출된다. handler 변수에는 컨트롤럭 객체가 전달된다. 체인형성이 가능하며
false 값을 리턴하면 체인의 다음 HandlerInterceptor 또는 컨트롤러를 실행하지 않고 요청처리를 종료한다.
-void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
: 컨트롤러가 요청을 처리한 뒤에 호출된다. 컨트롤러 실행 중에 예외가 발생하면 postHandler 메소드는 실행되지 않는다.
-void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
: 클라이언트의 요청을 처리한 뒤 뷰를 통해서 클라이언트에 응답을 전송한 뒤에 실행된다. 컨트롤러가 처리하는 도중이나 뷰를 생성하는 과정에서 예외가 발생하더라도 afterCompletion 메소드는 실행된다.
10) 뷰 영역
* ViewResolver 설정
스프링 컨트롤러는 뷰에 의존적이지 않다. 컨트롤러가 지정한 뷰 이름으로부터 응답 결과 화면을 생성하는 View 객체는 ViewResolver가 구한다.
* 스프링이 제공하는 주요 ViewResolver 구현 클래스
- InternalResourceViewResolver : 뷰 이름으로부터 JSP 나 Tiles 연동을 위한 View 객체를 리턴한다. ( 기본적으로 많이 사용 )
- VelocityViewResolver : 뷰 이름으로부터 Velocity 연동을 위한 View 객체를 리턴한다.
- BeanNameViewResolver : 뷰 이름과 동일한 이름을 갖는 빈 객체를 View 객체로 사용한다.
- ResourceBundleViewResolver : 뷰 이름과 View 객체간의 매핑 정보를 저장하기 위해 자원파일을 사용한다.
- XmlViewResolver : 뷰 이름과 View 객체 간의 매핑정보를 저장하기 위해 XML 파일을 사용한다.
* 스프링 스케쥴러 사용 https://blog.naver.com/cyon24/203674742