13.5. Views and resolving them

핸들러(controller)는 요청을 처리 한 뒤 ModelAndView 객체를 넘겨 줍니다. 이 때 이 객체에 view의 이름을 같이 넘겨 주는데 이 이름으로 실제 view를 찾아 주는 역할을 하는 것이 View Resolver입니다.

13.5.1. Resolving views – the ViewResolver interface

Spring 에서 제공하는 다양한 ViewResolver들을 살펴 봅니다.

13.5.2. Chaining ViewResolvers

여러 개의 viewResolver를 사용할 수 있습니다.

13.5.3. Redirecting to views

리다이렉트 하는 방법들이 나와있는데 pass하겠습니당;;

13.4.3. Intercepting requests – the HandlerInterceptor interface

HandlerMapping 안에 interceptor를 만들어서 HandlerMapping에 들어오는 요청들 중에 일부를 처리하는 Handler 하테 요청을 세 가지 시점(넘기기 전과 후, 완료 된 후)에 특정 작업을 추가할 수 있습니다.

레퍼런스에 나와있는 예제는 특정 시간 사이에 들어오는 요청이 아니면 모두 특정 페이지로 요청을 넘겨 버리게 됩니다.

먼저 인터셉터를 만들려면 HandlerInterceptor 인터페이스를 구현해야 합니다.
사용자 삽입 이미지구현해야 할 메소드는 세 개이며 저 중에서 원 하는 것만 구현해서 사용할 수 있도록 어댑터 클레스를 제공합니다.

package samples;

public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {

    private int openingTime;
    private int closingTime;

    public void setOpeningTime(int openingTime) {
        this.openingTime = openingTime;
    }

    public void setClosingTime(int closingTime) {
        this.closingTime = closingTime;
    }

    public boolean preHandle(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler) throws Exception {

        Calendar cal = Calendar.getInstance();
        int hour = cal.get(HOUR_OF_DAY);
        if (openingTime <= hour < closingTime) {
            return true;
        } else {
            response.sendRedirect(“http://host.com/outsideOfficeHours.html”);
            return false;
        }
    }
}

예제에서는 이 어댑터를 사용하여 구현하였으며 preHandler 메소드를 사용하여 요청을 핸들러에게 넘기기 전에 작업을 합니다. 작업의 내용은 현재 요청이 들어온 시간이 특정 시간 사이라면 true, 아니면 http://host.com/outsideOfficeHours.html 여기로 요청을 넘기고 false를 리턴합니다.

인터셉터를 사용하려면 HandlerMapping의 peopertie에 등록해 주면 됩니다.

<beans>
    <bean id=”handlerMapping”
          class=”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping”>
        <property name=”interceptors”>
            <list>
                <ref bean=”officeHoursInterceptor”/>
            </list>
        </property>
        <property name=”mappings”>
            <value>
                /*.form=editAccountFormController
                /*.view=editAccountFormController
            </value>
        </property>
    </bean>

    <bean id=”officeHoursInterceptor”
          class=”samples.TimeBasedAccessInterceptor”>
        <property name=”openingTime” value=”9″/>
        <property name=”closingTime” value=”18″/>
    </bean>
<beans>

요청을 처리하기 전에 로그인 되어 있는지 확인하는 로직을 이렇게 구현해도 될 것 같네요.

13.4.2. SimpleUrlHandlerMapping

이 HandlerMapping은 Ant 스타일의 path 매칭(특수 문자 사용을 말하는 듯 합니다.)을 사용할 수 있습니다.

<web-app>
    …
    <servlet>
        <servlet-name>sample</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!– maps the sample dispatcher to *.form –>
    <servlet-mapping>
        <servlet-name>sample</servlet-name>
        <url-pattern>*.form</url-pattern>
    </servlet-mapping>

    <!– maps the sample dispatcher to *.html –>
    <servlet-mapping>
        <servlet-name>sample</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    …
</web-app>

위와 같이 설정해두면 .form 과 .html 로 끝나는 request들을 sample-context.xml 이라는 DispatcherServlet에서 처리하게 됩니다.

<beans>
       
    <!– no ‘id’ required, HandlerMapping beans are automatically detected by the DispatcherServlet –>
    <bean class=”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping“>
        <property name=”mappings”>
            <value>
                /*/account.form=editAccountFormController
                /*/editaccount.form=editAccountFormController
                /ex/view*.html=helpController
                /**/help.html=helpController
            </value>
        </property>
    </bean>

    <bean id=”helpController”
          class=”org.springframework.web.servlet.mvc.UrlFilenameViewController”/>

    <bean id=”editAccountFormController”
          class=”org.springframework.web.servlet.mvc.SimpleFormController”>
        <property name=”formView” value=”account”/>
        <property name=”successView” value=”account-created”/>
        <property name=”commandName” value=”Account”/>
        <property name=”commandClass” value=”samples.Account”/>
    </bean>
<beans>

위의 설정 파일에서 ant 스타일의 문자를 사용하여 다수의 요청을 하나의 특정 컨트롤러에서 처리하도록 설정하였습니다.

ant 스타일에 대한 내용은 여기서 확인할 수 있습니다.

  • ? 는 문자 하나에 매칭하고
  • * 는 빵개에서 여러개의 문자에 매칭하고
  • ** 는 빵개에서 여러개의 디렉토리에 매칭합니다.

사용자 삽입 이미지

13.4.1. BeanNameUrlHandlerMapping

전달 받은 Request를 application context에 정의해둔 특정 bean의 이름으로 매핑시킵니다.

예를 들어 http://samples.com/editaccount.form 이라는 request를 처리할 컨트롤러를 다음과 같이 등록할 수 있습니다.

<beans>
  <bean id=”handlerMapping” class=”org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping“/>

  <bean name=”/editaccount.form” class=”org.springframework.web.servlet.mvc.SimpleFormController”>
    <property name=”formView” value=”account”/>
    <property name=”successView” value=”account-created”/>
    <property name=”commandName” value=”account”/>
    <property name=”commandClass” value=”samples.Account”/>
  </bean>
<beans>

formView 는 입력받을 view 이름이고 seccessView 는 성공적으로 폼에서 객체를 입력받은 뒤 submit 할 view 이름이고 commandName은 폼에서 입력받은 값들을 바인딩할 객체이며 그 객체의 타입은 commandClass에서 지정합니다.

~~~.form 로 끝나는 요청을 distpacher에서 받아서 위에 설정해둔 handler Mapping을 사용하도록 하려면 다음과 같이 web.xml에 설정해야 합니다.

<web-app>
    …
    <servlet>
        <servlet-name>sample</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

   <!– maps the sample dispatcher to *.form –>
    <servlet-mapping>
        <servlet-name>sample</servlet-name>
        <url-pattern>*.form</url-pattern>
    </servlet-mapping>
    …
</web-app>

BeanNameUrlHandlerMapping을 사용할 때는 굳이 위의 예제 처럼 bean으로 등록해줄 필요가 없습니다. 아무런 HandlerMapping도 등록되어 있지 않다면 Dispatcher가 알아서 BeanNameUrlHandlerMapping을 만들어서 사용하기 때문입니다.

사용자 삽입 이미지

13.4. Handler mappings

사용자 삽입 이미지
DispatcherServlet 과의 관계를 나타내면 다음과 위와 같이 여러 개의 handlerMapping 객체를 List 형태로 가지고 있습니다. Dispatcher Servlet은 위 그림에 표현하지 않은 다른 객체들도 여럿 가지고 있습니다.

저 중에 딱 Dispatcher Servelt과 HandlerMapping 하나와의 관계를 보겠습니다.
사용자 삽입 이미지요청을 처리할 커맨드 객체를 찾아서 HandlerExecutionChain 객체에 감싸서 넘겨 줍니다.

HandlerMapping들의 상위 클레스인 AbstractHandlerMapping 과 하위 클레스들의 관계를 살펴보겠습니다.
사용자 삽입 이미지위와 같은 상속구조를 가지며 HandlerMapping을 아무것도 설정하지 않을 떄는 BeanNameUrlHandlerMapping을 사용합니다.

AbstractHandlerMapping에서 설정할 수 있는 설정들은 다음과 같습니다.
interceptors :: 사용할 인터셉터의 리스트
defaultHandler :: 매칭 하는 핸들러 없을 때 사용할 기본 핸들러
order: 여러 개의 HandlerMapping이 있을 때 그것 들 중에서 어떤 걸로 먼저 핸들러 찾을 지 설정할 때 사용
alwaysUseFullPath :: true로 설정하면 request의 전체 경로를 사용하고, false면 현재 servlet이 매핑 되어 있는 위치에서 부터 경로를 사용합니다.
urlPathHelpe :r: 보통 기본 값 그대로 둔다고 하네요.(using this property, you can
tweak the UrlPathHelper used when inspecting URLs)
urlDecode :: 기본값은 false입니다. hadler mapping에서 적당한 핸들러를 찾기 전에 Request URL을 디코딩 하고 싶을 때 ture로 설정합니다.
lazyInitHandlers :: 싱글톤 스콥의 핸들러들의 객체를 필요로 할 때 생성하고 싶을 때 설정합니다.