Programming/Spring Boot

스프링부트 REST API Excel download 방법

Jan92 2021. 10. 22. 00:32

Spring boot REST API Excel download 기본적인 방법 (feat. apache poi)

 

 

스프링 부트 REST에서 poi 라이브러리를 사용하여 엑셀을 다운로드하는 방법입니다.

poi 라이브러리는 아파치 소프트웨어 재단에서 만들었고, microsoft office 파일 포맷을 자바 언어로 읽고, 쓰는 기능을 제공하는 라이브러리입니다.

 

 

작업 환경은 아래와 같습니다.

  • 'org.springframework.boot' version '2.4.10-SNAPSHOT'
  • java 1.8
  • Gradle

 

 

// https://mvnrepository.com/artifact/org.apache.poi/poi
implementation group: 'org.apache.poi', name: 'poi', version: '3.17'

 

먼저 apache.poi 라이브러리 의존성을 추가합니다.

 

 

 

@RequestMapping("/api/v1/excel")
@RestController
public class ExcelDownloadController {

    @GetMapping("/test")
    public void excelDownload(HttpServletResponse response) throws IOException {
        // Header, ContentType 설정, excel 파일명 변경가능
        response.setHeader("Content-Disposition", "attachment;filename=testExcel1.xlsx");
        response.setContentType("application/octet-stream");

        // 엑셀 파일 헤더
        List<String> header = Arrays.asList("이름", "생년월일", "연락처", "주소");

        // 엑셀 파일로 만들 리스트
        List<ExcelTest> excelTestList = new ArrayList<>();
        excelTestList.add(new ExcelTest("John", "910101", "01033333333", "ABCD"));
        excelTestList.add(new ExcelTest("Sally", "880303", "01012345678", "ZZZZ"));
        excelTestList.add(new ExcelTest("Helen", "891212", "01055555555", "GGHH"));
        excelTestList.add(new ExcelTest("Bob", "930508", "01098765432", "UIUI"));

        ByteArrayInputStream stream = ExcelUtils.createListToExcel(header, excelTestList);
        IOUtils.copy(stream, response.getOutputStream());
    }
}

 

다음은 REST Controller입니다.

우선 HttpServletResponse 객체가 필요합니다. 그리고 실제 프로젝트에서는 다운로드 받으려는 데이터가 필요하고, 데이터를 가져오는 로직이 필요하지만 해당 포스팅은 엑셀 다운로드 방법을 위한 글이므로 데이터는 보여드리기 위해서 Controller 내부에서 간단하게만 만들었습니다.

 

HttpServletResponse 객체에 Header와 ContentType을 세팅합니다.

 

 

이름 생년월일 연락처 주소
John 910101 01033333333 ABCD
Sally 880303 01012345678 ZZZZ
Helen 891212 01055555555 GGHH
Bob 930508 01098765432 UIUI

 

다음과 같은 엑셀 데이터를 가져오기 위해서 엑셀 내용 부분 외에 엑셀 데이터의 헤더 부분도 만들어줘야 합니다.

그리고 만들어진 엑셀의 헤더와 내용을 아래 createListToExcel이라는 메서드를 통해 ByteArrayInputStream 타입의 객체로 가공합니다.

 

 

* ExeclTest 객체는 String name, String birth, String phoneNumber, String address 4개의 필드를 가진 객체입니다.

 

 

 

public class ExcelUtils {

    public static ByteArrayInputStream createListToExcel(List<String> excelHeader, List<ExcelTest> excelTestList) {
        try (Workbook workbook = new HSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("excelDownloadTest");
            Row row;
            Cell cell;
            int rowNo = 0;

            int headerSize = excelHeader.size();

            // 테이블 헤더 스타일 설정
            CellStyle headStyle = workbook.createCellStyle();
            // 경계선 설정
            headStyle.setBorderTop(BorderStyle.THIN);
            headStyle.setBorderBottom(BorderStyle.THIN);
            headStyle.setBorderLeft(BorderStyle.THIN);
            headStyle.setBorderRight(BorderStyle.THIN);
            // 색상
            headStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.YELLOW.getIndex());
            headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            // 헤더 가운데 정렬
            headStyle.setAlignment(HorizontalAlignment.CENTER);

            // 헤더 생성
            row = sheet.createRow(rowNo++);
            for (int i=0; i<headerSize; i++) {
                cell = row.createCell(i);
                cell.setCellStyle(headStyle);
                cell.setCellValue(excelHeader.get(i));
            }

            // 내용 생성
            for (int j=0; j<excelTestList.size(); j++) {
                Row dataRow = sheet.createRow(j + 1);
                dataRow.createCell(0).setCellValue(excelTestList.get(j).getName());
                dataRow.createCell(1).setCellValue(excelTestList.get(j).getBirth());
                dataRow.createCell(2).setCellValue(excelTestList.get(j).getPhoneNumber());
                dataRow.createCell(3).setCellValue(excelTestList.get(j).getAddress());
            }

            // Making size of column auto resize to fit with data
            sheet.autoSizeColumn(0);
            sheet.autoSizeColumn(1);
            sheet.autoSizeColumn(2);
            sheet.autoSizeColumn(3);

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            workbook.write(outputStream);
            return new ByteArrayInputStream(outputStream.toByteArray());

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

 

ExcelUtils class의 createListToExcel 메서드입니다.

중간에 HeadStyle 부분과 아래 autoSizeColumn 부분은 부가적인 부분이고, 이 부분들을 제외하면 데이터를 엑셀로 만드는 핵심이 됩니다.

 

 

* apache.poi 라이브러리의 주요 클래스들은 주로 HSSF, XSSF로 시작하는데 차이점은 다음과 같습니다.

 

HSSF - Excel97 파일 포맷을 사용할 때 사용합니다. ex) HSSFWorkbook, HSSFSheet

XSSF - Excel 2007 OOXML(.xlsx) 파일 포맷을 사용할 때 사용합니다. ex) XSSFWorkbook, XSSFSheet

 

 

* 추가적으로 알아야 할 내용으로는

Workbook은 하나의 엑셀 파일을 의미하고, Sheet는 엑셀 파일 (Workbook)의 시트를 의미합니다.

Row, Cell은 Sheet 안에 있는 행과 열을 의미합니다.

 

 

 

// 1. workbook을 생성합니다.
Workbook workbook = new HSSFWorkbook()

// 2. workbook내에 sheet를 생성합니다.
Sheet sheet = workbook.createSheet();

// 3. sheet내에 row를 생성합니다.
row = sheet.createRow();

// 4. 하나의 row에 여러개의 cell을 생성합니다.
dataRow.createCell(0).setCellValue();
dataRow.createCell(1).setCellValue();
.
.
.

// 5. 3번 row 생성과 4번 row 내부에 cell 생성 과정이 반복되며 엑셀이 생성됩니다.

 

위에 긴 코드에서 핵심이 되는 내용들입니다.

 

 

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
return new ByteArrayInputStream(outputStream.toByteArray());

 

마지막으로 ByteArrayOutputStream 객체를 통해 완성된 workbook 데이터를 받아 toByteArray() 메서드를 통해 ByteArrayInputStream 객체로 return 합니다.

 

 

 

ByteArrayInputStream stream = ExcelUtils.createListToExcel(header, excelTestList);
IOUtils.copy(stream, response.getOutputStream());

 

마지막으로 다시 컨트롤러입니다. createListToExcel() 메서드로 받아온 ByteArrayInputStream 객체를 IOUtils.copy() 메서드를 통해 처리하면 로직은 마무리됩니다.

 

 


 

 

 

swagger

 

Swagger를 통해 해당 api를 테스트하면 Response body로 저렇게 바로 Download file을 통해 만들어진 엑셀 파일을 다운로드할 수 있습니다.

 

 


 

 

private ByteArrayInputStream createTupleToExcel(List<String> header, List<Tuple> list) throws IOException {
    try(Workbook workbook = new XSSFWorkbook()) {
      .
      .
      .
    
      for (Tuple tuple : list) {
        row = sheet.createRow(rowNo++);
        for(int j = 0; j < headerSize; j++) {
          cell = row.createCell(j);
          cell.setCellValue(tuple.get(j, String.class));
        }
      }
  }

 

추가적으로 실제 프로젝트에서 사용한 방법은 querydsl을 통해 내역 목록을 List<Tuple> 형식으로 받아와서 아래와 같이 각 row를 가공하여 데이터를 만들었습니다.