jhhan의 블로그

Spring - Excel Download 본문

Spring & Vue.js

Spring - Excel Download

jhhan000 2021. 1. 12. 23:18

이번 포스트는 스프링 - 엑셀 다운로드를 좀 더 그럴 듯하게 변경해서 진행합니다.

 

저번 엑셀 다운로드는 굉장히 간단하게만 알아봤습니다.

이번에는

  • backend: spring
  • frontend: vue.js

로 진행해보려 합니다.

빠른 진행을 위해 설정은 아래 링크를 참조하면 됩니다.

jhhan009.tistory.com/49?category=780121

 

먼저 백엔드부터 진행합니다.

모델과 컨트롤러가 중요합니다.

먼저 모델인 IntAndDouble 클래스를 만들어봅니다.(클래스 이름은 맘대로 하시면 됩니다.)

public class IntAndDouble {
    private Integer mode;
    private Double affinity;
    private Double lb;
    private Double ub;
    private String pUrl;
    private String qUrl;

    public Integer getMode() {
        return mode;
    }

    public void setMode(Integer mode) {
        this.mode = mode;
    }

    public Double getAffinity() {
        return affinity;
    }

    public void setAffinity(Double affinity) {
        this.affinity = affinity;
    }

    public Double getLb() {
        return lb;
    }

    public void setLb(Double lb) {
        this.lb = lb;
    }

    public Double getUb() {
        return ub;
    }

    public void setUb(Double ub) {
        this.ub = ub;
    }

    public String getpUrl() {
        return pUrl;
    }

    public void setpUrl(String pUrl) {
        this.pUrl = pUrl;
    }

    public String getqUrl() {
        return qUrl;
    }

    public void setqUrl(String qUrl) {
        this.qUrl = qUrl;
    }
}

간단한 코드인데

getter & setter 때문에 코드가 굉장히 길어졌습니다.

Intger, Double, String 형태를 함께 넘길 것입니다.

다음은 컨트롤러를 봅니다.

@RestController
public class TestController {

    @PostMapping("/excel")
    public void getExcelFile(@RequestBody List<IntAndDouble> iads, HttpServletResponse response) throws IOException {
        for (IntAndDouble iad : iads) {
            System.out.println(iad);
        }

        Workbook wb = new XSSFWorkbook();
        Sheet sheet = wb.createSheet("첫번째 시트");
        Row row;
//        Cell cell;
        int rowNum = 0;

        // Header
        row = sheet.createRow(rowNum++);
        row.createCell(0).setCellValue("Mode");
        row.createCell(1).setCellValue("Affinity");
        row.createCell(2).setCellValue("lb");
        row.createCell(3).setCellValue("ub");
        row.createCell(4).setCellValue("purl");
        row.createCell(5).setCellValue("qurl");

        // Body
        for (IntAndDouble iad : iads) {
            row = sheet.createRow(rowNum++);
            row.createCell(0).setCellValue(iad.getMode());
            row.createCell(1).setCellValue(iad.getAffinity());
            row.createCell(2).setCellValue(iad.getLb());
            row.createCell(3).setCellValue(iad.getUb());
            row.createCell(4).setCellValue(iad.getpUrl());
            row.createCell(5).setCellValue(iad.getqUrl());
        }

        // 컨텐츠 타입과 파일명 지정
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment;filename=example.xls");

        // Excel File Output
        wb.write(response.getOutputStream());
        wb.close();
    }
}

저번 글과 마찬가지로 디자인은 하지 않습니다.

그래도 엑셀을 다운받았는데 셀에 있는 데이터가 제대로 보이지 않으면 불쾌할 수도 있습니다.

  • autoSizeColumn(index)
  • 엑셀의 셀 크기를 자동으로 조절해주는 메서드입니다.
  • 열 개수에 맞춰서 사용하면 됩니다.
  • 제 코드의 경우 열이 6개이기 때문에 6번 사용하면 되겠네요 → 반복문을 사용한다면 훨씬 편하겠습니다.

application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

→ 저번과 바뀐 것을 알 수 있는데, 이 설정을 해야 확장자를 .xlsx로 설정할 수 있습니다.

요즘은 대부분 .xlsx를 쓰기 때문에 권장합니다.

지난번과 크게 바뀐 것은 없습니다.

 

이제 프론트를 설정합니다.

프론트 파일을 생성하면 아마 이렇게 생성이 될 것입니다.

따로 추가할 파일은 없습니다.

App.vue에 작성합니다.

<template>
  <div id="app">
    <div>
      <button @click="makeExcelFile">Excel</button>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'App',
  data () {
    return {
      data1: [
        {
          mode: 1,
          affinity: 0.5,
          lb: 1.34,
          ub: 2.53,
          pUrl: "purl1",
          qUrl: "qurl1"
        },
        {
          mode: 2,
          affinity: 1.5,
          lb: 13.34,
          ub: 2.453,
          pUrl: "purl2",
          qUrl: "qurl2"
        }
      ]
    }
  },
  methods: {
    makeExcelFile () {
      console.log("Excel!")
      axios.post(process.env.VUE_APP_API_ENDPOINT + "/excel", this.data1, { responseType: 'arraybuffer' })
          .then(result => {
            console.log(result)
            console.log(result.headers["content-type"])
            const url = window.URL.createObjectURL(new Blob([result.data], { type: result.headers["content-type"] }))
            const link = document.createElement("a")
            link.href = url
            link.download = "example.xlsx"
            link.click()
            window.URL.revokeObjectURL(url)
          })
          .catch(er => {
            console.log(er)
          })
    }
  }
}
</script>

 

  • data1에 추가한 데이터를 넘깁니다.
  • axios를 사용합니다.
  • reponsetype → 이것을 추가해야 반환된 값으로 엑셀 다운을 실행할 수 있습니다.
  • createObjectURL → 이를 통해 객체를 가리키는 URL을 통해서 DOMString을 생성합니다.
  • URL 생성은 Blob을 통해서 했습니다.
  • revokeObjectURL → createObjectURL로 생성된 기존 URL을 폐기합니다. 
  • 메모리 누수가 일어날 수 있어서 꼭 설정해줘야 한다고 합니다.

creatObjectURL에 대해 더 자세히 알고 싶다면

developer.mozilla.org/ko/docs/Web/API/URL/createObjectURL

로 가시면 됩니다.

그 외의 코드들은 보면 이해하실 것 같습니다.

 

프론트와 백을 모두 실행합니다.

그리고 웹브라우저에 접속하면 Excel 버튼이 하나 있습니다.

클릭합니다.

콘솔 창에는 다음과 같이 나타나는 것을 알 수 있습니다.

그리고 엑셀 파일이 다운로드 되는 것을 알 수 있습니다.

그리고 엑셀을 열어본다면?

아까 설정한 값들을 볼 수 있는 것을 알 수 있습니다.

 

 

이렇게 프론트와 백을 분리해서 엑셀 다운로드를 진행했습니다.

다만 한가지 의문이 들었던 것은

백엔드에서 반환값은 없습니다. void로 설정했으니까요.

근데 프론트에서는 값을 반환받아서 실행합니다.

원래 정석으로 간다면, InputStream으로 리턴을 해야합니다.

그 리턴값을 프론트에서 받아서 진행하는 것이 맞습니다.

하지만 이상하게도 void로 설정해놔도 엑셀 파일을 다운받는데에는 문제가 없더군요

 

사실 저도 나중에는 InputStream으로 다시 바꿔서 진행한 것으로 수정을 진행할 예정입니다.

작은 프로젝트나 개인적인 프로젝트에서 이렇게 하시는 것을 추천합니다.

대규모 프로젝트 & 많은 사람들이 쓸 것 같은 프로그램 에서는 이런 식으로 코딩하시면 안됩니다.

 

 

이렇게 프론트 & 백을 분리해서 엑셀을 다운로드 하기를 마칩니다.

'Spring & Vue.js' 카테고리의 다른 글

Spring - Excel Download(3)  (1) 2021.01.30
Spring - Excel Download(2)  (0) 2021.01.19
Spring + Vue.js(3)  (1) 2020.08.02
Spring & Vue.js  (0) 2020.06.11
Spring & Vue.js 연동  (5) 2020.05.13