Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

[Spring 레퍼런스] 17장 뷰 기술 #2

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.



17.5 XSLT
XSLT는 XML의 변환언어로 웹 어플리케이션의 뷰 기술로 인기가 있다. XSLT는 어플리케이션이 자연스럽게 XML을 다루거나 모델을 쉽게 XML로 변환할 수 있다면 뷰 기술로 괜찮은 선택이 될 수 있다. 다음 섹션은 모델 데이터로 XML 문서를 생성하고 스프링 웹 MVC 어플리케이션에서 XSLT로 변환하는 방법을 보여준다.


17.5.1 My First Words 예제
이 예제는 Controller에서 단어의 리스트를 생성하고 모델 맵에 단어를 추가하는 간단한 스프링 어플리케이션이다. XSLT의 뷰 이름과 일치하는 맵을 반환한다. 스프링 웹 MVC의 Controller 인터페이스의 자세한 내용은 Section 16.3, “컨트롤러 구현하기”을 참고해라. XSLT 뷰는 변환을 준비히려고 단어의 리스트를 간단한 XML 문서로 바꿀 것이다.


17.5.1.1 빈(Bean) 정의
설정은 간단한 스프링 어플리케이션의 표준 설정이다. 디스패처 서블릿 설정 파일은 ViewResolver, URL 매핑, 단일 컨트롤러 빈 등에 대한 참조를 담고 있다.

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

이는 단어를 생성하는 로직은 은닉화한다.


17.5.1.2 표준 MVC 컨트롤러 코드
컨트롤러 로직은 다음과 같이 정의된 핸들러 메서드와 함께 AbstractController의 하위클래스에서 은닉화된다.

protected ModelAndView handleRequestInternal(
  HttpServletRequest request,
  HttpServletResponse response) 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과 관련된 것은 아무것도 하지 않았다. 다른 스프링 MVC 어플리케이션과 같은 방법으로 모델 데이터를 생성한다. 어플리케이션에 설정에 따라 단어 리스트를 요청 속성에 추가해서 JSP/JSTL가 단어 리스트를 렌더링하거나 VelocityContext에 객체를 추가해서 Velocity가 단어리스트를 다룰 수 있다. XSLT가 단어 리스트를 렌더링하기 위해 단어 리스트는 당연히 XML 문서로 어떻게든 변환되어야 한다. 자동으로 객체 그래프를 'domify'할 수 있는 소프트웨어 패키지가 존재하지만 스프링에서는 선택한 어떤 방법으로든 모델에서 DOM을 생성하는 유연성을 가진다. 이는 DOM으로 만드는(domification) 과정을 다루는 도구를 사용하는 경우 위험한 모델 데이터의 구조에서 다루기 너무 큰 XML의 변환을 막는다.


17.5.1.3 모델데이터를 XML로 변환하기
단 어 리스트나 다른 모델 데이터에서 DOM 문서를 생성하려면 (제공된) org.springframework.web.servlet.view.xslt.AbstractXsltView의 하위클래스를 만들어야 한다. 하위 클래스를 만들때는 보통 추상 메서드인 createXsltSource(..)를 구현해야 한다. 이 메서드의 전달하는 첫번째 파라미터는 모델 맵이다. 간단한 단어 어플리케이션의 HomePage 클래스의 완전한 소스가 다음에 나와있다.

package xslt;

// 간결함을 위해서 임포트문은 생략한다

public class HomePage extends AbstractXsltView {

  protected Source createXsltSource(Map model, String rootName, HttpServletRequest
    request, HttpServletResponse response) throws Exception {

    Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    Element root = document.createElement(rootName);

    List words = (List) model.get("wordList");
    for (Iterator it = words.iterator(); it.hasNext();) {
      String nextWord = (String) it.next();
      Element wordNode = document.createElement("word");
      Text textNode = document.createTextNode(nextWord);
      wordNode.appendChild(textNode);
      root.appendChild(wordNode);
    }
    return new DOMSource(root);
  }
}

이 름/값 쌍의 파라미터들은 변환 객체에 추가할 하위클래스가 선택적으로 정의할 수 있다. 파라미터 이름은 <xsl:param name="myParam">defaultValue</xsl:param>로 선언한 XSLT 템플릿에서 정의한 것과 일치해야 한다. 파라미터를 지정하려면 AbstractXsltView 클래스의 getParameters() 메서드를 오버라이드하고 이름/값 쌍의 Map을 반환해라. 현재 요청 정보에서 파라미터를 얻어야 한다면 대신 getParameters(HttpServletRequest request)를 오버라이드할 수 있다.


17.5.1.4 뷰 프로퍼티 정의하기
views.properties 파일(또는 앞의 Velocity 예제에서 했듯이 XML 기반의 뷰 리졸버를 사용한다면 views.properties의 역할을 하는 XML 정의)은 뷰가 하나인 'My First Words' 어플리케이션에서는 다음과 같을 것이다.

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

이 예제에서 첫 프로퍼티 '.(class)'에서 모델을 DOM으로 만드는 과정(model domification)을 다루는 HomePage 클래스와 뷰을 엮는 방법을 볼 수 있다. 'stylesheetLocation' 프로퍼티는 XML을 HTML로 변환하는 과정을 다룰 XSLT 파일을 가리키고 마지막 프로퍼티인 '.root'는 XML 문서의 루트로 사용할 이름이다. 이는 앞의 HomePage 클래스의 createXsltSource(..) 메서드에 두번째 파라미터로 전달해서 얻는다.


17.5.1.5 문서 변환
마지막으로 앞에 문서를 변환하는데 사용할 XSLT 코드가 있다. 앞의 'views.properties' 파일에서 보았듯이 스타일시트는 'home.xslt'이고 이 파일은 'WEB-INF/xsl' 디렉토리의 war 파일에 존재한다.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" omit-xml-declaration="yes"/>

  <xsl:template match="/">
    <html>
      <head><title>Hello!</title></head>
      <body>
        <h1>My First Words</h1>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="word">
    <xsl:value-of select="."/><br/>
  </xsl:template>
</xsl:stylesheet>


17.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는 기본적으로 이 둘을 제공하고 대부분의 Java EE 컨테이너도 기본적으로 이 둘을 사용할 수 있게 하지만 인식오류가 생길 수 있다.


17.6 문서의 뷰 (PDF/Excel)

17.6.1 소개
항 상 HTML 페이지를 반환하는 것이 모델의 출력을 사용자에게 보여주는 가장 좋은 방법은 아니다. 스프링은 모델 데이터에서 동적으로 PDF 문서나 Excel 스프리드시트를 쉽게 생성할 수 있게 해준다. 문서는 뷰이고 클라이언트 PC가 응답에서 스프리트시트나 PDF 뷰어 어플리케이션을 실행할 수 있도록 올바른 컨텐트 타입으로 서버에서 스트리밍될 것이다.

Excel 뷰를 사용하려면 클래스패스에 'poi' 라이브러리를 추가해야 하고 PDF를 생성하려면 iText 라이브러리를 추가해야 한다.


17.6.2 구성과 설정
문 서에 기반한 뷰는 XSLT 뷰과 거의 동일한 방법으로 다룬다. 다음 섹션에서는 같은 모델을 PDF문서와 Excel 스프리드시트(Open Office에서 보고 다룰 수도 있다.)로 모두 렌더링하는데 XSLT 예제에서 사용한 동일한 컨트롤러를 호출하는 방법을 설명하면서 앞의 예제를 사용한다.


17.6.2.1 문서 뷰 정의
우선 views.properties 파일(또는 동일한 XML)을 수정하고 두가지 문서타입에 대한 간단한 뷰 정의를 추가해보자. 전체 파일은 이제 다음과 같을 것이고 XSLT 뷰는 앞에 나와있다.

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

xl.(class)=excel.HomePage

pdf.(class)=pdf.HomePage

모델 데이터를 추가하려고 템플릿 스프리드 시트나 채워넣을 수 있는 PDF 폼으로 시작하고 싶다면 뷰 정의에서 'url' 프로퍼티로 위치를 지정해라.


17.6.2.2 컨트롤러 코드
사 용할 컨트롤러 코드는 뷰이름을 변경한것 외에는 앞에서 사용한 XSLT 예제와 정확히 동일하다. 물론 URL이나 다른 로직에 기반해서 뷰 이름을 선택하도록 만들 수 있고 이 부분이 컨트롤러와 뷰의 커플링을 줄이는데 스프링이 정말로 좋다는 증거이다!


17.6.2.3 Excel 뷰의 하위클래스 만들기
XSLT 예제에서 했던것과 정확히 동일하게 출력 문서를 생성하는 동작을 커스터마이징하려면 적절한 추장클래스의 하위클래스르 만들 것이다. 엑셀에서는 org.springframework.web.servlet.view.document.AbstractExcelView (POI가 생성한 Excel파일에서)나 org.springframework.web.servlet.view.document.AbstractJExcelView (JExcelApi가 생성한 Excel 파일에서)의 하위클래스를 작성하고 buildExcelDocument() 메서드를 구현한다.

새로운 스프리드시트의 첫 행의 연속된 열로 모델 맵의 단어 리스트를 보여주는 POI Excel 뷰의 전체코드가 다음에 나와있다.

package excel;

// 간결하게 임포트 문은 생략한다

public class HomePage extends AbstractExcelView {

  protected void buildExcelDocument(
    Map model,
    HSSFWorkbook wb,
    HttpServletRequest req,
    HttpServletResponse resp)
    throws Exception {

    HSSFSheet sheet;
    HSSFRow sheetRow;
    HSSFCell cell;

    // 첫 시트(sheet)로 이동
    // getSheetAt: 존재하는 문서에서 wb가 생성된 경우에만
    // sheet = wb.getSheetAt(0);
    sheet = wb.createSheet("Spring");
    sheet.setDefaultColumnWidth((short) 12);

    // 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));
    }
  }
}

그리고 다음은 JExcelApi를 사용해서 같은 Excel 파일을 생성하는 뷰다.

package excel;

// 간결하게 임포트 문은 생략한다

public class HomePage extends AbstractJExcelView {

  protected void buildExcelDocument(Map model,
    WritableWorkbook wb,
    HttpServletRequest request,
    HttpServletResponse response)
  throws Exception {

    WritableSheet sheet = wb.createSheet("Spring", 0);

    sheet.addCell(new Label(0, 0, "Spring-Excel test"));

    List words = (List) model.get("wordList");
    for (int i = 0; i < words.size(); i++) {
      sheet.addCell(new Label(2+i, 0, (String) words.get(i)));
    }
  }
}

API의 차이점에 주목해라. JExcelApi가 약간 더 직관적이고 이지미 처리 기능이 좀 더 좋다. 하지만 JExcelApi를 사용하는 경우 큰 Excel 파일에서는 메모리 문제가 있다.

이 제 컨트롤러가 뷰 이름으로 xl를 반환하는 (return new ModelAndView("xl", map);) 등의 수정을 하고 어플리케이션을 다시 실행하면 이전과 같은 페이지를 요청했을 때 자동적으로 Excel 스프리드시트가 생성되고 다운로드 되는 것을 볼 것이다.


17.6.2.4 PDF 뷰의 하위클래스 만들기
단 어 리스트의 PDF 버전은 더 간단하다. 이번에는 다음과 같이 org.springframework.web.servlet.view.document.AbstractPdfView를 확장하고 buildPdfDocument() 메서드를 구현한다.

package pdf;

// 간결하게 임포트 문은 생략한다

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)));
  }
}

다시 한번 return new ModelAndView("pdf", map);로 pdf 뷰를 반환하게 컨트롤러를 수정하고 어플리케이션에서 URL을 갱신하자. 이번에는 모델 맵의 각 단어 리스트가 나오는 PDF 문서가 나올 것이다.


17.7 JasperReports
JasperReports (http://jasperreports.sourceforge.net)는 이해하기 쉬운 XML 파일 형식을 사용해서 리포트를 생성할 수 있는 강력한 오픈소스 리포트 엔진이다. JasperReports는 CSV, Excel, HTML, PDF 네가지 형식으로 리포트를 만들 수 있다.


17.7.1 의존성
어플리케이션에는 JasperReports의 최신버전이 포함시켜야 한다. 이 글을 쓰는 시점에서 최신 버전은 0.6.1이다. JasperReports는 다음 프로젝트에 의존하고 있다.

  • BeanShell
  • Commons BeanUtils
  • Commons Collections
  • Commons Digester
  • Commons Logging
  • iText
  • POI
JasperReports도 JAXP를 따르는 XML 파서가 필요하다.


17.7.2 구성
스프링 컨테이너 구성에서 JasperReports 뷰를 설정하려면 뷰 이름을 원하는 리포트 형식에 맞는 뷰 클래스에 매핑할 ViewResolver를 정의해야 한다.


17.7.2.1 ViewResolver 설정
보통 프로퍼티 파일에서 뷰 이름을 뷰 클래스와 파일들에 매핑하는데 ResourceBundleViewResolver를 사용할 것이다.

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

여기서 기반이름인 views으로(이 파일의 내용은 다른 섹션에서 설명한다.) 리소스 번들에서 뷰 매핑을 검색할 ResourceBundleViewResolver 클래스의 인스턴스를 설정했다.


17.7.2.2 View 구성
스프링 프레임워크는 JasperReports에 대한 다섯가지 View 구현체를 가진다. 네가지는 JasperReports가 지원하는 출력 포맷에 대응하고 한가지는 런타임에서 포맷을 결정할 수 있게 한다.


Table 17.2. JasperReports View 클래스
클래스명 렌더링 형식
JasperReportsCsvView CSV
JasperReportsHtmlView HTML
JasperReportsPdfView PDF
JasperReportsXlsView Microsoft Excel
JasperReportsMultiFormatView 이 뷰는 런타임에서 결정된다










이러한 클래스를 뷰 이름과 리포트 파일에 매핑하는 것은 이전 섹션에서 설정한 리소스 번들에 추가한 적합한 엔트리에 관련되어 있다.

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

여기서 simpleReport 뷰 이름이 JasperReportsPdfView 클래스에 매핑된 것을 볼 수 있다. 그래서 이 리포트는 PDF 형식으로 출력된다. 뷰의 url 프로퍼티는 사용하는 리포트 파일의 위치로 설정한다.


17.7.2.3 리포트 파일에 대해서
JasperReports 는 두 가지 다른 타입의 리포트 파일을 가진다. .jrxml 확장자를 갖는 디자인 파일과 .jasper 확장자를 갖는 컴파일된 리포트 파일이다. 보통 어플리케이션에 배포하기 전에 .jrxml 디자인 파일을 .jasper 파일로 컴파일하는데 JasperReports Ant 테스크를 사용한다. 스프링 프레임워크에서는 이러한 파일들을 모두 리포트 파일에 매핑할 수 있고 스프링 프레임워크가 알아서 .jrxml파일을 컴파일할 것이다. 스프링 프레임워크가 .jrxml 파일을 컴파일한 후에 컴파일된 리포트는 어플리케이션 생명주기동안 캐싱된다는 것을 기억해야 한다. 그러므로 파일을 변경하려면 어플리케이션을 재시작해야 한다.


17.7.2.4 JasperReportsMultiFormatView 사용하기
JasperReportsMultiFormatView 로 런타임에서 리포트 형식을 지정할 수 있다. 리포트의 실제 렌더링은 다른 JasperReports 뷰 클래스에 위임한다. JasperReportsMultiFormatView 클래스는 런타임에서 지정되는 정확한 구현체를 위한 랩퍼 계층을 추가할 뿐이다.

JasperReportsMultiFormatView 클래스는 두가지 컨셉을 도입했다. 두 컨셉은 형식키(format key)와 식별자키(discriminator key)이다. JasperReportsMultiFormatView 클래스는 실제 뷰 구현체 클래스를 검색하는데 매핑 키를 사용하고 매핑키를 검색하는데 형식키를 사용한다. 코딩하는 관점에서는 키에는 포맷키를, 값에는 매핑키를 가진 모델에 엔트리를 추가한다. 예를 들면 다음과 같다.

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 17.3. JasperReportsMultiFormatView 기본 매핑키의 매핑
매핑 키 뷰 클래스
csv JasperReportsCsvView
html JasperReportsHtmlView
pdf JasperReportsPdfView
xls JasperReportsXlsView








그러므로 위의 예제에서 /foo/myReport.pdf URI의 요청은 JasperReportsPdfView 클래스로 매핑될 것이다. JasperReportsMultiFormatView의 formatMappings 프로퍼티로 매핑키와 뷰 클래스의 매핑을 오버라이드할 수 있다.


17.7.3 ModelAndView의 생성
선 택한 포맷으로 리포트를 제대로 렌더링하려면 리포트에 필요한 모든 데이터를 스프링에 제공해야 한다. JasperReports에서는 리포트 데이터소스와 함께 모든 리포트 파라미터를 전달해야 한다. 리포트 파라미터는 간단한 이름/값의 쌍이고 다른 값/쌍을 추가하듯이 모델의 Map에 추가할 수 있다.

모델에 데이터소스를 추가할 때 선택할 수 있는 두가지 접근법이 있다. 첫 접근법은 JRDataSource의 인스턴스나 Collection타입을 임의의 키로 모델 Map에 추가하는 것이다. 그러면 스프링이 모델에서 이 객체를 찾아내서 리포트 데이터소스로 다룰 것이다. 예를 들어 다음과 같이 모델을 생성할 것이다.

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

두 번째 접근방법은 JRDataSource의 인스턴스나 Collection를 특정 키 아래 추가하고 뷰 클래스의 reportDataKey 프로퍼티를 사용해서 이 키를 설정하는 것이다. 두 경우에 스프링은 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 인스턴스가 모델에 추가되는 것을 볼 수 있다. 올바른 인스턴스가 사용되는지 보장하기 위해 뷰 설정을 적절하게 수정한다.

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

첫 접근방법을 사용하는 경우 스프링은 찾아낸 JRDataSource의 첫 인스턴스나 Collection을 사용한다는 것을 알아야 한다. JRDataSource의 여러 인스턴스나 Collection을 모델에 두어야 한다면 두번째 접근방법을 사용해야 한다.


17.7.4 하위리포트 사용하기
JasperReports 는 마스터 리포트 파일내에 내장된 하위 리포트를 지원한다. 리포트 파일에 하위 리포트를 추가하는 아주 다양한 메타니즘이 좀재한다. 가장 쉬운 방법은 디자인 파일에 하위리포트의 피로트 경로와 SQL 쿼리를 하드코딩하는 것이다. 이 접근방법의 단점은 명확하다. 리포트 파일에 하드코딩된 값은 재사용성을 낮추고 리포트 디자인을 수정하기 어렵게 한다. 이 문제를 해결하기 위해 선언적으로 하위리포트를 설정하고 컨트롤러에서 이러한 하위리포트에 추가적인 데이터를 직접 포함시킬 수 있다.


17.7.4.1 하위리포트 파일 구성하기
마스터 리포트에 포함된 하위리포트 파일들을 스프링을 사용해서 제어하려면 리포트 파일이 외부 소스에서 하위리포트를 받아들이도록 설정해야 한다. 이 설정을 하려면 리포트파일에서 다음과 같이 파라미터를 설정한다.

<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 뷰 클래스를 구성할 때 subReportUrls 프로퍼티를 사용해서 스프링이 리포트 파일을 로딩해서 JasperReports에 하위리포트로 전달하도록 할 수 있다.

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

여기서 Map의 키는 리포트 디자인 파일의 하위리포트 파라미터의 이름과 일치하고 엔트리는 리포트 파일의 URL이다. 스프링은 이 리포트 파일을 로딩하고 필요하다면 컴파일해서 주어진 키로 JasperReports 엔진에 전달한다.


17.7.4.2 하위리포트 데이터 소스 설정하기
하 위리포트를 구성하는데 스프링을 사용하는 경우 이 과정은 완전히 선택적이다. 원한다면 정적 쿼리를 사용해서 하위 리포트에 대한 데이터 소스를 설정할 수 있다. 하지만 스프링이 ModelAndView가 반환한 데이터를 JRDataSource 인스턴스로 변환하기를 원한다면 스프링이 변환해야 하는 ModelAndView에 어느 파라미터를 변환해야 하는지 지정해야 한다. 선택한 뷰 클래스의 subReportDataKeys 프로퍼티를 사용해서 파라미터명 목록을 설정한다.

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

여기서 제공한 키는 ModelAndView에서 사용한 키와 리포트 디자인 파일에서 사용한 키 모두와 반드시 일치해야 한다.


17.7.5 익스포터(Exporter) 파라미터 설정
익 스포터(exporter) 구성과 관련해서 특수한 요구사항이 있다면(PDF 리포트의 페이지 크기를 지정하는 등) 뷰 클래스의 exporterParameters 파라미터를 사용해서 스프링 설정 파일에 선언적으로 익스포터 파라미터를 설정할 수 있다. exporterParameters 프로퍼티의 타입은 Map이다. 설정에서 엔트리의 키는 익스포터 파라미터 정의를 포함하는 정정 필드의 정규화된 이름이어야 하고 엔트리의 값은 파라미터에 할당하고자 하는 값이어야 한다. 다음에 이 예제가 나와있다.

<bean id="htmlReport" class="org.springframework.web.servlet.view.jasperreports.JasperReportsHtmlView">
  <property name="url" value="/WEB-INF/reports/simpleReport.jrxml"/>
  <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>

이 예제에서 출력 HTML의 푸터에 출력할 net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER로 익스포터 파라미터가 설정된 JasperReportsHtmlView를 볼 수 있다.


17.8 피드(Feed) 뷰
AbstractAtomFeedView와 AbstractRssFeedView 모두 기반클래스인 AbstractFeedView를 상속받고 각각 Atom과 RSS 피드에 사용한다. 이 두 클래스는 java.net의 ROME 프로젝트에 기반하고 있으며 org.springframework.web.servlet.view.feed 패키지에 있다.

다음과 같이 AbstractAtomFeedView에서는 buildFeedEntries() 메서드를 구현해야 하고 선택적으로 buildFeedMetadata() 메서드(기본 구현체는 비어있다.)를 오버라이드한다.

public class SampleContentAtomView extends AbstractAtomFeedView {

  @Override
  protected void buildFeedMetadata(Map<String, Object> model, Feed feed,
      HttpServletRequest request) {
    // 구현부는 생략한다
  }

  @Override
  protected List<Entry> buildFeedEntries(Map<String, Object> model,
      HttpServletRequest request, HttpServletResponse response)
      throws Exception {

    // 구현부는 생략한다
  }
}

다음과 같이 AbstractRssFeedView를 구현할 때도 비슷한 요구사항이 적용된다.

public class SampleContentAtomView extends AbstractRssFeedView {

  @Override
  protected void buildFeedMetadata(Map<String, Object> model, Channel feed,
      HttpServletRequest request) {
    // 구현부는 생략한다
  }

  @Override
  protected List<Item> buildFeedItems(Map<String, Object> model,
      HttpServletRequest request, HttpServletResponse response)
      throws Exception {
    // 구현부는 생략한다
  }
}

로 케일(Locale)에 접근해야 하는 경우 buildFeedItems()와 buildFeedEntires() 메서드를 HTTP 요청에 전달한다. HTTP 응답은 쿠키나 다른 HTTP 헤더를 설정할 때만 전달된다. 메서드가 반환된 뒤 피드는 자동적으로 응답 객체에 자동적으로 쓰여질 것이다.

Atom 뷰를 생성하는 예제는 Alef Arendsen가 작성한 SpringSource Team Blog의 을 참고해라.


17.9 XML 마샬링 뷰
MarhsallingView 는 XML로 응답 내용을 렌더링할 때 org.springframework.oxm 패키지에 정의된 XML Marshaller를 사용한다. 마샬링되어야 하는 객체는 MarhsallingView의 modelKey 빈 프로퍼티를 사용해서 명시적으로 설정할 수 있다. 아니면 뷰는 모든 모델 프로퍼티를 순회하고 Marshaller가 지원하는 타입만 마샬링할 것이다. org.springframework.oxm 패키지의 기능에 대한 내용은 O/X 매퍼(Mapper)를 사용한 XML 마샬링(Marshalling)장을 참고해라.


17.10 JSON 매핑 뷰
MappingJacksonJsonView 는 JSON으로 응답 내용을 렌더링할 때 Jackson 라이브러리의 ObjectMapper를 사용한다. 기본적으로 모델 맵의 전체 내용(프레임워크에 특화된 클래스의 예외와 함께)은 JSON으로 인코딩 될 것이다. 맵의 내용을 필터링해야 하는 경우 사용자들은 RenderedAttributes 프로퍼티로 인코딩해야 하는 모델 속성을 지정할 것이다. extractValueFromSingleKeyModel 프로퍼티도 모델 속성의 맵 보다는 직접 추출하고 직렬화한 단일키(single-key) 모델에 값을 갖는데 사용할 것이다.

Jackson이 제공하는 어노테이션을 사용해서 JSON 매핑을 필요에 따라 커스터마이징 할 수 있다. 더 세밀한 제어가 필요하다면 특정 타입에 제공해야 하는 커스텀 JSON 직렬변환기(serializers)/역직렬변환기(deserializers)가 필요한 곳에 ObjectMapper 프로퍼티로 커스텀 ObjectMapper를 주입할 수 있다.
2013/03/03 02:29 2013/03/03 02:29