Chapter 13. 통합 뷰 기술들

13.1. 소개

Spring의 탁월한 영역중의 하나는 MVC framework의 나머지 부분으로 부터 뷰 기술들을 분리하는 것이다. 예를 들어, JSP가 존재하는 곳에서 Velocity 또는 XSLT 사용을 결심하는 것은 근본적으로 구성(configuration)의 문제이다. 이 장에서는 Spring이 작업하는 주요한 뷰 기술들은 다루고, 새로운 기술들을 추가하는 간단한 방법을 알아본다. 이 장은 MVC framework과 연관된 일반적인 뷰의 기본적인 방법을 이미 다룬 Section 12.5, “view와 view결정하기”와 유사할 것이다.

13.2. JSP & JSTL

Spring은 JSP와 JSTL뷰를 위해 특별히 연관된 해결책을 제공한다. JSP 또는 JSTL 사용은 WebApplicationContext에서 정의된 일반적인 뷰해결자(viewresolver)를 사용한다. 더욱이, JSP들을 쓸 필요가 있을때 실제로 뷰에서 표현할 것이다(render). 이 부분은 JSP 개발을 쉽게 할 수 있게 제공되는 몇몇 추가적인 기능들에서 설명한다.

13.2.1. 뷰 해결자(View resolvers)

Spring에 통합된 다른 뷰 기술처럼 뷰 해결자가 필요로하는 JSP들을 위해 여러분들의 목적(views)을 해결할 것이다. 대부분 보통 JSP들은 InternalResourceViewResolverResourceBundleViewResolver를 개발할 때 뷰 해결자를 사용했다. 이 둘은 WebApplicationContext에 선언되어있다:

# The ResourceBundleViewResolver:
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename"><value>views</value></property>
</bean>

# And a sample properties file is uses (views.properties in WEB-INF/classes):
welcome.class=org.springframework.web.servlet.view.JstlView
welcome.url=/WEB-INF/jsp/welcome.jsp

productList.class=org.springframework.web.servlet.view.JstlView
productList.url=/WEB-INF/jsp/productlist.jsp

보는바와 같이, ResourceBundleViewResolver은 1)클래스와 2)URL을 대응시키기위해 뷰이름들을 정의하는 설정파일이 필요하다. ResourceBundleViewResolver와 함께 오직 해결자가 사용하는 뷰들의 다른 형태를 혼합할 수 있다.

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

InternalResourceBundleViewResolver는 위에서 설명한것처럼 JSP를 사용하기 위해 구성할 수 있다. 가장 좋은 실행은, WEB-INF 디렉토리 아래의 디렉토리안에 JSP 파일들을 위치시키는것을 강력하게 권장한다. 그래서 클라이언트들에 의해 직접적으로 접근할수 없게 한다.

13.2.2. 'Plain-old' JSPs 대(versus) JSTL

자바 표준 테그 라이브러리를 사용할때, 특별한 뷰 클래스, JstlView을 사용해야하고, JSTL는 i18N기능들을 작업하기전에 몇몇의 표현이 필요하다.

13.2.3. 추가적인 태그들을 쉽게 쓸수 있는 개발

스트링은 이전 챕터들에서 설명한것처럼 객체들을 명령하기위한 request 파라미터들의 데이터 바인딩을 제공한다. 데이터 바인딩 기능들의 조합에서 JSP 페이지들 개발을 쉽게 할 수 있게 하기 위해서, Spring은 더 쉽게 만들어진 몇몇 태그들을 제공한다. 모든 Spring 태그들은 html에서 벗어나는(escaping) 기능들을 가질 수 있거나 문자열들의 벗어나는 기능들을 가지지 않을 수 도 있다.

태그 라이브러리 서술자(TLD)는 자기 자신의 배치(distribution)안에 spring.jar와 같이 포함한다. 개별적인 태그에관한 더많은 정보은 온라인에서 찾을 수 있다: http://www.springframework.org/docs/taglib/index.html.

13.3. Tiles

Spring을 사용하는 웹 애플리케이션 안에서 -다른 뷰 기술들과 같이- Tiles는 통합 가능하다. 다음은 대체로 타일을 사용하는 방법을 설명한다.

13.3.1. 의존물들(Dependencies)

Tiles을 사용할 수 있게 하기 위해서 여러분의 프로젝트 안에 포함된 연관된 추가적인 의존물들을 가진다. 다음은 여러분들이 필요로하는 의존물들의 목록이다.

  • struts version 1.1

  • commons-beanutils

  • commons-digester

  • commons-logging

  • commons-lang

의존물들은 Spring 구분(distribution)안에 모두 이용할 수 있다.

13.3.2. Tiles를 통합하는 방법

Tiles를 사용 할 수 있게 하기 위해서, 정의들이 포함된 파일을 사용해서 구성해야한다 (정의들에 대한 기본적인 정보와 다른 Tiles 개념들, http://jakarta.apache.org/struts에서 볼 수 있다). Spring 안에서 TilesConfigurer가 사용되어진다. 다음에 보는 것은 ApplicationContext 구성 예의 일부분이다:

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
    <property name="factoryClass">
        <value>org.apache.struts.tiles.xmlDefinition.I18nFactorySet</value>
    </property>
    <property name="definitions">
        <list>
            <value>/WEB-INF/defs/general.xml</value>
            <value>/WEB-INF/defs/widgets.xml</value>
            <value>/WEB-INF/defs/administrator.xml</value>
            <value>/WEB-INF/defs/customer.xml</value>
            <value>/WEB-INF/defs/templates.xml</value>
        </list>
    </property>
</bean>

여러분이 볼수 있는 바와 같이, WEB-INF/defs 디렉토리에 위치한, 정의들을 포함한 5개의 파일들이 있다. WebApplicationContext의 초기와에서 파일들은 로드(loaded)될 것이고 factoryClass-설정 에의해 정의된 definitionsfactory은 초기화된다. 이를 마친 후, 정의 파일들에 포함된 tiles은 Spring 웹 애플리케이션 내에 뷰로써 사용되어질 수 있다. 뷰들을 사용가능하게 하기 위해서 Spring과 함께 사용되어지는 다른 기술처럼 ViewResolver를 가진다. 아래의 InternalResourceViewResolverResourceBundleViewResolver의 두 가능성을 발견할 수 있다.

13.3.2.1. InternalResourceViewResolver

InternalResourceViewResolver는 각 뷰가 가지고있는 것을 분석하기위해(resolve) viewClass에서 주어진 것을 증명한다. The InternalResourceViewResolver instantiates the given viewClass for each view it has to resolve.

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="requestContextAttribute"><value>requestContext</value></property>
    <property name="viewClass">
        <value>org.springframework.web.servlet.view.tiles.TilesView</value>
    </property>
</bean>

13.3.2.2. ResourceBundleViewResolver

ResourceBundleViewResolver는 해결자(resolver)가 사용할 수 있는 뷰이름들과 뷰클래스들을 포함한 설정 파일들을 제공한다 :

<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename"><value>views</value></property>
</bean>
    ...
    welcomeView.class=org.springframework.web.servlet.view.tiles.TilesView
    welcomeView.url=welcome (<b>this is the name of a definition</b>)
        
    vetsView.class=org.springframework.web.servlet.view.tiles.TilesView
    vetsView.url=vetsView (<b>again, this is the name of a definition</b>)
        
    findOwnersForm.class=org.springframework.web.servlet.view.JstlView
    findOwnersForm.url=/WEB-INF/jsp/findOwners.jsp
    ...

여러분이 볼수 있는 바와 같이, ResourceBundleViewResolver을 사용할때는, 다른 뷰 기술들을 사용하여 뷰를 혼합시킬 수 있다.

13.4. Velocity & FreeMarker

VelocityFreeMarker는 두 templating 언어이다. 이 둘은 Spring MVC 애플리케이션 내의 뷰 기술들과 같이 사용되어질수 있다. 언어들은 아주 유사하고 유사한 필요에 도움이 된다. 그래서 이번 섹션에서 함께 생각해 본다. 두 언어들 사이에 의미론상으로 구문론상의 차이점은 FreeMarker 웹사이트에서 보아라.

13.4.1. 의존물들(Dependencies)

여러분의 웹 애플리케이션은 Velocity 또는 FreeMarker 각각 작업을 하기 위해서 velocity-1.x.x.jar 또는 freemarker-2.x.jar를 포함시켜야 할 것이다. 그리고 commons-collections.jar 또한 Velocity를 이용하는데 필요할 것이다. 전형적으로 J2EE 서버에 의해 발견한 보증된 곳인 WEB-INF/lib 폴더에 포함되고 애플리케이션의 클래스 패스에 추가되어진다. 또한 WEB-INF/lib 폴더 안에 spring.jar가 이미 있다고 가정한다. 최신의 안정된 velocity, freemarker 그리고 commons collections jars는 Spring 프레임워크 안에 제공되어있고 관련된 /lib/ 하위-디렉토리들로부터 복사할 수 있다. 만약 Spring의 Velocity 뷰 안의 dateToolAttribute또는 numberToolAttribute를 사용하여 만든다면, 또한 velocity-tools-generic-1.x.jar를 포함시켜야 할 것이다.

13.4.2. 컨텍스트 구성(Context configuration)

알맞은 구성은 아래에 보는바와 같이 관련된 구성자가 *-servlet.xml를 정의를 추가하여 초기화하는 것이다.

<!--
  This bean sets up the Velocity environment for us based on a root path for templates.
  Optionally, a properties file can be specified for more control over the Velocity
  environment, but the defaults are pretty sane for file based template loading.
-->
<bean 
  id="velocityConfig" 
  class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
  <property name="resourceLoaderPath"><value>/WEB-INF/velocity/</value></property>          
</bean>

<!--
  View resolvers can also be configured with ResourceBundles or XML files.  If you need
  different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean 
  id="viewResolver" 
  class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
  <property name="cache"><value>true</value></property>
  <property name="prefix"><value></value></property>
  <property name="suffix"><value>.vm</value></property>
</bean>
<!-- freemarker config -->
<bean 
  id="freemarkerConfig" 
  class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
  <property name="templateLoaderPath"><value>/WEB-INF/freemarker/</value></property> 
</bean>

<!--
  View resolvers can also be configured with ResourceBundles or XML files.  If you need
  different view resolving based on Locale, you have to use the resource bundle resolver.
-->
<bean 
  id="viewResolver" 
  class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
  <property name="cache"><value>true</value></property>
  <property name="prefix"><value></value></property>
  <property name="suffix"><value>.ftl</value></property>
</bean>

NB: 애플리케이션 컨텍스트 정의 파일에 VelocityConfigurationFactoryBean 또는 FreeMarkerConfigurationFactoryBean을 web-apps에 추가시키지 말아라.

13.4.3. 생성 템플릿들(Creating templates)

템플릿들은 Section 13.4.2, “컨텍스트 구성(Context configuration)”에서 보여준 *Configurer에 의해 명세화한 디렉토리안에 저장이 필요하다. 이문서는 두언어를 위해 생성 템플릿의 세부사항을 포함시키지 않는다 - 관련된 웹사이트에서 정보를 볼 수 있다. 만약 중요부분의 뷰 해결자들(resolvers)을 사용한다면, 논리적 뷰 이름을 JSP에대해 InternalResourceViewResolver 비슷한 형태인 템플릿 파일 이름과 관련시켜서 설명한다. 그래서 만약 제어자가 "welcome"이라는 뷰이름을 포함한 ModelAndView 객체를 되돌린다면 해결자는 /WEB-INF/freemarker/welcome.ftl 또는 /WEB-INF/velocity/welcome.vm 의 적합한템플릿을 찾을 것이다.

13.4.4. 진보한 구성(Advanced configuration)

위의 중요한 기본 구성들은 대부분 애플리케이션 요구사항에 적합할 것이다. 그러나 추가적인 구성선택들은 색다르거나 진보한 요구사항을 지시할때 이용할 수 있다.

13.4.4.1. velocity.properties

이 파일은 완전히 선택적이다. 그러나 명세화한다면, velocity 자체 구성을 하기 위해서 Velocity 런타임을 통과한 값을 포함한다. 단지 진보한 구성을 요구하는것, 만약 이 파일이 필요하다면 VelocityConfigurer 위치에서 위의 정의와 같이 명세화하라.

<bean 
  id="velocityConfig" 
  class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
  <property name="configLocation">
    <value>/WEB-INF/velocity.properties</value>
  </property>
</bean>

대신에, 다음 inline properties와 "configLocation" 설정을 교체함에따라 Velocity 구성 bean을 위해 bean 정의에 직접적으로 velocity properties를 명세화할 수 있다.

<bean 
  id="velocityConfig" 
  class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
  <property name="velocityProperties">
    <props>
      <prop key="resource.loader">file</prop>
      <prop key="file.resource.loader.class">
        org.apache.velocity.runtime.resource.loader.FileResourceLoader
      </prop>
      <prop key="file.resource.loader.path">${webapp.root}/WEB-INF/velocity</prop>
      <prop key="file.resource.loader.cache">false</prop>
    </props>
  </property>
</bean>

Velocity의 Spring 설정을 위해 API 문서을 참조하거나, velocity.properties 파일 자체의 예들과 정의들을 위한 Velocity 문서를 참조하라.

13.4.4.2. FreeMarker

FreeMarker 'Settings' 과 'SharedVariables' 는 FreeMarkerConfigurer bean의 적당한 bean프라퍼티를 셋팅하여 Spring에 의해 관리되는 FreeMarker Configuration 객체로 직접적으로 전달될수 있다. freemarkerSettings 프라퍼티는 java.util.Properties객체를 요구하고 freemarkerVariables 프라퍼티는 java.util.Map를 요구한다.

<bean 
  id="freemarkerConfig" 
  class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
  <property name="templateLoaderPath"><value>/WEB-INF/freemarker/</value></property> 
  <property name="freemarkerVariables">
    <map>
      <entry key="xml_escape"><ref local="fmXmlEscape"/></entry>
    </map>
  </property>
</bean>

<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>

Configuration객체에 적용할 셋팅과 변수의 상세사항을 위해서 FreeMarker문서를 보라.

13.4.5. 바인드(Bind) 지원과 폼(form) 핸들링

Spring은 JSP에서 사용하기 위해서 <spring:bind>를 포함한 태그라이브러리를 제공한다. 이 태그는 주로 폼지원 객체로 부터 값을 표시하거나 웹티어나 비지니스티어내 Validator로 부터 실패한 유효성체크의 결과를 보여주기 위해서 폼을 가능하게 한다. 1.1버전에서 부터, Spring은 자체적인 폼 input 요소를 생성하기 위한 추가적인 편리한 매크로를 가지고 Velocity 와 FreeMarker 모두에 대해 같은 기능을 지원한다.

13.4.5.1. 바인드(bind) 매크로

매크로의 표준 세트는 두가지 언어(Velocity 와 FreeMarker)를 위한 spring.jar 파일내 유지된다. 그래서 그것들은 적합하게 설정된 애플리케이션을 위해 언제나 사용가능하다. 어쨌든 그것들은 당신의 view가 bean프라퍼티인 exposeSpringMacroHelperstrue로 셋팅될때만 사용될수 있다. 당신의 view가 값을 상속할 모든 경우에 같은 프라퍼티는 VelocityViewResolverFreeMarkerViewResolver에서 셋팅될수 있다. 이 프라퍼티는 Spring 매크로의 장점을 가지기를 원하는 곳을 제외하고HTML 폼 핸들링의 어떠한 양상을 위해 요구되지 않는다. 아래는 이러한 view의 정확한 설정을 보여주는 view.properties파일의 예제이다.

personFormV.class=org.springframework.web.servlet.view.velocity.VelocityView
personFormV.url=personForm.vm
personFormV.exposeSpringMacroHelpers=true
personFormF.class=org.springframework.web.servlet.view.freemarker.FreeMarkerView
personFormF.url=personForm.ftl
personFormF.exposeSpringMacroHelpers=true

Spring 라이브러리내 정의된 몇몇 매크로는 내부적(개인적)으로 검토되지만 매크로 정의내 존재하는 범위는 모든 매크로를 호출 코드와 사용자 템플릿을 위해 볼수 있도록 만드는 것은 없다. 다음 부분은 당신에게 템플릿에서 직접적으로 호출될 필요가 있는 매크로에만 집중한다. 만약 당신이 매크로 코드를 직접적으로 보길 원한다면 파일은 spring.vm / spring.ftl 라고 불리고 org.springframework.web.servlet.view.velocityorg.springframework.web.servlet.view.freemarker 패키지내 존재한다.

13.4.5.2. 간단한 바인딩

Spring 폼 컨트롤러를 위한 'formView' 처럼 작동하는 html폼(vm / ftl 템플릿)내에서, 당신은 필드값으로 바인드하는 것과 유사한 코드를 사용하고 JSP와 유사한 형태로 각각의 input필드를 위한 에러메시지를 표시할수 있다. command객체의 이름은 디폴트에 의해 "command" 이지만 폼 컨트롤러의 'commandName' bean프라퍼티를 셋팅하여 당신의 MVC설정에 오버라이드될수 있다는 것을 알라. 예제 코드는 먼저 설정된 personFormVpersonFormF view를 위해 밑에서 보여진다.

<!-- velocity macros are automatically available -->
<html>
...
<form action="" method="POST">
  Name: 
  #springBind( "command.name" )
  <input type="text" 
    name="${status.expression}" 
    value="$!status.value" /><br>
  #foreach($error in $status.errorMessages) <b>$error</b> <br> #end
  <br>
  ... 
  <input type="submit" value="submit"/>
</form>
...
</html>
<!-- freemarker macros have to be imported into a namespace.  We strongly
recommend sticking to 'spring' -->
<#import "spring.ftl" as spring />
<html>
...
<form action="" method="POST">
  Name: 
  <@spring.bind "command.name" /> 
  <input type="text" 
    name="${spring.status.expression}" 
    value="${spring.status.value?default("")}" /><br>
  <#list spring.status.errorMessages as error> <b>${error}</b> <br> </#list>
  <br>
  ... 
  <input type="submit" value="submit"/>
</form>
...
</html>

#springBind / <@spring.bind> 는 문장(period)과 당신이 바인드하고자 하는 command객체 필드의 이름에 의해 당신의 command객체(FormController프라퍼티를 변경하지 않는다면 이것은 'command'가 될것이다.)의 이름을 구성하는 'path' 인자를 요구한다. 내포된 필드는 "command.address.street" 처럼 사용될수 있다. bind 매크로는 web.xml내 ServletContext 파라미터인 defaultHtmlEscape에 의해 명시된 디폴트 HTML 회피(escaping) 행위를 가정한다.

#springBindEscaped / <@spring.bindEscaped> 라고 불리는 매크로의 선택적인 폼은 2개의 인자를 가지고 HTML회피가 상태 에러 메시지나 값에서 사용될지에 대해 명시한다. 요구되는 값은 true나 false이다. 추가적인 폼 핸들링 매크로는 HTML회피와 사용이 가능한 매크로의 사용을 단순화한다. 그것들은 다음 부분에서 설명된다.

13.4.5.3. 폼 input 생성 매크로

두가지 언어를 위한 추가적인 편리한 매크로는 바인딩과 폼 생성(유효성 체크 에러 표시를 포함하여)을 단순화한다. 폼 input필드를 생성하기 위한 매크로를 사용하는 것은 결코 필요하지 않다. 그것들은 간단한 HTML과 혼합되거나 대응될수 있거나 먼저 강조된 Spring 바인드 매크로에 직접적으로 호출한다.

사용가능한 매크로의 다음 테이블은 VTL과 FTL정의와 파라미터 목록을 보여준다.

Table 13.1. 매크로 정의 테이블

매크로VTL 정의FTL 정의
message (코드 파라미터에 기반한 자원 번들로부터 문자열 출력)#springMessage($code)<@spring.message code/>
messageText (코드 파라미터에 기반한 자원 번들로부터 문자열 출력, 디폴트 파라미터의 값으로 되돌아 감)#springMessageText($code $default)<@spring.messageText code, default/>
url (애플리케이션 컨텍스트 root를 가진 상대적인 URL을 접두사로 붙이는)#springUrl($relativeUrl)<@spring.url relativeUrl/>
formInput (사용자 입력을 모으기 위한 표준적인 input필드)#springFormInput($path $attributes)<@spring.formInput path, attributes, fieldType/>
formHiddenInput * (비-사용자 입력을 서브릿하기 위한 hidden input필드)#springFormHiddenInput($path $attributes)<@spring.formHiddenInput path, attributes/>
formPasswordInput * (비밀번호를 모으기 위한 표준적인 input 필드. 이 타입의 필드로 활성화될 값은 없다는것을 알라.)#springFormPasswordInput($path $attributes)<@spring.formPasswordInput path, attributes/>
formTextarea (long값을 모으기 위한 큰 텍스트 필드, freeform형태의 텍스트 input)#springFormTextarea($path $attributes)<@spring.formTextarea path, attributes/>
formSingleSelect (선택되기 위한 하나의 필수값을 허용하는 선택사항의 drop down 박스)#springFormSingleSelect( $path $options $attributes)<@spring.formSingleSelect path, options, attributes/>
formMultiSelect (하나 이상의 값을 선택하기 위한 사용자를 허용하는 선택사항의 리스트 박스)#springFormMultiSelect($path $options $attributes)<@spring.formMultiSelect path, options, attributes/>
formRadioButtons (사용가능한 선택사항으로 부터 만들수 있는 하나의 selection을 허용하는 radio버튼의 세트)#springFormRadioButtons($path $options $separator $attributes)<@spring.formRadioButtons path, options separator, attributes/>
formCheckboxes (선택되기 위한 하나 이상의 값을 허용하는 checkbox의 세트)#springFormCheckboxes($path $options $separator $attributes)<@spring.formCheckboxes path, options, separator, attributes/>
showErrors (연결된 필드를 위한 유효성 체크 에러의 간단한 표시)#springShowErrors($separator $classOrStyle)<@spring.showErrors separator, classOrStyle/>

* FTL (FreeMarker) 에서, 두개의 매크로는 당신이 'hidden' 이나 fieldType 파라미터를 위한 값처럼 'password' 을 명시하는 일반적인 formInput 매크로를 사용할수 있는것처럼 실질적으로 필수가 아니다.

위 매크로중 어느것을 위한 파라미터는 일관적인 수단을 가진다.

  • path: 바인드 하기 위한 필드의 이름(이를테면 "command.name")

  • options: 모든 사용가능한 값의 map은 input필드내 선택될수 있다. 값을 표시하기 위한 map의 key는 form으로 부터 게시될것이고 command객체로 연결된다. key에 대응되어 저장되는 map객체는 사용자를 위한 폼에 표시되는 라벨이고 form에 의해 게시되는 관련값들과는 다르다. map은 언제나 컨트롤러에 의한 참조데이터처럼 제공된다. map구현물은 필수행위에 의존되어 사용될수 있다. 엄격하게 정렬된 map을 위해 적당한 비교자를 가진 TreeMap과 같은 SortedMap은 사용될수 있고 입력순으로 값을 반환해야만 하는 임의의 map을 위해 commons-collections의 LinkedHashMap 이나 LinkedMap를 사용하자.

  • separator: 다중 옵션이 신중한(discreet) 요소(radio버튼이나 checkbox)처럼 사용가능한 곳. 순차적인 문자는 목록에서 각각 분리되기 위해 사용된다. (이를테면 "<br>").

  • attributes: HTML 태그 자체에 포함되는 임의의 태그나 텍스트의 추가적인 문자열. 이 문자열은 매크로에 의해 문자그대로 울린다. 예를 들어, textarea필드에서 당신은 'rows="5" cols="60"' 처럼 속성을 제공하거나 'style="border:1px solid silver"' 처럼 스타일 정보를 전달할수 있다.

  • classOrStyle: showErrors 매크로를 위해, 태그를 확장하는 CSS클래스의 이름은 사용할 각각의 에러를 포장한다. 아무런 정보도 없다면(또는 값이 공백이라면) 에러는 <b></b> 태그내 포장될것이다.

매크로의 예제는 몇몇 FTL과 VTL는 아래에서 간단하게 설명된다. 두가지 언어사이의 사용상의 차이점은 이 노트에서 설명된다.

13.4.5.3.1. input 필드
<!-- the Name field example from above using form macros in VTL -->
...
    Name:
    #springFormInput("command.name" "")<br>
    #springShowErrors("<br>" "")<br>

formInput 매크로는 path파라미터(command.name)와 위 예제에서 빈 추가적인 attribute속성을 가져온다. 다른 폼 생성 매크로와 함께 매크로는 path파라미터에 함축적으로 Spring 바인드를 수행한다. 바인딩은 새로운 바인드가 발생해서 showErrors 매크로가 다시는 path파라미터를 전달할 필요가 없을때까지 유효하다.

showErrors 매크로는 separator(분리자 - 문자들은 주어진 필드에 다중 에러를 분리하기 위해 사용될것이다.) 파라미터를 가지고 두번째 파라미터를 받아들인다. 이번은 클래스명과 style속성이다. FreeMarker는 Velocity와는 달리 attribute속성을 위한 디폴트값을 명시하는것이 가능하다. 그리고 위 두개의 매크로 호출은 다음의 FTL처럼 표시될수 있다.

<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>

출력은 name필드를 생성하는 form일부를 밑에서 보여준다. 그리고 form이 필드내 어떤값도 가지지 않고 서브밋된 후에 유효성체크 에러를 표시한다. 유효성체크는 Spring의 Validation프레임워크를 통해 발생한다.

생성된 HTML은 다음처럼 보일것이다.

Name:
  <input type="text" name="name" value=""     
>
<br>
  <b>required</b>
<br>
<br>

formTextarea매크로는 formInput매크로와 같은 방법으로 작동하고 같은 파라미터 목록을 받아들인다. 공통적으로 두번째 파라미터(속성)는 스타일정보를 전달하거나 textarea를 위한 rows와 cols를 전달하기 위해 사용될것이다.

13.4.5.3.2. selection 필드

4개의 selection 필드 매크로는 HTML form내 공통 UI값 selection input를 생성하기 위해 사용될수 있다.

  • formSingleSelect

  • formMultiSelect

  • formRadioButtons

  • formCheckboxes

4가지 매크로 각각은 form필드를 위한 값과 그 값에 관련된 라벨을 포함하는 옵션의 map을 받아들인다. 값과 라벨은 같을수 있다.

FTL내 radio버튼의 예제는 밑에 있다. form지원 객체는 이 필드를 위한 'London'의 디폴트 값을 명시하고 유효성체크가 필요하지 않다. form이 표현될때 선택하는 city의 전체 목록이 'cityMap'이라는 이름하의 모델내 참조 데이터처럼 제공된다.

...
  Town:
  <@spring.formRadioButtons "command.address.town", cityMap, "" /><br><br>

이것은 분리자 ""를 사용하여 cityMap내 각각의 값중 하나인 radio버튼을 표현한다. 추가적인 속성은 제공되지 않는다(매크로를 위한 마지막 파라미터는 없다). cityMap은 map내 각각의 키(key)-값(value)쌍을 위한 같은 문자열을 사용한다. map의 키는 form이 실질적으로 전송된 요청 파라미터처럼 서브밋하는 것이다. map의 값은 사용자가 보는 라벨이다. 위 예제에서 form지원 객체내 주어진 3개의 잘 알려진 city이 목록과 디폴트 값이다.

Town:
<input type="radio" name="address.town" value="London"
   
>
London
<input type="radio" name="address.town" value="Paris"
  checked="checked" 
>
Paris
<input type="radio" name="address.town" value="New York"
   
>
New York

만약 당신의 애플리케이션이 내부 코드에 의해 city를 다루는것을 기대한다면 예를 들어, 코드의 map은 아래의 예제처럼 적합한 key를 가지고 생성될것이다.

protected Map referenceData(HttpServletRequest request) throws Exception {
  Map cityMap = new LinkedHashMap();
  cityMap.put("LDN", "London");
  cityMap.put("PRS", "Paris");
  cityMap.put("NYC", "New York");
  
  Map m = new HashMap();
  m.put("cityMap", cityMap);
  return m;
}

코드는 radio값이 적절한 코드지만 사용자가 좀더 사용자에게 친숙한 city이름을 볼수 있는 출력을 생성할것이다.

Town:
<input type="radio" name="address.town" value="LDN"
   
>
London
<input type="radio" name="address.town" value="PRS"
  checked="checked" 
>
Paris
<input type="radio" name="address.town" value="NYC"
   
>
New York

13.4.5.4. HTML회피를 오버라이드하고 XHTML호환 태그를 만든다.

위 form매크로의 디폴트 사용은 HTML 4.01호환 HTML태그의 결과를 보일것이고 Spring의 바인트 지원이 사용하는것처럼 web.xml내 정의된 HTML회피를 위한 디폴트 값을 사용한다. XHTML호환 태그를 만들거나 디폴트 HTML회피 값을 오버라이드하기 위해 당신은 템플릿(또는 당신의 템플릿을 볼수 있는 모델내)에 두개의 변수를 명시할수 있다. 템플릿내 그것들을 명시하는 장점은 form내 다른 필드를 위해 다른 행위를 제공하기 위한 템플릿 처리로 그것들이 나중에 다른 값으로 변경될수 있다는 것이다.

당신의 태그를 위한 XHTML호환으로 변경하기 위해 xhtmlCompliant라는 이름의 모델/컨텍스트 변수를 'true'의 값을 명시하라.

## for Velocity..
#set($springXhtmlCompliant = true)

<#-- for FreeMarker -->
<#assign xhtmlCompliant = true in spring>

Spring매크로에 의해 생성되는 태그는 직접적으로 처리된 후 XHTML호환이 될것이다.

유사한 방법으로 HTML회피는 필드마다 명시될수 있다.

<#-- until this point, default HTML escaping is used -->

<#assign htmlEscape = true in spring>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name" />

<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML escaping off -->

13.5. XSLT

XSLT는 XML을 위한 변형언어이고 웹 애플리케이션내 view기술처럼 인기있다. 당신의 애플리케이션이 당연히 XML을 다루거나 당신의 모델이 XML로 쉽게 변활될수 있다면 XSLT는 view기술로 좋은 선택이 될수 있다. 다음 부분은 모델 데이터처럼 XML문서를 생성하는 방법을 보여주고 Spring애플리케이션내 XSLT로 변형한다.

13.5.1. 나의 첫번째 단어

이 예제는 Controller내 단어 목록을 생성하고 그것들을 모델 map으로 추가하는 사소한 Spring애플리케이션이다. map은 XSLT view의 view이름과 함께 반환된다. Spring Controller들의 상세사항을 위해 Section 12.3, “컨트롤러”을 보라. XSLT view는 단어의 목록에서 변형될 준비가 된 간단한 XML문서로 바뀔것이다.

13.5.1.1. Bean 정의

설정은 간단한 Spring애플리케이션을 위한 표준이다. dispatcher 서블릿 설정파일은 URL맵핑과 하나의 컨트롤러 bean을 가지는 ViewResolver를 위한 참조를 포함한다.

<bean id="homeController"class="xslt.HomeController"/> 

그것은 우리의 단어 생성 'logic'을 구현한다.

13.5.1.2. 표준적인 MVC 컨트롤러 코드

컨트롤러 로직은 정의된 핸들러 메소드와 함께 AbstractController의 하위클래스로 캡슐화된다.

protected ModelAndView handleRequestInternal(
    HttpServletRequest req,
    HttpServletResponse resp)
    throws Exception {
        
    Map map = new HashMap();
    List wordList = new ArrayList();
        
    wordList.add("hello");
    wordList.add("world");
       
    map.put("wordList", wordList);
      
    return new ModelAndView("home", map);
} 

지금까지 우리는 XSLT가 명시하는것을 아무것도 하지 않았다. 모델 데이터는 당신이 다른 Spring MVC애플리케이션을 하는것처럼 같은 방법으로 생성된다. 지금 애플리케이션의 설정에 의존하여 단어의 목록은 JSP/JSTL에 의해 요청 속성을 추가하여 표시될수 있거나 VelocityContext에 객체를 구가하여 Velocity에 의해 다루어질수 있다. XSLT가 그것들을 표현하기 위해, 그것들은 XML문서로 변환된다. 자동적으로 'domify' 객체 그래프가 될 사용가능한 소프트웨어 패키지가 있다. 당신은 선택한 방법으로 모델에서 DOM을 생성하는 완벽한 유연성을 가진다. 이것은 domification처리를 관리하는 툴을 사용할때 위험한 모델 데이터의 구조에서 너무 큰 부분으로 작동하는 XML의 변형을 방지한다.

13.5.1.3. 모델 데이터를 XML로 변환하기

우리의 단어 목록이나 다른 모델 데이터로부터 DOM문서를 생성하기 위해 우리는 org.springframework.web.servlet.view.xslt.AbstractXsltView의 하위클래스를 만든다. 우리는 추상 메소드인 createDomNode()을 구현해야만 한다. 이 메소드에 전달되는 첫번째 파라미터는 모델 map이다. 우리의 단어 애플리케이션내 HomePage클래스의 완벽한 목록이다. 이것은 W3C Node를 요구하는 것으로 변환하기 전에 XML문서를 빌드하기 위한 JDOM을 사용한다. 하지만 이것은 W3C API보다 다루기 쉬운 JDOM(그리고 Dom4J) API를 알기 때문에 간단한다.

package xslt;

// imports omitted for brevity

public class HomePage extends AbstractXsltView {

    protected Node createDomNode( 
        Map model, String rootName, HttpServletRequest req, HttpServletResponse res
    ) throws Exception {
        
        org.jdom.Document doc = new org.jdom.Document();
        Element root = new Element(rootName);
        doc.setRootElement(root);

        List words = (List) model.get("wordList");
        for (Iterator it = words.iterator(); it.hasNext();) {
            String nextWord = (String) it.next();
            Element e = new Element("word");
            e.setText(nextWord);
            root.addContent(e);
        }

        // convert JDOM doc to a W3C Node and return
        return new DOMOutputter().output( doc );
    }

}
13.5.1.3.1. stylesheet 파라미터 추가하기

일련의 파라미터 이름/값 쌍은 선택적으로 변형객체로 추가될 하위클래스에 의해 정의될수 있다. 파라미터 이름은 파라미터를 명시하기 위한 <xsl:param name="myParam">defaultValue</xsl:param>로 선언되는 XSLT템플릿으로 정의되는 것들에 매치되어야만 한다. AbstractXsltView의 getParameters() 메소드를 오버라이드하고 이름/값 쌍의 Map을 반환한다. 만약 파라미터가 현재 요청으로부터 정보를 가져올 필요가 있다면 당신은 getParameters(HttpServletRequest request) 메소드를 대신 오버라이드(1.1버전부터)할수 있다.

13.5.1.3.2. 날짜와 화폐단위 포맷팅

JSTL 과 Velocity와는 달리, XSLT는 로케일에 기반한 화폐단위와 날짜 포멧팅을 위한 지원이 상대적으로 빈약하다. 그 사실을 인정해서 Spring은 그부분에 대한 지원을 위해 createDomNode() 메소드로 부터 사용할수 있는 헬퍼 클래스를 제공한다. org.springframework.web.servlet.view.xslt.FormatHelper를 위해 JavaDoc를 보라.

13.5.1.4. view프라퍼티 정의하기

views.properties 파일(또는 위 Velocity예제에서 처럼 XML기반한 view해설자(resolver)를 사용할때 동등한 xml정의)은 'My First Words'인 하나의 view를 가진 애플리케이션을 위한 것처럼 보인다.

home.class=xslt.HomePage
home.stylesheetLocation=/WEB-INF/xsl/home.xslt
home.root=words

여기서, 당신은 view가 첫번째 프라퍼티인 '.class'내 모델 domification을 다루는 것에 의해 쓰여진 HomePage클래스와 묶이는 방법을 볼수 있다. stylesheetLocation 프라퍼티는 HTML파일로의 변형이 되는 XML을 다루는 XSLT파일을 가리키고 마지막 프라퍼티인 '.root'는 XML문서의 root처럼 사용될 이름이다. 이것은 createDomNode 메소드를 위한 두번째 파라미터로 위 HomePage 클래스에 전달된다.

13.5.1.5. 문서 변형

마지막으로, 우리는 위 문서를 변형하기 위해 사용되는 XSLT코드를 가진다. views.properties 파일에서 강조된것처럼 이것은 home.xslt로 불리고 WEB-INF/xsl하위의 war파일내 있다.

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text/html" omit-xml-declaration="yes"/>

    <xsl:template match="/">
        <html>
            <head><title>Hello!</title></head>
            <body>

                <h1>My First Words</h1>
                <xsl:for-each select="wordList/word">
                    <xsl:value-of select="."/><br />
                </xsl:for-each> 

            </body>
        </html>
    </xsl:template>

</xsl:stylesheet>

13.5.2. 요약

언급된 파일의 요약과 WAR파일내 그것들의 위치는 아래의 단순화된 WAR구조에서 보여진다.

ProjectRoot
  |
  +- WebContent
      |
      +- WEB-INF
          |
          +- classes
          |    |
          |    +- xslt
          |    |   |
          |    |   +- HomePageController.class 
          |    |   +- HomePage.class
          |    |
          |    +- views.properties
          |
          +- lib
          |   |
          |   +- spring.jar
          |
          +- xsl
          |   |
          |   +- home.xslt
          |
          +- frontcontroller-servlet.xml

당신은 XML파서와 XSLT엔진이 클래스패스에서 사용가능한지를 확인할 필요가 있다. JDK 1.4는 디폴트로 그것들을 제공한다. 그리고 대부분의 J2EE컨테이너는 디폴트에 의해 그것들을 사용가능하게 만들것이다. 하지만 이것은 인식되는 에러의 가능한 원인이다.

13.6. 문서 views (PDF/Excel)

13.6.1. 소개

HTML페이지를 반환하는 것은 사용자에게 모델 출력을 보여주기 위해 언제나 가장 좋은 방법은 아니다. 그리고 Spring은 모델 데이터로부터 동적으로 PDF문서나 Excel 스프레드시트를 생성하는것을 쉽게 만든다. 문서는 view이고 서버로부터 응답시 클라이언트 PC가 스프레드시트나 PDF뷰어 애플리케이션을 실행할수 있도록 하는 올바른 컨텐츠타입을 가지고 나올것이다.

Excel 뷰를 사용하기 위해, 당신은 클래스패스내 'poi' 라이브러리를 추가할 필요가 있다. 그리고 PDF생성을 위해 iText.jar를 추가할 필요가 있다. 둘다 Spring 배포물에 포함되어 있다.

13.6.2. 설정 그리고 셋업

문서 기반 view는 XSLT view와 대부분 동일한 형태로 다루어진다. 그리고 다음의 부분은 XSLT예제에서 사용된 같은 컨트롤러가 PDF문서나 Excel 스프레드시트(Open Office에서 볼수 있거나 변경이 가능한)처럼 같은 모델을 표시하기 위해 호출되는 방법을 보여주어서 이전의 것을 빌드한다.

13.6.2.1. 문서 view정의

첫번째, views.properties파일(또는 xml파일형태의 프라퍼티 파일)을 수정하자. 그리고 두가지 문서 타입을 위해 간단한 view정의를 추가하자. 전체 파일은 이전에 XSLT view에서 보여진 것과 비슷하게 보일것이다.

home.class=xslt.HomePage
home.stylesheetLocation=/WEB-INF/xsl/home.xslt
home.root=words

xl.class=excel.HomePage

pdf.class=pdf.HomePage

만약 당신의 모델 데이터를 추가하기 위해 템플릿 스프레드시트로 시작하길 원한다면 view정의내 'url' 프라퍼티로 위치를 명시하라.

13.6.2.2. 컨트롤러 코드

컨트롤러 코드에서 우리는 사용하기 위한 view의 이름을 변경하는것보다 이전의 XSLT예제로부터 같은것을 사용할것이다. 물론, 당신은 능숙할수 있고 URL파라미터나 몇몇 다른 로직에 기반하여 이것을 선택할수 있다. 이것은 Spring이 컨트롤러로부터 view를 디커플링하는데 매우 좋다는것을 증명한다.

13.6.2.3. Excel view를 위한 하위클래스 만들기

XSLT에제를 위해 했던것처럼, 우리는 출력문서를 생성하는 사용자정의 행위를 구현하기 위한 적합한 추상 클래스의 하위클래스를 만들것이다. Excel을 위해, 이것은 org.springframework.web.servlet.view.document.AbstractExcelView의 하위클래스를 생성하고 buildExcelDocument를 구현한다.

새로운 스프레드시트의 첫번째 칼럼의 연속적인 row내 모델 map으로 부터 단어목록을 보여주는 Excel view을 위한 완벽한 목록이다.

package excel;

// imports omitted for brevity

public class HomePage extends AbstractExcelView {

    protected void buildExcelDocument(
        Map model,
        HSSFWorkbook wb,
        HttpServletRequest req,
        HttpServletResponse resp)
        throws Exception {
    
        HSSFSheet sheet;
        HSSFRow sheetRow;
        HSSFCell cell;

        // Go to the first sheet
        // getSheetAt: only if wb is created from an existing document
        //sheet = wb.getSheetAt( 0 );
        sheet = wb.createSheet("Spring");
        sheet.setDefaultColumnWidth((short)12);

        // write a text at A1
        cell = getCell( sheet, 0, 0 );
        setText(cell,"Spring-Excel test");

        List words = (List ) model.get("wordList");
        for (int i=0; i < words.size(); i++) {
            cell = getCell( sheet, 2+i, 0 );
            setText(cell, (String) words.get(i));

        }
    }
}

만약 당신이 지금 view(새로운 ModelAndView("xl", map);를 반환하는)의 이름처럼 xl을 반환하는 컨트롤러를 수정하고 다시 애플리케이션을 실행한다면, 이전처럼 같은 페이지를 요청할때 자동적으로 Excel 스프레드시트가 생성되거나 다운로드되는것을 알게된다.

13.6.2.4. PDF view를 위한 하위클래스 만들기

단어 목록의 PDF버전은 좀더 간단하다. 이 시점에, 클래스는 org.springframework.web.servlet.view.document.AbstractPdfView를 확장하고 다음처럼 buildPdfDocument() 메소드를 구현한다.

package pdf;

// imports omitted for brevity

public class PDFPage extends AbstractPdfView {

    protected void buildPdfDocument(
        Map model,
        Document doc,
        PdfWriter writer,
        HttpServletRequest req,
        HttpServletResponse resp)
        throws Exception {
        
        List words = (List) model.get("wordList");
        
        for (int i=0; i<words.size(); i++)
            doc.add( new Paragraph((String) words.get(i)));
    
    }
}

새로운 ModelAndView("pdf", map);을 반환하여 pdf view를 반환하기 위한 컨트롤러를 수정하고 애플리케이션내 URL을 다시 로드하라. 이 시점에 PDF문서는 모델 map에서 각각의 단어를 목록화 하는것을 나타낼것이다.

13.7. JasperReports

JasperReports (http://jasperreports.sourceforge.net)는 강력하고, 쉽게 이해되는 XML파일 포맷을 사용하여 디자인된 리포트의 생성을 지원하는 오픈소스 리포팅 엔진이다. JasperReports는 4가지 다른 포맷(CSV, Excel, HTML 그리고 PDF)으로 리포트 출력을 표시할수 있다.

13.7.1. 의존성

애플리케이션은 JasperReports의 최근 릴리즈를 포함할 필요가 있을것이다. 최근 릴리즈는 이 시점에 0.6.1이다. JasperReports자체는 다음의 제품에 의존성을 가진다.

  • BeanShell

  • Commons BeanUtils

  • Commons Collections

  • Commons Digester

  • Commons Logging

  • iText

  • POI

JasperReports는 또한 JAXP호환 XML파서를 요구한다.

13.7.2. 설정

ApplicationContext에서 JasperReports view를 설정하기 위해 당신의 리포트가 표시되길 원하는 포맷에 의존하는 적당한 view클래스를 위한 view이름을 맵핑하는 ViewResolver를 정의해야만 한다.

13.7.2.1. ViewResolver 설정하기

전형적으로, 당신은 view클래스와 프라퍼티 파일내 파일을 위한 view이름을 맵핑하기 위한 ResourceBundleViewResolver을 사용할것이다.

<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename">
        <value>views</value>
    </property>
</bean>
                  

우리는 기본이름인 views을 가진 자원번들내 view맵핑을 찾을 ResourceBundleViewResolver의 인스턴스를 설정한다. 이 파일의 정확한 내용은 다음 부분에서 언급된다.

13.7.2.2. View 설정하기

Spring은 JasperReports에 의해 지원되는 4가지의 출력 포맷중 하나에 관련된 것중 4가지의 JasperReports를 위한 5가지의 다른 View구현물을 포함하고 실행시 결정되는 포맷을 허용한다.

Table 13.2. JasperReports View 클래스

클래스명표시(Render) 형태
JasperReportsCsvViewCSV
JasperReportsHtmlViewHTML
JasperReportsPdfViewPDF
JasperReportsXlsViewMicrosoft Excel
JasperReportsMultiFormatView실행시 결졍됨(Section 13.7.2.4, “JasperReportsMultiFormatView 사용하기”를 보라.)

view이름을 위한 클래스중 하나와 리포트 파일을 맵핑하는것은 여기서 보여진것처럼 이전 부분에서 설정된 자원번들로 적당한 항목을 추가하는 간단한 사항이다.

simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
              

당신은 simpleReport라는 이름을 가진 view가 JasperReportsPdfView클래스에 맵핑되는것을 볼수 있다. 이것은 PDF포맷으로 표시되는 리포트의 출력을 만들것이다. view의 url 프라퍼티는 참조하는 리포트 파일의 위치를 셋팅한다.

13.7.2.3. 리포트 파일에 대해

JasperReports는 리포트 파일의 두가지 타입(.jrxml 확장자를 가지는 디자인 파일, .jasper 확장자를 가지는 컴파일된 리포트 파일)을 가진다. 전형적으로, 당신은 이것을 당신의 애플리케이션으로 배치하기전에 .jrxml 디자인 파일을 .jasper파일로 컴파일하기 위해 JasperReports Ant작업을 사용한다. Spring으로 당신은 리포트 파일을 위해 이러한 파일들을 맵핑할수 있다. 그리고 Spring은 당신을 위해 구동되는 .jrxml파일을 컴파일할것이다. 당신은 .jrxml파일이 Spring에 의해 컴파일된 후 그것을 주의해야 한다. 컴파일된 리포트는 애플리케이션 생명을 위해 캐시된다. 파일을 변경하기 위해 당신은 애플리케이션을 다시 시작할 필요가 있을것이다.

13.7.2.4. JasperReportsMultiFormatView 사용하기

JasperReportsMultiFormatView는 수행시 명시되는 리포트 포맷을 허용한다. 리포트의 실질적인 표시는 다른 JasperReports view클래스중 하나로 위임된다. JasperReportsMultiFormatView 클래스는 실행시 명시되는 구현물을 허용하는 레퍼(wrapper) 레이어를 간단하게 추가한다.

JasperReportsMultiFormatView 클래스는 포맷(format) 키와 식별자(discriminator) 키라는 두가지 개념을 소개한다. JasperReportsMultiFormatView 클래스는 실질적인 view구현물 클래스를 검사하기 위해 맵핑키를 사용하고 맵핑키를 검사하기 위해 포맷키를 사용한다. 코딩에서 당신은 키와 값으로의 맵핑키처럼 포맷키를 가진 모델을 위한 항목을 추가한다.

public ModelAndView handleSimpleReportMulti(HttpServletRequest request,
HttpServletResponse response) throws Exception {

  String uri = request.getRequestURI();
  String format = uri.substring(uri.lastIndexOf(".") + 1);

  Map model = getModel();
  model.put("format", format);

  return new ModelAndView("simpleReportMulti", model);
}

이 예제에서, 맵핑키는 요청 URI의 확장으로부터 결정되고 디폴트 포맷키(format)의 모델에 추가된다. 만약 당신이 다른 포맷키를 사용하길 바란다면 JasperReportsMultiFormatView클래스의 formatKey 프라퍼티를 사용해서 설정할수 있다.

디폴트에 의해 다음의 맵핑키의 맵핑은 JasperReportsMultiFormatView내 설정된다.

Table 13.3. JasperReportsMultiFormatView 디폴트 맵핑키 맵핑

맵핑키View 클래스
csvJasperReportsCsvView
htmlJasperReportsHtmlView
pdfJasperReportsPdfView
xlsJasperReportsXlsView

위 예제에서 URI /foo/myReport.pdf에 대한 요청은 JasperReportsPdfView클래스에 맵핑될것이다. 당신은 JasperReportsMultiFormatViewformatMappings프라퍼티를 사용하여 view클래스 맵핑에 대한 맵핑키를 오버라이드할수 있다.

13.7.3. ModelAndView 활성화하기

당신이 선택한 포맷으로 정확하게 리포트를 표시하기 위해서, 당신은 리포트를 활성화하기 위해 필요한 모든 데이터를 Spring에 제공해야만 한다. JasperReports를 위한 이 방법에서 당신은 리포트 데이터소스를 가지고 모든 리포트 파라미터를 전달해야만 한다. 리포트 파라미터는 간단한 이름/값 쌍이고 이름/값 쌍을 추가해야할 모델을 위한 Map에 추가되어야만 한다.

모델에 데이터소스를 추가할때 당신은 선택할 두가지 접근법을 가진다. 첫번째 접근법은 어떠한 임의의 키의 모델 Map을 위한 JRDataSourceCollection의 인스턴스를 추가하는것이다. Spring은 모델내 이 객체를 위치시키고 리포트 데이터소스처럼 이것을 처리한다. 예를 들어, 당신은 다음처럼 모델을 활성화할것이다.

private Map getModel() {
  Map model = new HashMap();
  Collection beanData = getBeanData();
  model.put("myBeanData", beanData);
  return model;
}

두번째 접근법은 특정 키의 JRDataSourceCollection의 인스턴스를 추가하고 view클래스의 reportDataKey 프라퍼티를 사용하여 키를 설정한다. 두 경우 다 Spring은 JRBeanCollectionDataSource인스턴스내 Collection의 인스턴스일것이다. 예를 들어,

private Map getModel() {
  Map model = new HashMap();
  Collection beanData = getBeanData();
  Collection someData = getSomeData();
  model.put("myBeanData", beanData);
  model.put("someData", someData);
  return model;
}

당신은 두개의 Collection인스턴스가 모델에 추가되는것을 볼수 있다. 사용되는것이 정확한지 확인하기 위해, 우리는 view설정을 간단하게 변경한다.

simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
simpleReport.reportDataKey=myBeanData
              

첫번째 접근법을 사용할때 Spring은 JRDataSourceCollection의 인스턴스를 사용할 것이다. 만약 당신이 JRDataSourceCollection의 다중 인스턴스를 모델에 둘 필요가 있다면 당신은 두번째 접근법을 사용할 필요가 있다.

13.7.4. 하위-리포트로 작동하기

JasperReports는 당신의 주(master) 리포트 파일내 내장 하위-리포트를 위한 지원을 제공한다. 여기엔 당신의 리포트 파일내 하위-리포트를 포함하기 위한 다양한 기법이 있다. 가장 쉬운 방법은 리포트 경로와 디자인 파일의 하위 리포트를 위한 SQL쿼리를 하드코딩하는 것이다. 이 접근법의 결점은 분명하다. 당신의 리포트 파일에 들어가는 하드코딩된 값은 재사용성을 줄이고 리포트 디자인을 변경하거나 수정하는것을 힘들게 한다. 이것을 극복하기 위해 당신은 선언적인 하위-리포트를 설정할수 있다. 그리고 당신은 컨트롤러로 부터 직접적으로 하위-리포트를 위한 추가적인 데이터를 포함할수 있다.

13.7.4.1. 하위-리포트 파일 설정하기

Spring을 사용하여 주 리포트내 하위-리포트 파일이 포함되는것을 제어하기 위해, 당신의 리포트 파일은 외부 소스로부터 하위-리포트를 받아들이도록 설정이 되어야만 한다. 이것을 하기 위해 당신은 다음처럼 리포트 파일내 파라미터를 선언한다.

<parameter name="ProductsSubReport" class="net.sf.jasperreports.engine.JasperReport"/>

그 다음, 당신은 하위-리포트 파라미터를 사용하는 하위-리포트를 정의한다.

<subreport>
    <reportElement isPrintRepeatedValues="false" x="5" y="25" width="325"
        height="20" isRemoveLineWhenBlank="true" backcolor="#ffcc99"/>
    <subreportParameter name="City">
        <subreportParameterExpression><![CDATA[$F{city}]]></subreportParameterExpression>
    </subreportParameter>
    <dataSourceExpression><![CDATA[$P{SubReportData}]]></dataSourceExpression>
    <subreportExpression class="net.sf.jasperreports.engine.JasperReport">
                  <![CDATA[$P{ProductsSubReport}]]></subreportExpression>
</subreport>

이것은 ProductsSubReport 파라미터하의 net.sf.jasperreports.engine.JasperReports의 인스턴스처럼 전달되는 하위-리포트를 기대하는 주 리포트 파일을 정의한다. 당신의 Jasper view클래스가 설정될때, 당신은 리포트 파일을 로드하도록 Spring에 지시할수 있고 subReportUrls 프라퍼티를 사용하여 하위-리포트같은 JasperReports엔진으로 전달할수 있다.

<property name="subReportUrls">
    <map>
        <entry key="ProductsSubReport">
            <value>/WEB-INF/reports/subReportChild.jrxml</value>
        </entry>
    </map>
</property>

여기서 Map의 키는 리포트 디자인 파일내 하위-리포트 파라미터의 이름과 관련된다. 그리고 항목은 리포트 파일의 URL이다. Spring은 리포트 파일을 로드할것이고 필요하다면 컴파일하고 주어진 키로 JasperReports엔진에 전달할것이다.

13.7.4.2. 하위-리포트 데이터소스 설정하기

이 단계는 Spring을 사용하여 하위-리포트를 설정할때 완전히 선택사항이다. 당신이 원한다면, 정적 쿼리를 사용하여 하위-리포트를 위한 데이터소스를 설정할수 있다. 어쟀든, 당신은 Spring이 ModelAndView내 반환되는 데이터를 JRDataSource의 인스턴스로 변환하기를 원한다면 Spring이 변환할 ModelAndView내 파라미터를 명시할 필요가 있다. 이 설정을 하기 위해 파라미터 이름의 목록은 선택된 view클래스의 subReportDataKeys 프라퍼티를 사용한다.

<property name="subReportDataKeys">
    <value>SubReportData</value>
</property>

여기서 당신이 제공하는 키는 ModelAndView내 사용되는 키와 리포트 디자인 파일내 사용되는 키 모두와 일치해야만 한다.

13.7.5. 전파자(Exporter) 파라미터 설정하기

만약 당신이 전파자(exporter) 설정을 위한 특별한 요구사항을 가진다면, 이를 테면 PDF리포트를 위한 페이지 크기를 명시하기를 원한다면 당신은 view클래스의 exporterParameters 프라퍼티를 사용하여 Spring설정 파일내 선언적으로 전파자(exporter) 파라미터를 설정할수 있다. exporterParameters 프라퍼티는 Map같은 타입이 되고 설정내 항목의 키는 전파자(exporter) 파라미터 정의와 당신이 파라미터에 할당하기를 원하는 값이 될 항목의 값을 포함하는 정적 필드의 전체경로의 이름이 되어야한다. 이것의 예제는 아래와 같다.

<bean id="htmlReport"
          class="org.springframework.web.servlet.view.jasperreports.JasperReportsHtmlView">
  <property name="url">
    <value>/WEB-INF/reports/simpleReport.jrxml</value>
  </property>
  <property name="exporterParameters">
    <map>
      <entry key="net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER">
        <value>Footer by Spring!
          &lt;/td&gt;&lt;td width="50%"&gt;&amp;nbsp; &lt;/td&gt;&lt;/tr&gt;
          &lt;/table&gt;&lt;/body&gt;&lt;/html&gt;
        </value>
      </entry>
    </map>
  </property>
</bean>

여기서 당신은 JasperReportsHtmlView가 결과 HTML내 footer를 출력할 net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER를 위한 전파자(exporter) 파라미터를 가지고 설정되는것을 볼수 있다.