본문 바로가기

카테고리 없음

뷰 영역 구현

반응형

07. 뷰 영역 구현


01. ViewResolver 설정


 -. 스프링의 컨트롤러는 뷰에 의존적이지 않다. 컨트롤러는 아래 코드와 같이 결과를 생성할 뷰의 이름만 지정한다.

 -. 컨틀롤러가 지정한 뷰 이름으로부터 응답 결과 화면을 생성하는 View 객체는 ViewResolver가 구한다.


@Controller

public class HelloControoler {


      @RequestMapping("/hello.do")

      public String hello( ) {


            // 처리결과를 뷰 이름 지정

           return "hello";

     }

}


※ 스프링이 제공하는 주요 ViewResolver 구현 클래스

ViewResolver 구현 클래스

설명

 InternalResourceViewResolver

 -. 뷰이름으로부터 JSP나 tILES 연동을 위한 View 객체를 리턴한다.

 VelocityViewResolver

 -. 뷰 이름으로부터 Velocity 연동을 위한 View 객체를 리턴한다.

 VelocityLayoutVioewResolver

 -. VelocityViewResolver와 동일한 기능을 제공하며,

    추가로 vELOCITY의 레이아웃 기능을 제공한다.

 BeanNameViewResolver

 -. 뷰 이름과 동일한 이름을 갖는 빈 객체를 View 객체로 사용한다.

 ResourceBundleViewResolver

 -. 뷰 이름과 View 객체간의 매핑 정보를 저장하기 위해 자원파일을 사용한다.

 XmlViewResolver

 -. 뷰 이름과 View 객체간의 매핑 정보를 저장하기 위해 XML 파일을 사용한다.




1.1 ViewResolver 인터페이스

package org.springframework.web.servlet;


import java.util.Locale;


public interface ViewResolver {

     

      View resolveViewName(String viewName, Locale locale) throws Exception;

}

 -. ViewResolver는 뷰 이름과 지역화를 위한 Locale을 파라미터로 전달받으며, 매핑되는 View 객체를 리턴한다.

 -. 매핑되는 View 객체가 존재하지 않으면 null을 리턴한다.




1.2 View 객체

 -. View Resolver는 응답 결과를 생성할 뷰 객체를 리턴한다.

 -. 모든 뷰 클래스는 View 인터페이스를 구현하고 있으며, View 인터페잇느느 다음과 같이 정의되어 있다.

package org.springframework.web.servlet;


import java.util.Map;


import javax.ervlet.httpHttpServletRequest;

import javax.ervlet.httpHttpServletResponse;


public interface View {


      String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";


      String getContentType();

      void render(Map<String, ?> model. HttpServletRequest request, HttpServletResponse response) throws Exception;

}

 -. getContentType() 메서드는 "text/html"과 응답 결과의 컨텐트 타입을 리턴한다.

 -. render() 메서드는 실제로 응답 결과를 생성한다.

 -. render() 메서드의 첫 번째 파라미터인 model에는 컨트롤러가 리턴한 ModelAndView 객체의 모델 데이터가 전달된다.

 -. rkrrkdml View 객체는 이 모델 데이터로부터 응답 결과를 생성하는 데 필요한 정보를 구현한다.



1.3 InternalResourceViewResolver 설정

 -. InternalResourceViewResolver 클래스는 JSP나 HTML 파일과 같이 웹 어플리케이션의 내부 자원을 이용하여 뷰를 생성하는

    AbstractUrlBasedView 타입 뷰 객체를 리턴한다.

 -. 기본적으로 사용하는 View 클래스는 InternalResourceView 클래스이다.


<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      p:prefix="/WEB-INF/viewjsp/" p:suffix=".jsp" />
</bean> 


 -. InternalResourceViewResolver는 컨트롤러가 지정한 뷰 이름으로부터 실제로 사용될 뷰를 선택한다.

    이 때 컨틀로러가 지정한 뷰 이름 앞뒤로 prefix 프로퍼티와 suffix 프로퍼티를 추가한 값이 실제로 사용될 자원의 경로가 된다.

ModelAndView = new ModelAndView("hello");

return mav; 


 -. 기본적으로 사용되는 InternalResourceView 클래스는 단순히 지정한 자원 경로로(일반적으로 JSP) 요청을 전달한다.

 -. 만약, 스프링의 국제화 관련 정보를 JSTL에서 사용하고 싶다면, 다음과 같이 JstlView 클래스를 viewClass 프로퍼티로 지정하면 된다.

<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      p:viewClass="org.springframework.web.servlet.view.JstlView"

      p:prefix="/WEB-INF/viewjsp/"

      p:suffix=".jsp" />



1.4 BeanNameViewResolver 설정

 -. BeanNameViewResolver 클래스는 뷰 이름과 동일한 이름을 갖는 빈을 뷰 객체로 사용한다.

 -. BeanNameViewResolver는 주로 커스텀 View를 뷰로 사용해야 하는 경우에 사용된다.


ex) 파일 다운로드를 위한 정보를 읽어와 뷰에 전달하는 컨트롤러가 다운로드 관련 정보를 뷰에 전달하는 예

@Controller
public class DownloadController {

      @RequestMapping("/downlowd.do")

      public ModelAndView download(HttpServletRequest request, HttpServletResponse response) {


            File downloadFile = getFile(request);

            return new ModelAndView("download", "downloadFile", downloadFile);

      }
      …
}


※ 위 결과를 보여 줄 DownloadView 클래스

<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">


<bean id="download"

      class="madvirus.spring.chap07.view.DownloadView"/>



1.5 XmlViewResolver 설정

 -. XmlViewResolver는 BeanNameViewResolver와 마찬가지로 뷰 이름과 동일한 이름을 갖는 빈을 뷰 객체로 사용한다.

 -. 차이점은 XmlViewResolver는 별도의 XML 설정 파일로부터 빈 객체를 검색한다는 것이다.

 -. XmlViewResolver는 다음과 같이 location 프로퍼티를 이용하여 외부 설정 파일의 경로를 지정한다.

<bean id="viewResolver"

      class="org.springframework.web.servlet.view.XmlViewResolver"

      p:location="/WEB-INF/nonHtml-view.xml"/>


 -. location 프로퍼티의 값을 지정하지 않을 경우 기본값은 "/WEB-INF/nonHtml-view.xml"이다.

 -. location 프로퍼티에 지정하는 파일은 스프링 XML 설정 파일이다.



1.6 ResourceBundleViewResolver 설정

 -. ResourceBundleViewResolve는 리소스 번들(프로퍼티 파일)로부터 뷰 이름과 매핑되는 View 클래스를 구한다.

 -. ResourceBundleViewResolver는 ResourceBundleMessageSource와 마찬가지로 basename 프로퍼티나 basenames 프로퍼티를 이용해서

    뷰 클래스 정보를 저장한 리소스 경로를 명시한다.

<bean id ="viewResolve"

      class="org.springframework.web.servlet.view.ResourceBundleViewResolver"

      p:basename="views/views" />


 -. 위 설정에서 ResourceBundleViewResolve는 "views/views" 리소스로부터 설정 정보를 로딩한다.

    즉, "views/views.properties", "views/views_en.properties"등 Locale에 따라 알맞은 프로퍼티 파일로부터 뷰 매핑 정보를 로딩하게 된다.

 -. 프로퍼티 파일은 다음과 같이 "뷰이름.class=뷰클래스' 형태로 뷰 이름과 뷰 클래스 사이의 매핑을 입력한다.

download.class = madvirus.spring.chap07.view.DownloadView



1.7 다수의 ViewResolver 설정하기

 -. 하나의 DispatcherServlet은 한 개 이상의 ViewResolver를 설정할 수 있도록 하고 있다.

 -. 다수의 ViewResolver를 설정한 경우 "order" 프로퍼티를 이용하여 뷰 이름을 검사할 ViewResolver의 순서를 결정할 수 있다.

 -. "order" 프로퍼티의 값이 작은 ViewResolver가 높은 우선순위를 갖는다.

     만약, 우선순위를 명시하지 않으면 Integer.MAX_VALUE를 "order"프로퍼티의 값으로 갖는다.

     (즉, 가장 낮은 우선순위를 갖게 된다.)

 -. ViewResolver 구현 클래스가 org.springframework.core.Ordered 인터페이스를 구현하지 않은 경우,

    해당 ViewResolver 구현 클래스도 가장 낮은 우선순위를 갖게 된다. 

    하지만 스프링이 제공하는 ViewResolver 구현 클래스는 모든 Ordered 인터페이스를 구현하고 있다.

 -. DispatcherServlet은 "order" 프로퍼티의 값이 작은, 즉, 우선순위가 높은 ViewResolver에게 뷰 객체를 요청한다.

    만약, 우선순위가 높은 ViewResolve가 null을 리턴하면, 그 다음 우선순위를 갖는 ViewResolver에 뷰를 요청한다.

    뷰 객체를구하면, 해당 뷰 객체를 이용하여 응답 결과를 생성한다.


ex) "order" 프로퍼티의 설정 예

<bean

      class="org.springframework.web.servlet.view.XmlViewResolver"

      p:location="/WEB-INF/nonHtml-view.xml"

      p:order="0" />


<bean

      class="org.springframework.webservlet.view.InternalResourceViewResolver"

      p"prefix="/WEB-INF/viewjsp/" p:suffix="/jsp"

      p:order="1" />




02. HTML 특수 문자 처리 방식 설정

 -. 스프링은 각 뷰 기술과 관련하여 메시지나 커맨드 객체의 값을 출력할 수 있는 기능을 제공하고 있다.

 -. JSP를 뷰 기술로 사용할 경우 다음의 커스텀 태그를 이용해서 메시지를 출력할 수 있다.

<title><spring:message code="login.form.title" /></title>


 -. 만약 위 커스텀 태그가 출력하는 값이 '<입력폼>'이라고 하자. 이때, '<'와 '>'는 HTML에서 특수문자이기 때문에

    '&lt;'나 '&gt;'와 같은 엔티티 레퍼런스로 변환해 주어야 원하는 값이 출력된다.

 -. 스프링은 이런 특수문자를 어떻게 처리할 지의 여부를 defaultHtmlEscape 컨텍스트 파라미터를 통해서 지정할 수 있다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <context-param>
        <param-name>defaultHtmlEscape</param-name>
        <param-value>false</param-value>
    </context-param>
    …
</web-app>


 -. defaultHtmlEscape 컨텍스트 파리미터의 값을 true로 지정하면 스프링이 제공하는 커스텀 태그나 Volcity 매크로는

    HTML의 특수 문자를 엔티티 레퍼런스로 치환한다.

 -. 반면에 defaultHtmlEscape 컨텍스트 파라미터의 값이 false이면, 특수 문자를 그대로 출력한다.


 ※ defaultHtmlEscape 컨텍스트 파라미터의 기본 값은 true 이다.




03. JSP를 이용한 뷰 구현

 -. JSP를 뷰로 사용하려면 InternalResourceViewResolver를 사용하면 된다.

 -. 아래 코드와 같이 suffix 프로퍼티의 값을 .jsp로 지정함으로써 논리적 뷰 이름을 특정 JSP에 매핑할 수 있다.

<bean id ="viewResolve"

      class="org.springframework.web.servlet.view.ResourceBundleViewResolver"

      p:prefix="/WEB-INF/viewjsp/" p:suffix=".jsp" />

</bean>



3.1 메시지를 출력을 위한 <spring:message>커스텀 태그

 -. 스프링은 MessageSource로부터 메시지를 가져와 출력해주는 <spring:message> 커스텀 태그를 제공하고 있다.

 -. <spring:message> 커스텀 태그는 다음과 같이 code 속성을 이용하여 읽어 올 메시지의 코드를 지정한다.

<%@ page contentType="text/html; charset=EUC-KR" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title><spring:message code="login.form.title"/></title>
</head>
<body>
<form:form commandName="login">
<form:errors />
<p>
    <label for="loginType"><spring:message code="login.form.type" /></label>
    <form:select path="loginType" items="${loginTypes}" />
</p>

<p>
    <input type="submit" value="<spring:message code="login.form.submit" />">
</p>
</form:form>
</body>
</html>


 -. 위 코드에서 사용되는 메시지를 포함하고 있는 메시지 리소스 파일은 다음과 같이 각 코드 값에 해당되는 메시지를 설정하고 있다.

login.form.title=Login FORM
login.form.type=Login Type

login.form.id=ID

login.form.password=Password

login.form.submit=Login


 -. 메시지 리소스 파일은 {n} 형식을 잉요하여 변하는 부분을 명시할 수 있다.

greeting=\uc804 {0}\uc785\ub2c8\ub2e4.{1}


 -. <spring:message> 태그는 주어진 코드에 해당하는 메시지가 존재하지 않는다면 예외를 발생시킨다.

 -. 코드에 해당하는 메시지가 없을 때 예외를 발생시키는 대신 지정한 메시지를 출력하고 싶다면 text 속성에 기본 메시지를 입력하면 된다.

<spring:message code="no_code" text="코드가 없습니다." />


 -. <spring:message> 태그가 생성한 메시지를 출력하지 않고 request나 session과 같은 기본 객체의 속성에 저장할 수도 있다.

<spring:message code="login.form.password" var="label" scope="request"/>

${label}: <input type=.../>



3.2 스프링이 제공하는 폼 관련 커스텀 태그

 -. 스프링의 장점 중 하나는 입력 폼 값을 커맨드 객체에 저장하는 기능을 제공한다는 것이다.

 -. 스프링은 또한 반대로 커맨드 객체의 값을 입력 폼에 출력해 주는 JSP 커스텀 태그를 제공하고 있어, 좀 더 쉽게 폼 관련 태그를 생성할 수 있도록 도와준다.

 -. 스프링이 제공하는 폼 관련 커스텀 태그를 사용하려면 다음과 같이 커스텀 태그를 설정해 주어야 한다.

<%@ taglib prefix="for" uri="http://www.springframework.org/tags/form" %>


(1) <form> 태그를 위한 커스텀 태그 : <form:form>

 -. <form:form> 커스텀 태그는 <form> 태그를 생성할 때 사용된다. <form:for> 커스텀 태그를 사용하는 가장 간단한 방법은 다음과 같다.

<form:form>
<<form:errors />
<p>
      <label for="loginType"><spring:message code="login.form.type" /></label>

      <form:select path="loginType" items="${loginTypes}" />

</p>


<p>

      <input type="submit" value="<spring:message code="login.form.submit" />>

</p>

<form:form>


 -. <form:form>태그의 method 속성과 action 속성을 표시하지 않으면 method 속성의 값은 "post"로 설정되고

    action 속성의 값은 현재 요청 URL값이 설정된다.

<<form id="command" action="/chap07/login/login.do" method="post">

      …

</form>


 -. 생성된 <form> 태그의 id 속성은 입력 폼의 값을 저장하는 커맨드 객체의 이름이 할당된다.

 -. 커댄드 객체의 이름이 기본값인 "command"가 아니라면 다음과 같이 com-mandName 속성에 커맨드 객체의 이름을 명시해 주어야 한다.

<form:form commandName="login">
      …
</form:form>


 -. <form:form> 커스텀 태그는 <form> 태그와 관련하여 다음의 속성들을 추가적으로 제공하고 있다.

   ◎ action - 폼 데이터를 정송할 URL을 입력(HTML <form> 태그 속성)

   ◎ enctype - 전송될 데이터의 인코딩 타입, HTML<form> 태그 속성과 동일

   ◎ method - 전송 방식, HTML<form> 태그 속성과 동일


 -. <form:form> 택의 몸체에는 <input> 태그나 <select> 태그와 같이 입력 폼을 출력하는 데 필요한 HTML 태그를 입력할 수 있다.

    이때 입력한 값이 잘못 되어 다시 값을 입력해야 하는 경우에는 다음과 같이 커맨드 객체의 값을 사용해서 이전에 입력한 값을 출력한다.

<form:form>

      …

      <input type="text" name="id" value="${login.id}"/>

      …

</form:form>


(2) <input>태그를 위한 커스텀 태그 : <form:input>, <form:password>, <form:hidden>

 -. <input> 태그와 관련도니 기본 커스텀 태그

커스텀 태그

설명

 <form:input>

 -. text 타입의 <input> 태그

 <form:password>

 -. password 타입의 <input> 태그

 <form:hidden>
 -. hidden 타입의 <intput> 태그



(3) <select> 태그를 위한 커스텀 태그 : <form:select>, <form:options>, <form:option>

 -. <select> 태그와 관련된 커스텀 태그

커스텀 태그

설명

 <form:select>

 -. <select> 태그를 생성한다.

 -. <option> 태그를 생성하는 데 필요한 콜렉션을 전달받을 수도 있다.

 <form:options>

 -. 지정한 콜렉션 객체를 이용하여 <opton> 태그를 생성한다.

 <form:option>

 -. 한 개의 <option> 태그를 생성한다.


(4) checkbox 타입 <input> 태그를 위한 커스텀 태그 : <form:checkboxes>, <form:checkbox>

 -. 한 개 이상의 값을 커맨드 객체의 특정 프로퍼티에 저장하고 싶은 경우, 배열이나 List와 같은 콜렉션 타입을 사용해서 값을 저장한다.

커스텀 태그

설명

 <form:checkboxes>

 -. 커맨드 객체의 특정 프로퍼티와 관련된 checkbox 타입의 <input> 태그 목록을 생성한다.

 <form:checkbox>

 -. 커맨드 객체의 특정 프로퍼티와 관련도니 한 개의 checkbox 타입 <input> 태그를 생성한다.


(5) radio 타입 <input> 태그를 위한 커스텀 태그 : <form:radiobuttons>, <form:radiobutton>

 -. 여러 가지 옵션 중에서 한가지를 선택해야 하는 경우, radio 타입의 <input> 태그를 사용한다.

커스텀 태그

설명

 <form:radiobuttons>

 -. 커맨드 객체의 특정 프로퍼티와 관련도니 radio 타입의 <input> 태그 목록을 생성한다.

 <form:radiobutton>

 -. 커맨드 객체의 특정 프로퍼티와 관련된 한 개의 radio 타입 <input> 태그를 생성한다.


(6) <TEXTAREA> 태그를 위한 커스텀 태그 : <from:textarea>

 -. 게시글 내용과 같이 여러 줄을 입력 받아야 하는 경우 <textarea> 태그를 사용한다.

 -. 스프링은 <form:textarea> 커스텀 태그를 제공하고 있으며, 이 태그를 이용하면 커맨드 객체와 관련된 <textarea>태그를 생성할 수 있다.

<p>

      <form:label path="etc">기타</form:label>

      <form:textarea path="etc" cols="20" rows="3" />

</p>


(7) CSS 및 HTML 태그와 관련된 공통 속성

 -. <form:input>, <form:select> 등 스프링이 입력 폼과 관련해서 제공하는 커스텀 태그는 HTML의 CSS 및 이벤트 관련 속성을 제공하고 있다.

  ① CSS와 관련도니 속성

   ◎ cssClass - HTML의 class 속성값

   ◎ cssErrorClass - 폼 검증 에러가 발생했을 때 사용할 HTML의 class 속성 값

   ◎ cssStyle - HTML의 style 속성 값


  ② HTML 태그가 사용하는 속성값

   ◎ id, title, dir

   ◎ disabled, tabindex

   ◎ onfocus, onblur, onchange

   ◎ onclick, ondblclick

   ◎ onkeydown, onkeypress, onkeyup

   ◎ onmousedown, onmousemove, onmouseup

   ◎ onmouseout, onmouseover



3.3 스프링이 제공하는 에러 관련 커스텀 태그

 -. Validator는 아래 코드와 같이 Errors를 이용하여 에러 정보를 저장하였다.

package madvirus.spring.chap07.controller;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class MemberInfoValidator implements Validator {

    @Override
    public void validate(Object target, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userId", "required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address.address1",
                "required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address.address2",
                "required");
        ValidationUtils
                .rejectIfEmptyOrWhitespace(errors, "jobCode", "required");
        MemberInfo memberInfo = (MemberInfo) target;
        if (memberInfo.getFavorites() == null
                || memberInfo.getFavorites().length == 0) {
            errors.rejectValue("favorites", "must_select");
        }
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return MemberInfo.class.isAssignableFrom(clazz);
    }
}


 -. Errors나 BindingResult를 이용해서 에러 정보를 추가한 경우, <form:errors> 커스텀 태그를 이용해서 에러 메시지를 출력할 수 이쑈다.

 -. <form:errors> 커스텀 태그는 path 속성을 이용해서 커맨드 객체의 특정 프로퍼티와 관련된 에러 메시지를 출력할 수 있다.

<form:form commandName="memberInfo">

<p>

      <form:label path="userId">회원 ID</FORM:LABEL>

      <form:input path="userId" />

      <form:errors path="userId" />

</p>



 -. 위 코드의 경우 "userId" 프로퍼티와 관련된 모든 에러 메시지를 출력한다.

    에러 메시지는 에러 코드와 필드 이름, 그리고 커맨드 클래스 이름을 통해서 결정된다.

 -. <form:errors> 커스텀 태그는 지정한 프로퍼티와 관련된 한 개 이상의 에러 메시지를 출력하게 된다.

    각 에러 메시지를 생성할 때 다음과 같은 두 개의 속성이 사용된다.

   ◎ element - 각 에러 메시지를 출력할 때 사용될 HTML 태그, 기본 값은 span 이다.

   ◎ delimiter - 각 에러 메시지를 구분할 때 사용될 HTML 태그, 기본 값은 <br/> 이다.


 ※ element, delimiter 속성의 사용 예

<form:errors path="userId" element="div" delimeter=""/>



3.4 <spring:htmlEscape> 커스텀 태그와 htmlEscape 속성

 -. defaultHtmlEscape 컨텍스트 파라미터를 사용해서 웹 어플리케이션 전반에 걸쳐서 HTML의 특수 문자를 엔티티 레퍼런스로 치환할 지의 여부를 결정하는데,

   만약 각 JSP 페이 별로 특수 문자 치환 여부를 설정해 주고 싶다면 다음과 같이 <spring:htmlEscape> 커스텀 태그를 사용하면 된다.

<%-- JSP 페이지의 앞 부분에서 설정 --%>

<spring:htmlEscape defaultHtmlEscape="true"/>


<spring:message .../>

<form:input .../>



 -. <spring:htmlEscape> 커스텀 태그를 설정하면, 이후로 실행되는 <spring:message> 커스텀 태그나 <form:input> 커스텀 태그와 같이

    스프링이 제공하는 커스텀 태그는 <spring:htmlEscape> 커스텀 태그의 defaultHtmlEscape 속성에서 지정한 값을 기본 값으로 사용한다.



3.5 <form:form>의 RESTful 지원

 -. 스프링 MVC는 HTTP의 GET, POST, PUT, DELETE 방식을 지원하고 있으며, 다음과 같이 컨트롤러 메서드에서 어떤 HTTP 방식을 지원할 지 선택할 수 있다.

package madvirus.spring.chap07.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ArticleController {

    @RequestMapping(value="/article/{id}", method=RequestMethod.GET)
    public String read(@PathVariable("id") Integer id, Model model) {
        model.addAttribute("article", new Article(id));
        return "article/read";
    }
   
    @RequestMapping(value="/article/{id}", method=RequestMethod.DELETE)
    public String delete(@PathVariable("id") Integer id, Model model) {
        model.addAttribute("article", new Article(id));
        return "article/delete";
    }
   
    @RequestMapping(value="/article/{id}", method=RequestMethod.PUT)
    public String modify(@PathVariable("id") Integer id, Model model) {
        model.addAttribute("article", new Article(id));
        return "article/modify";
    }
   
    @RequestMapping(value="/article", method=RequestMethod.POST)
    public String write(Model model) {
        model.addAttribute("article", new Article(2));
        return "article/write";
    }

    @RequestMapping("/articleForm.do")
    public String testForm(Model model) {
        model.addAttribute("article", new Article(1));
        return "article/testForm";
    }
}


 ※ 대부분의 웹 브라우저는 GET 방식과 POST 방식만을 지원하고 있어서 DELETE방식이나 PUT 방식의 요청을 전송 할 수 없기 때문에,

    웹 브라우저를 이용할 경우 GET 방식과 POST 방식으로만 처리 해야 한다.


  스프링 3 버전부터는 PUT과 DELETE 방식을 이용해서 컨트롤러를 구현하면서 웹 브라우저에서도 그대로 해당 컨트롤러를 사용할 수 있도록 지원한다.



04. Tiles 2 연동을 이용한 레이아웃 템플리 처리

 -. 레이아웃이 동일하고 공통된 내용이 들어가는 영역이 많은 경우에는 Tiles와 같은 템플릿 라이브러리를 사용해서 레이아웃을 처리하면

    뷰 관련 코드에서 레이아웃을 처리하기 위한 코드의 중복을 제거할 수 있다는 장점이 있다.

 ※ 스프링은 테플릿 라이브러리인 Tiles 2 버전을 지원하고 있다.



4.1 TilesConfigurer를 이용한 Tiles 2 연동

 -. Tiles 2 연동을 위해서는 다음의 jar 파일을 클래스 패스에 추가해 주어야 한다. (commons-logging.jar의 경우 spring core 모듈에서도 의존하는 모듈이다.)

   ◎ tiles-api2.1x.jar, tiles-core-2.1x.jar, tiles-jsp-2.1x.jar

   ◎ commons-beanutils, commons-digester,jar, commons-logging.jar

   ◎ 스프링 MVC 모듈


 -. 스프링 3에서는 Tiles 2를 연동하기 위해서는 스프링 설정 파일에 다음의 두 가지 내용을 설정한다.

   ◎ TilesConfigurer를 이용하여 Tiles2 레이아웃 설정 파일 명시

   ◎ UrlBasedViewResolver의 viewClass 프로퍼티를 TilesView로 지정


ex) 설정 예

 -. TilesConfigurer 클래스와 TilesView 클래스는 모두 tiles2 패키지에 포함된 클래스임에 유의해야 한다.

<bean id="tilesConfigurer"
      class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
      <property name="definitions">
            <list>
                  <value>/WEB-INF/tiles2def/tilesdef.xml</value>
            </list>
      </property>
</bean>

<bean id="viewResolver"
      class="org.springframework.web.servlet.view.UrlBasedViewResolver">
      <property name="viewClass"
      value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>


 -. TilesConfigurer 빈 객체는 definitions 속성을 이용해서 Tiles 설정 파일 목록을 전달받는다.

 -. Tiles 2 설정 파일은 다음과 같이 작성된다.

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

<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">

<tiles-definitions>

    <definition name="base_layout"
        template="/WEB-INF/viewtiles2/template/layout.jsp"
        preparer="menuPreparer">
        <put-attribute name="header"
            value="/WEB-INF/viewtiles2/template/header.jsp" />
        <put-attribute name="footer"
            value="/WEB-INF/viewtiles2/template/footer.jsp" />
    </definition>

    <definition name="loginForm" extends="base_layout">
        <put-attribute name="title" value="로그인폼" />
        <put-attribute name="body"
            value="/WEB-INF/viewtiles2/loginForm.jsp" />
    </definition>

    <definition name="loginSuccess" extends="base_layout">
        <put-attribute name="title" value="로그인 성공" />
        <put-attribute name="body"
            value="/WEB-INF/viewtiles2/loginSuccess.jsp" />
    </definition>


      …


</tiles-definitions>


 -. TilesView 클래스는 컨틀롤러가 지정한 뷰 이름과 동일한 이름을 갖는 <definition> 태그를 사용하여 뷰를 생성한다.

   예를 들어, 컨트롤러가 다음과 같이 뷰 이름을 "loginForm"으로 지정했다고 하자.

@Controller
@RequestMapping("/login/login.do")
public class LoginController {

      @RequestMapping(method = RequestMethod.GET)
      public String form() {


            return "loginForm";
      }

      …

}


 -. 이 경우 name 속성의 값이 "loginForm"인 <definition> 태그의 정보를 사용한다.

    즉, 앞서 Tiles 설정 내용에서 봤듯이 layout.jsp를 레이아웃 템플릿으로 사용하고, 헤더와 푸터에 각각 header.jsp와 footer.jsp를 삽입하고,

    그리고 내용에는 loginForm.jsp를 사용하게 된다.

 -. 레이아웃 템플릿 파일은 다음과 같이 Tiles2가 제공하는 커스템 태그를 이용하여 레이아웃 정보를 설정한다.

<%@ page contentType="text/html; charset=EUC-KR"%>
<%@ taglib uri="http://www.apache.org/tags-tiles" prefix="tiles"%>
<html>
<head>

<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">

<title><tiles:getAsString name="title" /></title>
</head>
<body>

<tiles:insertAttribute name="header" />

<hr/>

<tiles:insertAttribute name="body" />

<hr/>

<tiles:insertAttribute name="footer" />

</body>
</html>




4.2 스프링 빈을 ViewPreparer로 사용하기

 -. 메뉴 목록처럼 모든 뷰 코드에서 공통으로 사용되는 데이터가 존재할 경우 Tiles에서는 ViewPreparer를 잉요한다.

 -. 보통 ViewPreparer는 아래 코드와 같이 Tiles 설정 파일에 완전한 클래스 이름을 적는 것이 보통이다.

<tiles-definitions>

    <definition name="base_layout"
        template="/WEB-INF/viewtiles2/template/layout.jsp"

        preparer="madvirus.spring.chap07.tiles2.MenuPreparer">
        <put-attribute name="header"
            value="/WEB-INF/viewtiles2/template/header.jsp" />
        <put-attribute name="footer"
            value="/WEB-INF/viewtiles2/template/footer.jsp" />
    </definition>

      …

</tiles-definitions>


 -. 그런데 이 경우 스프링이 제공하는 DI/AOP를 ViewPreparer에 적용할 수 없는 단점이 있다.

   그래서 스프링 MVC는 Tiles의 ViewPreparer로 스프링 빈을 사용할 수 있는 기능을 제공하고 있다.

   스프링 빈을 Tiles의 ViewPreparer로 사용하려면 다음의 세가지만 설정하면 된다.

   ◎ TilesConfigurer 설정에 preparerFactoryClass 프로퍼티 설정 추가

   ◎ ViewPreparer로 사용할 스프링 빈 등록

   ◎ Tiles 설정 파일의 preparer 속성에 스프링 빈 이름 등록


 -. 먼저 아래 코드와 같이 TilesConfigurer의 preparerFactoryClass 프로퍼티의 값으로 SpringBeanPreparerFactory 클래스의 완전한 이름을 설정하고,

    ViewPreparer로 사용할 클래스를 스프링 빈으로 등록한다.

<bean id="tilesConfigurer"
      class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
      <property name="definitions">
            <list>
                  <value>/WEB-INF/tiles2def/tilesdef.xml</value>
            </list>
      </property>
      <property name="preparerFactoryClass"
            value="org.springframework.web.servlet.view.tiles2.SpringBeanPreparerFactory" />
</bean>

<bean id="menuPreparer" class="madvirus.spring.chap07.tiles2.MenuPreparer" />


 -. Tiles 설정 파일에서는 다음과 같이 <definition> 태그의 preparer 속성의 값으로 앞서 설정한 스프링 빈의 이름을 지정하면,

    ViewPreparer로 해당 빈을 사용하게 된다.

<tiles-definitions>

    <definition name="base_layout"
        template="/WEB-INF/viewtiles2/template/layout.jsp"
        preparer="menuPreparer">
        <put-attribute name="header"
            value="/WEB-INF/viewtiles2/template/header.jsp" />
        <put-attribute name="footer"
            value="/WEB-INF/viewtiles2/template/footer.jsp" />
    </definition>

      …
</tiles-definitions>




05. Velocity를 이용한 뷰 구현

 -. JSP와 더불어 뷰를 생성하는 데 많이 사용되는 기술 중의 하나가 Velocity이다.

 -. Velocity는 템플릿 엔진으로서 템플릿 파일을 비교적 쉽게 작성할 수 있고, 자바 객체를 템플릿 파일에서 사용할 수 있기 때문에

    생성하는 데 JSP 만큼 널리 사용되고 있다.

 -. Velocity를 뷰로 사용하려면 다음의 Velocity 관련 jar 파일을 추가해 주어야 한다.

   ◎ Velocity 1.5

   ◎ Velocity Tools View 1.4



5.1 VelocityViewResolver와 VelocityConfigurer를 이용한 Velocity 연동

 -. 스프링에서 Velocity를 연동할 때에는 VelocityViewResolver를 사용하면 된다.

 -. VelocityViewResolver는 Velocity 템플릿 파일을 이용해서 뷰를 생성하며 VelocityConfigurer를 사용해서 Velocity와 관련된 설정 정보를 입력한다.

<bean id="velocityEngine"
      class="org.springframework.ui.velocity.VelocityConfigurer">
      <property name="resourceLoaderPath" value="/WEB-INF/viewvm/" />

      <property name="velocityProperties">

            <props>
                  <prop key="input.encoding">EUC-KR</prop>
                  <prop key="output.encoding">EUC-KR</prop>
            </props>
      </property>
</bean>

<bean id="viewResolver"

      class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"

      p:suffix=".vm"

      p:contentType="text/html; charset=EUC-KR"/>
</bean>

 -. VelocityConfigurer의 resourceLoaderPath 프로퍼티는 템플릿 파일을 로딩할 경로를 입력하며,

    velocityProperties 프로퍼티는 velocityProperties 프로퍼티는 Velocity 설정 정보를 지정한다.

 -. VelocityViewResolver는 VelocityConfigurer에서 설정한 경로에서 템플릿 파일을 읽어 와 뷰를 생성한다.

    prefix 프로퍼티와 suffix 프로퍼티는 뷰 이름으로부터 템플릿 파일의 경롤르 생성할 때 사용된다.

 -. 템플릿 파일에서는 컨트롤러에서 생성한 모델 정보를 사용할 수 있다.

package madvirus.spring.chap07.controller;

import java.util.Calendar;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

    @RequestMapping("/hello.do")
    public ModelAndView hello() {
        ModelAndView mav = new ModelAndView();
        mav.setViewName("hello");
        mav.addObject("greeting", getGreeting());
        return mav;
    }

    private String getGreeting() {
        int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
        if (hour >= 6 && hour <= 10) {
            return "좋은 아침입니다.";
        } else if (hour >= 12 && hour <= 15) {
            return "점심 식사는 하셨나요?";
        } else if (hour >= 18 && hour <= 22) {
            return "좋은 밤 되세요";
        }
        return "안녕하세요";
    }
}


 -. 이 경우 템플릿 파일에서는 다음과 같이 모델의 이름을 사용해서 객체를 사용할 수 있다.

<html>
<head>

<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">

<title>인사</title>

</head>

<body>

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

</body>

</html>


(1) Velocity Tools의 NumberTool과 DateTool 설정

 -. Velocity는 템플릿 파일에서 유용하게 사용할 수 있는 다양한 툴을 구현한 VelocityTools를 제공한다.

 -. 이 툴에는 숫자와 날짜를 형식에 맞게 출력해 주는 NumberTool과 DateTool을 제공하고 있다.

 -. VelocityViewResolver는 이 두 가지 툴을 템플릿 파일에서 접근할 때 사용할 이름을 지정할 수 있는 프로퍼티를 제공하고 있다.

   ◎ dateToolAttribute - DateTool에 접근할 때 사용할 변수명을 지정

   ◎ numberToolAttribute - NumberTool에 접근할 때 사용할 변수명을 지정


(2) request 및 session 속성 템플릿 파일에서 사용하기

 -. VelocityViewResolver는 모델에 저장도니 모델 데이터만 뷰에 전달한다. 하지만, 경우에 따라서 request 객체나 session 객체에 저장된 속성(attribute)을

   템플릿에 접근하고 싶을 때가 있다. 예를 들어, session 객체[에 현재 로그인 한 사용자 정보가 저장되어 있는 경우를 생각해 보자. 템플릿 파일에서 session

   객체의 속성에 접근 할 수 없다면, 다음과 같이 컨트롤러에서 일일이 필요한 값을 모델 객체에 추가해 주어야 한다.

  -. 이러한 불편함을 없애기 위해, VelocityViewResolver는 다음의 두 프로퍼티의 값을 true로 지정함으로써

    request나 session의 속성을 템플릿에 전달 할 수 있도록 하고 있다.

   ◎ exposeRequestAttributes

   ◎ exposeSessionAttributes



5.2 스프링이 제공하는 Velocity 매크로

 -. 스프링이 JSP를 위한 다양한 커스텀 태그를 제공하고 있듯이, Velocity를 위한 매크로도 제공하고 있다.

 -. 커스텀 태그와 마찬가지로 입력 폼을 위한 HTML 태그와 에러 메시지 등을 생성할 때 매크로를 유용하게 사용할 수 있다.


(1) 메시지 출력을 위한 #springMessage와 #springMessageText 매크로

   ◎ #springMessage(code) - 코드에 해당하는 메시지를 출력한다, 메시지가 존재하지 않을 경우 예외가 발생한다.

   ◎ #springMessageText(code text) - 코드에 해당하는 메시지를 출력한다. 메시지가 존재하지 않을 경우 text를 출력한다.


(2) 커맨드 객체 연동을 위한 #springBind 매크로와 #springBindEscaped 매크로

 -. #springBind 매크로는 커맨드 객체와 관련도니 BindStratus 정보를 설정한다.

 -. #springBind 매크로는 다음과 같이 커맨드 객체와 관련도니 경로를 설정하며, 관련 BindStatus 정보는 status 변수를 통해서 접근한다.

<p>

      <label for="id">#springMessage("login.form.id")</label>

      #springBind("login.id")

      <input type="text" name="${status.expression}" id"=${status.expression}"

            value="$!status.value" />

      #springShowErrors("<br/>" "")

</p>


 -. HTML 특수 문자 처리 여부를 직접 명시하고 싶다면, 다음과 같이 #springBindEscaped 매크로를 사용하면 된다.

<p>

      <label for="id">#springMessage("login.form.id")</label>

      #springBindEscaped("login.id", false)

      <input type="text" name="${status.expression}" id"=${status.expression}"

            value="$!status.value" />

      #springShowErrors("<br/>" "")

</p>


(3) <input> 태그를 위한 매크로

 -. #springFromInput 매크로를 사용하면 커맨드 객체와 관련된 <input> 태그를 손쉽게 출력할 수 있다.

   ◎ #springMessage($path $attributes)


 -. hidden 타입의 <input> 태그나 password 타입의 <input> 태그를 생성하고 싶다면, 다음의 두 매크롤르 사용하면 된다.

   ◎ #springFormPasswordInput($path $attributes) - password 타입의 <input> 태그를 생성한다.

   ◎ #springFormHiddenInput($path $attributes) - hidden 타입의 <input> 태그를 생성한다.


(4) <select> 태그를 위한 매크로

 -. 스프링은 <select> 태그와 관련하여 다음과 같이 두 개의 매크로를 제공하고 있다.

   ◎ #springFormSingleSelect($path $options $attributes)

   ◎ #springFormMultiSelect($path $options $attributes)


(5) checkbox 타입 <input> 태그를 위한 매크로

 -. checkbox 타입을 위한 <input> 태그를 생성할 때에는 다음과 같은 매크로를 사용한다.

   ◎ #springFormCheckboxes($path $options $separator $attributes)

   ◎ #springFormCheckbox($path $attributes)


(6) radio 타입 <input> 태그를 위한 매크로

 -. radio 타입의 <input> 태그를 생성하기 위한 스프링 매크로는 다음과 같다.

   ◎ #springFormRadioButton($path $options $separator $attributes)


(7) <textarea> 태그를 위한 매크로

 -. <textarea> 태그를 생성하기 위한 매크로는 다음과 같다.

   ◎ #springFormTextarea($path $attributes)


(8) 에러 메시지 출력을 위한 #springShowErrors 매크로

 -. #springShowErrors 매크로는 커맨드 객체와 관련된 에러 메시즈를 출력할 때 사용도니다.

   ◎  #springShowErrors($separator $class/style)

      - #springBind/#springBindEscaped 매크로로 바인딩 된 BindStatus와 관련도니 에러 메시지들을 출력한다.


(9) VelocityLayoutViewResolver를 이용한 레이아웃 템플릿 적용

 -. Tiles와 비슷하게 Velocity도 동일한 레이아웃 템플릿을 적용해 주는 기능을 제공하고 있다.

 -. 레이아웃 템플릿을 모든 Velocity 템플릿에 적용하면 VelocityLayoutViewResolver를 ViewResolver로 사용하도록 설정해 주어야 한다.

    <bean id="velocityConfigurer"
        class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <property name="resourceLoaderPath" value="/WEB-INF/viewvmlayout/" />
        <property name="velocityProperties">

              …
        </property>
    </bean>

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver"
        p:layoutUrl="/template/layout.vm"

        p:suffix=".vm"
        p:suffix=".vm" p:contentType="text/html; charset=EUC-KR"
        ... />


 ① 컨트롤러의 처리 결과를 보여 줄 템플릿 파일을 찾은 뒤, 템플릿 파일로부터 결과 화면을 생성한다.

    이때 생성된 결과 화면을 screen_content 변수에 저장한다.

 ② layoutUrI 프로퍼티로 지정한 레이아웃 템플릿 파일을 파싱하여 결과 화면을 생성한다.

    이때, 레이아웃 템플릿 파일은 $screen_content 변수를 사용하여 1단계에서 생성한 결과 화면을 내부에 포함시키게 된다.


(10) Velocity  레이아아웃 파일 생성

 -. 레이아웃으로 사용될 Velocity 템플릿 파일은 일반적인 Velocity 템플릿 파일과 별다른 차이가 없다.

    차이점이 있다면 앞서 설명했듯이 처리 결과로 생성된 결과를 포함하기 위한 $screen_content 변수가 사용된다는 점이다.

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>제목</title>
</head>
<body>
#parse("/template/header.vm")
<hr />
$screen_content
<hr/>
#parse("/template/footer.vm")
</body>
</html>




06. HTML 이외의 뷰 구현


6.1 파일 다운로드 구현을 위한 커스텀 View

 -. 파일 다운로드를 구현하는 경우, 컨트롤러 클래스는 다운로드 받을 파일과 관련된 정보를 생성해서 뷰에 전달할 것이다.

 -. 아래 코드는 File 객체를 "download" 뷰에 전달하는 컨트롤러이다.

@Controller
public class DownloadController implements ApplicationContextAware {

    private WebApplicationContext context = null;

    @RequestMapping("/file")
    public ModelAndView download() throws Exception {
        File downloadFile = getFile();
        return new ModelAndView("download", "downloadFile", downloadFile);
    }

    private File getFile() {
        String path = context.getServletContext().getRealPath(
                "/WEB-INF/설명.txt");
        return new File(path);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.context = (WebApplicationContext) applicationContext;
    }
}


 -. 파일을 다운로드를 위한 뷰는 JSP나 Velocity가 아니기 때문에, 파일 다운로드를 위한 커스텀 뷰 클래스를 구현해 주어야 한다.

 -. 또한, BeanNameViewResolver나 XMLViewResolver를 이용해서 커스텀 뷰 클래스를 사용할 수 있도록 알맞게 설정해 주어야 한다.

<bean id ="viewResolver"

      class="org.springframework.web.servlet.view.BeanNameViewResolver" />


<bean id ="download"

      class="madvirus.spring.chap07.view.DownloadView" />


 -. 파일을 다운로드를 구현하려면 컨텐츠 타입을 "application/octet-stream"과 같이 다운로드를 위한 타입으로 설정해 주어야 하며,

    다운로드 받는 파일 이름을 알맞게 설정하기 위해서는 Content-Disposition 헤더의 값을 알맞게 설정해 주어야 한다.

package madvirus.spring.chap07.view;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

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

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;

public class DownloadView extends AbstractView {

    public DownloadView() {
        setContentType("application/download; charset=utf-8");
    }

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        File file = (File) model.get("downloadFile");

        response.setContentType(getContentType());
        response.setContentLength((int) file.length());

        String userAgent = request.getHeader("User-Agent");
        boolean ie = userAgent.indexOf("MSIE") > -1;
        String fileName = null;
        if (ie) {
            fileName = URLEncoder.encode(file.getName(), "utf-8");
        } else {
            fileName = new String(file.getName().getBytes("utf-8"),
                    "iso-8859-1");
        }
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
        response.setHeader("Content-Transfer-Encoding", "binary");
        OutputStream out = response.getOutputStream();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            FileCopyUtils.copy(fis, out);
        } finally {
            if (fis != null)
                try {
                    fis.close();
                } catch (IOException ex) {
                }
        }
        out.flush();
    }
}



6.2 AbstractExcelView 클래스를 이용한 엑셀 다운로드 구현

 -. 스프링은 엑셀 형식으로 뷰 데이터를 생성할 수 있도록 다음의 두 View 클래스를 제공하고 있다.

   ◎ AbstractExcelView - POI API를 이용하여 엑셀 응답을 생성한다.

   ◎ AbstractExcelView - JExcel API를 이용하여 엑셀 응답을 생성한다.


 -. AbstractExcelView 클래스는 다음과 같은 메서드를 정의하고 있다.

protected abstract void buildExcelDocument (

      Map<String, Object> model, HSSFWorkbook workbook,

      HttpServletRequest request, HttpServletResponse response)

      throws Exception;


 -. HSSFWorkbook은 POI API가 제공하는 엑셀 관련 클래스이다.

 -. 하위 클래스는 이 클래스를 이용해서 엑셀 문서를 생성하면 된다.

package madvirus.spring.chap07.view;

import java.util.List;
import java.util.Map;

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

import madvirus.spring.chap07.controller.PageRank;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.web.servlet.view.document.AbstractExcelView;

public class PageRanksView extends AbstractExcelView {

    @SuppressWarnings("unchecked")
    @Override
    protected void buildExcelDocument(Map<String, Object> model,
            HSSFWorkbook workbook, HttpServletRequest request,
            HttpServletResponse response)
throws Exception {
        HSSFSheet sheet = createFirstSheet(workbook);
        createColumnLabel(sheet);

        List<PageRank> pageRanks = (List<PageRank>) model.get("pageRanks");
        int rowNum = 1;
        for (PageRank rank : pageRanks) {
            createPageRankRow(sheet, rank, rowNum++);
        }
    }

    private HSSFSheet createFirstSheet(HSSFWorkbook workbook) {
        HSSFSheet sheet = workbook.createSheet();
        workbook.setSheetName(0, "페이지 순위");
        sheet.setColumnWidth(1, 256 * 20);
        return sheet;
    }

    private void createColumnLabel(HSSFSheet sheet) {
        HSSFRow firstRow = sheet.createRow(0);
        HSSFCell cell = firstRow.createCell(0);
        cell.setCellValue("순위");

        cell = firstRow.createCell(1);
        cell.setCellValue("페이지");
    }

    private void createPageRankRow(HSSFSheet sheet, PageRank rank, int rowNum) {
        HSSFRow row = sheet.createRow(rowNum);
        HSSFCell cell = row.createCell(0);
        cell.setCellValue(rank.getRank());

        cell = row.createCell(1);
        cell.setCellValue(rank.getPage());

    }
}



6.3 AbstractPdfView 클래스를 이용한 PDF  다운로드 구현

 -. 스프링은 iText API를 이용해서 PDF를 생성할 수 잇는 AbstractPdfView 클래슬르 제공하고 있다.

 -. AbstractPdfView 클래슨느 다음과 같은 메서드를 정의하고 있다.

protected abstract void buildPdfDocument(Map<String, Object> model,

      Document document, PdfWriter writer,

      HttpServletRequest request, HttpServletResponse response) throws Exception


 -. com.lowagie.textDocument 클래스는 iText가 제공하는 클래스로서, Document 객체에 PDF 문설르 생성하느 데 필요한 객체를 추가함으로써

    PDF 문서를 생성할 수 있다.

package madvirus.spring.chap07.view;

import java.util.List;
import java.util.Map;

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

import madvirus.spring.chap07.controller.PageRank;

import org.springframework.web.servlet.view.document.AbstractPdfView;

import com.lowagie.text.Cell;
import com.lowagie.text.Document;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Table;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfWriter;

public class PageReportView extends AbstractPdfView {

    @SuppressWarnings("unchecked")
    @Override
    protected void buildPdfDocument(Map<String, Object> model,
            Document document, PdfWriter writer, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        List<PageRank> pageRanks = (List<PageRank>) model.get("pageRanks");
        Table table = new Table(2, pageRanks.size() + 1);
        table.setPadding(5);

        BaseFont bfKorean = BaseFont.createFont(
                "c:\\windows\\fonts\\batang.ttc,0", BaseFont.IDENTITY_H,
                BaseFont.EMBEDDED);

        Font font = new Font(bfKorean);
        Cell cell = new Cell(new Paragraph("순위", font));
        cell.setHeader(true);
        table.addCell(cell);
        cell = new Cell(new Paragraph("페이지", font));
        table.addCell(cell);
        table.endHeaders();

        for (PageRank rank : pageRanks) {
            table.addCell(Integer.toString(rank.getRank()));
            table.addCell(rank.getPage());
        }
        document.add(table);
    }
}



6.4 MarshaalingView를 이용한 XML 응답 생성

 -. 스프링 3 버전에는 자바 객체를 XML로 출력해주는 뷰 구현체인 MarshaalingView 클래스가 추가되었다.

 -. MarshaalingView 클래스는 자바 객체를 XML 문서로 변환시켜주는 Marshaaler와 변환할 모델 키를 각각

   marshaaler 프로퍼티와 modeKey 프로퍼티로 전달받는다.

<bean id="pageXmlReport"
        class="org.springframework.web.servlet.view.xml.MarshallingView">
        <property name="marshaller" ref="marshaller" />
        <property name="modelKey" value="report" />
    </bean>

    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
            <list>
                <value>madvirus.spring.chap07.controller.PageRankReport</value>
            </list>
        </property>
</bean>

<bean id="viewResolver"
        class="org.springframework.web.servlet.view.BeanNameViewResolver" />


 -. 위 설정에서 Jaxb2Marshaller는 PageRankReport 클래스를 매핑 대상 클래스로 설정하고 있는데,

    PageRankReport 클래스 및 이 클래스가 포함하고 있는 PageRank 클래슨느 다음과 같이 JAXB 2 API를 사용해서 매핑 정보를 설정하고 있다.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "rank-report")
public class PageRankReport {

    @XmlElement(name = "page-rank")
    private List<PageRank> pageRanks;
      …
}


@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "rank", "page" })
public class PageRank {

    private int rank;
    private String page;
      …
}


 -. PageRankReport 객체와  PageRank 객체를 생성해서 report 키 값으로 모델에 저장한 뒤 MarshallingView에 전달하는 컨트롤러를  구현

@RequestMapping("/pageXmlReport")
public ModelAndView xmlReport() {
        List<PageRank> pageRanks = new ArrayList<PageRank>();
        pageRanks.add(new PageRank(1, "/bbs/mir2/list"));
        pageRanks.add(new PageRank(2, "/bbs/mir3/list"));
        pageRanks.add(new PageRank(3, "/bbs/changchun2/list"));
        return new ModelAndView("pageXmlReport", "report", new PageRankReport(pageRanks));
}



6.5 MappingJacksonJsonView를 이용한 JSON 응답 생성

 -. 스프링 3 버전은 자바 객체를 JSON으로 변환해서 보여주는 뷰 구현 클래스인 MappingJacksonJsonView를 제공하고 있다.

 -. 아래 코드는 MappingJacksonJsonView의 설정 예를 보여주고 있다.

<bean id="pageJsonReport"
      class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />


<bean id="viewResolver"

      class="org.springframework.web.servlet.view.BeanNameViewResolver" />


 -. 컨트롤러에서는 다음과 같이 뷰 이름으로 MappingJacksonJsonView 타입의 빈을 설정해 주면 된다.

@RequestMapping("/pageJsonReport")
public ModelAndView jsonReport() {
        List<PageRank> pageRanks = new ArrayList<PageRank>();
        pageRanks.add(new PageRank(1, "/bbs/mir2/list"));
        pageRanks.add(new PageRank(2, "/bbs/mir3/list"));
        pageRanks.add(new PageRank(3, "/bbs/changchun2/list"));
        return new ModelAndView("pageJsonReport", "report", new PageRankReport(pageRanks));
}


 -. MappingJacksonJsonView는 모델에 저장도니 모든 객체를 JSON 형식으로 변환해 준다.

public String controllerMethod(Model model) {


      …

      model.addAttribute("report", report);

      model.addAttribute("summary", summary);

      …

}


 -. 컨트롤러에서 위와 같이 모델을 설정한 경우 MappingJackson.JsonView는 다음과 같은 형식의 JSON 응답 결과를 생성한다.

{"report":..., "summary":...}


 -. 응답 결과로 생성되는 컨텐츠 타입은 application/json이며 캐리턱 인코딩은 UTF-8 이다.




07. Locale 처리

 -. 스프링이 제공하는 <spring:message> 커스텀 태근느 웹 요청과 관련된 언어 정보를 이용해서 알맞은 언어의 메시지를 출력한다.

 -. 실제로, 스프링 MVC는 LocaleResolver를 이용해서 웹 요청과 관련된 Locale을 추출하고, 이 Locale 객체를 이요해서 알맞은 언어의 메시지를 선택하게 된다.


7.1 LocaleResolver 인터페이스

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

package org.springframework.web.servlet;

import java.util.Locale;


import javax.ervlet.httpHttpServletRequest;

import javax.ervlet.httpHttpServletResponse;


public interface LocaleResolver {


      Locale resolveLocale(HttpServletRequest request);

      void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale);

}


 -. resolveLocale() 메서드는 요청과 관련된 Locale을 리턴한다. DispatcherServlet은 등록되어 있는 LocaleResovler의 resolveLocale() 메서드를 호출해서

   웹 요청을 처리할 때 사용할 Locale을 구한다.

 -. setLocale() 메서드는 Locale을 변경할 때 사용된다. 예를 들어, 쿠키나 HttpSession에 Locale 정보를 저장할 때에 이 메서드가 사용된다.


7.2 LocaleResolver의 종류

 -. 스프링이 제공하는 LocaleResolver 구현 클래스

클래스

설명

 AcceptHeaderLocaleResolver

 -. 웹 브라우저가 전송한 Accept-Language 헤더로부터 Locale을 선택한다.

 -. setLocale() 메서드를 지원하지 않는다.

 CookieLocaleResolver

 -. 쿠키를 이용해서 Locale 정보를 구한다.

 -. setLocale() 메서드는 쿠키에 Locale 정보를 저장한다.

 SessionLocaleResolver

 -. 세션으로부터 Loclae 정보를 구한다.

 -. setLocale() 메서드는 세션에 Locale 정보를 저장한다.

 FixedLocaleResolver

 -. 웹 요청에 상관없이 특정한 Locale로 설정한다.

 -. setLocale() 메서드를 지원하지 않는다.


  LocaleResolver를 직접 등록할 경우 빈의 이름을 "localeResolver"로 등록해 주어야 한다.



7.3 LocaleResolver를 이용한 Locale 변경

 -. LocaleResolver를 빈으로 등록했다면, 컨트롤러에서 LocaleResolver를 이용해서 Locale을 변경할 수 있게 된다.

<bean class="madvirus.spring.chap07.controller.LocaleChangeController">

      <property name="localeResolver" ref="localeResolver" />

</bean>


<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver" />


 -. 이 경우, 컨트롤러 클래스는 다음과 같이 LovaleResolver의 setLocale() 메서드를 호출해서 클라이언트의 웹 요청을 위한 Locale을 변경할 수 있다.

package madvirus.spring.chap07.controller;

import java.util.Locale;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.LocaleResolver;

@Controller
public class LocaleChangeController {

    private LocaleResolver localeResolver;

    @RequestMapping("/changeLanguage")
    public String change(@RequestParam("lang") String language, HttpServletRequest request, HttpServletResponse response) {
        Locale locale = new Locale(language);
        localeResolver.setLocale(request, response, locale);
        return "redirect:/index.jsp";
    }

    public void setLocaleResolver(LocaleResolver localeResolver) {
        this.localeResolver = localeResolver;
    }
}


 -. LocaleResolver를 이용해서 Locale을 변경하면, 이후 요청에 대해서는 지정한 Locale을 이용해서 메시지 등을 로딩하게 된다.

 -. RequsetContextUtils 클래스는 웹 요청과 관련된 LocaleResolver를 구현할 수 있는 메서드를 제공하고 있으므로, 위 코드를 다음과 같이 변경할 수 있다.

package madvirus.spring.chap07.controller;

import java.util.Locale;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.support.RequestContextUtils;

@Controller
public class LocaleChangeController2 {

    @RequestMapping("/changeLanguage2")
    public String change(@RequestParam("lang") String language,
            HttpServletRequest request, HttpServletResponse response) {
        Locale locale = new Locale(language);
        LocaleResolver localeResolver = RequestContextUtils
                .getLocaleResolver(request);
        localeResolver.setLocale(request, response, locale);
        return "redirect:/index.jsp";
    }
}



7.4 LocaleChangeInterceptor를 이용한 Locale 변경
 -. 스프링이 제공하는 LocaleChangeInterceptor 클래스를 사용하면 웹 요청 파라미터를 이용해서 손쉽게 Locale을 변경할 수 있다.

 -. LocaleChangeInterceptor 클래스는 HandlerInterceptor로서 다음과 같이 HandlerMapping의 interceptors 프로퍼티에 등록만 하면 설정이 완료된다.

<bean id="localeChangeInterceptor"
        class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
        p:paramName="language" />


<bean
        class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="localeChangeInterceptor" />
            </list>
        </property>
</bean>


 -. paramName 프로퍼티는 Locale을 설정할 때 사용할 파라미터 이름을 명시한다.

    예를 들어, 위 코드에서는 paramName 프로퍼티의 값으로 language를 설정했는데,

    이 경우 language 요청 파라미터를 사용해서 Locale을 변경할 수 있다.

http://localhost:8080/chap07/jsp/login/login.do?language=en


 -. LocaleChangeInterceptor는 paramName 프로퍼티로 설정한 요청 파라미터가 존재할 경우,

   파라미터의 값을 이용해서 Locale을 생성한 뒤 LocaleResolver를 이용해서 Locale을 변경한다. 이후, 요청에서는 변경된 Locale이 적용된다.

반응형