저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링4 입문

스프링 4

DDD Start

객체 지향과
디자인 패턴

JSP 2.3

JPA 입문
OKKY 세미나에서 발표한 자료.


Posted by 최범균 madvirus
TAG Vue.js

댓글을 달아 주세요

스프링 부트 2.0에서 엑셀 다운로드 기능을 구현하는 방법을 정리했다.


pom.xml 설정


https://start.spring.io/ 사이트에서 스프링 부트 2.0.x 버전을 선택해서 프로젝트를 생성한다. Dependencies로는 Web과 Thymeleaf를 선택한다. 생성한 프로젝트 pom.xml 파일에 엑셀 생성을 위해 poi 의존을 추가한다.


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

<project xmlns="http://maven.apache.org/POM/4.0.0"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 

              http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>


    <groupId>madvirus</groupId>

    <artifactId>excel-download</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging>


    <name>excel-download</name>

    <description>Demo project for Spring Boot</description>


    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.0.1.RELEASE</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>


    <properties>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <java.version>1.8</java.version>

    </properties>


    <dependencies>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-thymeleaf</artifactId>

        </dependency>


        <dependency>

            <groupId>org.apache.poi</groupId>

            <artifactId>poi</artifactId>

            <version>3.17</version>

        </dependency>


        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-devtools</artifactId>

            <scope>runtime</scope>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

    </dependencies>


    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>



</project>


엑셀 다운로드 위한 application.properties 파일 설정


확장자나 파라미터를 이용해서 엑셀 다운로드를 처리할 수 있도록 application.propertie 파일에 다음 설정을 추가한다.


spring.mvc.contentnegotiation.favor-parameter=true

spring.mvc.contentnegotiation.favor-path-extension=true

spring.mvc.contentnegotiation.media-types.xls=application/vnd.ms-excel


스프링 부트는 기본적으로 ContentNegotiationViewResolver를 사용하는데 각 프로퍼티는 다음을 설정한다.

  • favor-parameter: 이 값이 true면 ContentNegotiationViewResolver가 format 파라미터로 지정한 미디어 타입을 사용하도록 설정
  • favor-path-extension: 이 값이 true면 ContentNegotiationViewResolver가 확장자로 지정한 미디어 타입을 사용하도록 설정
  • media-types.타입: 타입에 해당하는 컨텐츠 타입을 지정

예를 들어 위 설정을 사용하면 다음 요청을 엑셀 타입(application/vnd.ms-excel) 요청으로 인지하고, 엑셀 미디어 타입에 해당하는 응답을 처리할 수 있는 뷰를 사용해서 응답을 생성한다.

  • stat.xls (확장자가 xls)
  • stat?format=xls (format 파라미터가 xls)

예제 컨트롤러


다음 코드는 일반 뷰와 엑셀 다운로드를 처리하는 컨트롤러 코드이다.


@Controller

public class StatController {

    private void populateModel(Model model) {

        List<StatRow> rows = Arrays.asList(

                new StatRow("고객1", 1000, 1500),

                new StatRow("고객2", 2000, 2500),

                new StatRow("고객3", 3000, 3500)

        );

        model.addAttribute("rows", rows);

    }


    @GetMapping("/stat")

    public String get(Model model) {

        populateModel(model);

        return "stat";

    }


    @GetMapping("/stat.xls")

    public String getExcelByExt(Model model) {

        populateModel(model);

        return "statXls";

    }


    @GetMapping(path = "/stat", params = "format=xls")

    public String getExcelByParam(Model model) {

        populateModel(model);

        return "statXls";

    }

}


get() 메서드는 일반 뷰를 사용해서 응답을 생성한다. getExcelByExt() 메서드는 확장자가 xls인 요청 경로를 처리하므로 "statXls"에 대응하는 뷰 중에서 엑셀 타입을 응답으로 생성할 수 있는 뷰를 선택한다. 비슷하게 getExcelByParam() 역시 format 파라미터가 xls인 요청을 처리하므로 엑셀 타입을 생성할 수 있는 뷰를 선택한다.


엑셀 생성을 위한 뷰 클래스


엑셀 다운로드를 위한 뷰 클래스는 다음과 같이 구현한다. 빈 객체 이름으로 "statxls"를 사용했는데 이 이름은 앞서 컨트롤러에서 리턴한 뷰 이름과 같다.


package exceldownload;


import org.apache.poi.ss.usermodel.*;

import org.springframework.stereotype.Component;

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


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.util.List;

import java.util.Map;


@Component("statXls")

public class StatXlsView extends AbstractXlsView {

    @Override

    protected void buildExcelDocument(

            Map<String, Object> model, Workbook workbook,

            HttpServletRequest request, HttpServletResponse response) throws Exception {

        response.setHeader("Content-Disposition", "attachment; filename=\"stat.xls\"");


        List<StatRow> stats = (List<StatRow>) model.get("rows");


        CellStyle numberCellStyle = workbook.createCellStyle();

        DataFormat numberDataFormat = workbook.createDataFormat();

        numberCellStyle.setDataFormat(numberDataFormat.getFormat("#,##0"));


        Sheet sheet = workbook.createSheet("mobilestat");

        for (int i = 0 ; i < stats.size() ; i++) {

            StatRow stat = stats.get(i);

            Row row = sheet.createRow(i);


            Cell cell0 = row.createCell(0);

            cell0.setCellValue(stat.getName());


            Cell cell1 = row.createCell(1);

            cell1.setCellType(CellType.NUMERIC);

            cell1.setCellValue(stat.getValue1());

            cell1.setCellStyle(numberCellStyle);


            Cell cell2 = row.createCell(2);

            cell2.setCellType(CellType.NUMERIC);

            cell2.setCellValue(stat.getValue2());

            cell2.setCellStyle(numberCellStyle);

        }

    }

}



타임리프 뷰 구현


타임리프트를 이용한 뷰 구현 파일인 stat.html은 다음과 같아 간단하게 구현했다. 엑셀 다운로드를 위한 링크를 추가했다.


<!DOCTYPE HTML>

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta charset="utf-8" />

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

    <title>통계</title>

</head>

<body>


<a href="stat.xls">엑셀다운, 확장자(stat.xls)</a> |

<a href="stat?format=xls">엑셀다운, 파라미터(stat?format=xls)</a>

<table border="1">

    <thead>

    <tr>

        <th>이름</th>

        <th>값1</th>

        <th>값2</th>

    </tr>

    </thead>

    <tbody>

    <tr th:each="row : ${rows}">

        <td th:text="${row.name}"></td>

        <td th:text="${#numbers.formatInteger(row.value1, 1, 'COMMA')}"></td>

        <td th:text="${#numbers.formatInteger(row.value2, 1, 'COMMA')}"></td>

    </tr>

    </tbody>

</table>


</body>

</html>


예제 실행


완전한 예제 프로젝트는 https://github.com/madvirus/excel-download 리포지토리에서 구할 수 있다. 명령 프롬프트에서 "mvnw spring-boot:run" 명령어를 부트 어플리케이션을 실행한 뒤에 http://localhost:8080/stat 주소에 연결해보자. 다음 결과를 볼 수 있다.



엑셀 다운로드 링크를 클릭해보자. 두 링크 중 아무거나 클릭하면 엑셀 파일을 다운로드 한다.



실제 다운로드한 파일을 열어보자. 아래와 같이 엑셀 파일이 올바르게 생성된 것을 확인할 수 있다.



Posted by 최범균 madvirus

댓글을 달아 주세요

스프링 부트(spring boot)를 사용한다면 타임리프(thymeleaf)의 식 객체(expression object)를 쉽게 확장할 수 있다. 먼저 식 객체를 생성해주는 타임리프 IExpressionObjectDialect를 구현한다. 이 클래스를 스프링 빈으로 등록해야 한다. 아래 예는 @Component를 붙여 컴포넌트 스캔 대상으로 설정했다.


import java.util.Collections;

import java.util.Set;


import org.springframework.stereotype.Component;

import org.thymeleaf.context.IExpressionContext;

import org.thymeleaf.dialect.AbstractDialect;

import org.thymeleaf.dialect.IExpressionObjectDialect;

import org.thymeleaf.expression.IExpressionObjectFactory;


@Component

public class MyFormatDialect extends AbstractDialect implements IExpressionObjectDialect {


    protected ScgFormatDialect() {

        super("myFormat");

    }


    @Override

    public IExpressionObjectFactory getExpressionObjectFactory() {

        return new IExpressionObjectFactory() {


            @Override

            public Set<String> getAllExpressionObjectNames() {

                return Collections.singleton("scgFormat");

            }


            @Override

            public Object buildObject(IExpressionContext context, String expressionObjectName) {

                return new MyFormat();

            }


            @Override

            public boolean isCacheable(String expressionObjectName) {

                return true;

            }

        };

    }


}


생성자에서는 식 객체의 이름을 "myFormat"으로 지정한다.

getExpressionObjectFactory() 메서드는 IExpressionObjectFactory 객체를 리턴한다. 이 객체의 buildObject() 메서드가 생성하는 객체가 식 객체가 된다. 이 객체는 타임리프 식에서 사용할 메서드를 제공한다. 다음은 식 객체로 사용할 클래스의 구현 예이다.


public class MyFormat {


    public String date(String date) {

        if (!StringUtils.hasText(date))

            return null;

        if (date.length() == 8) {

            return date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6, 8);

        } else {

            return date;

        }

    }


    public String contractNum(String contractNum) {

        if (!StringUtils.hasText(contractNum))

            return null;

        if (contractNum.length() > 5) {

            return contractNum.substring(0, 5) + "-" + contractNum.substring(5);

        } else {

            return contractNum;

        }

    }


    public String phone(String phone) {

        if (!StringUtils.hasText(phone))

            return null;

        if (phone.length() == 11) {

            return phone.substring(0, 3) + "-" + phone.substring(3, 7) + "-" + phone.substring(7);

        } else if (phone.length() == 10) {

            return phone.substring(0, 3) + "-" + phone.substring(3, 6) + "-" + phone.substring(6);

        } else {

            return phone;

        }

    }

}


이제 커스텀 식 객체를 타임리프 식에서 사용하면 된다.


<td th:text="${#myFormat.phone(item.handphone)}"></td>

<td th:text="${#myFormat.contractNum(item.useContractNum)}"></td>




Posted by 최범균 madvirus

댓글을 달아 주세요