엑셀 모듈 사용 가이드
개요
조회된 데이터를 엑셀파일로 만들어주거나 사용자에게 다운받을 수 있도록 제공해준다.
엑셀파일을 import가능한 형태의 데이터로 만들어주어 개발자에게 편의성을 제공한다.
Apache Poi 기반으로 작성되어 있어 apache poi 가 내장되어있고 제공해주는 기능 외에 apche poi를 직접 쓸수있다.
엑셀 다운로드
프로젝트 생성(chamomile-sample-boot-basic)
dependency 등록(pom.xml)
<dependency> <groupId>net.lotte.chamomile.module</groupId> <artifactId>chamomile-file-excel</artifactId> </dependency>yml 파일 작성(application.yml)
chmm: file: repositoryPath: /files allowExtension: "" maxSize: 20MB directoryDateNameFormat: yyyy/MM엑셀용 VO 생성
BoardExcelVO.java@Getter @Setter @NoArgsConstructor @SuperBuilder public class BoardExcelVO extends TimeAuthorLog { private Long id; private String title; private String content; public BoardExcelVO(Long id, String title, String content) { this.id = id; this.title = title; this.content = content; } }mapper 및 쿼리 작성
BoardMapper.javaList<BoardExcelVO> getAllBoardsListExcel();resources/sql/service/BoardMapper.xml
<!-- Create --> <select id="getAllBoardsListExcel" resultType="demo.board.domain.BoardExcelVO"> SELECT * FROM BOARD </select>서비스 인터페이스 및 서비스 구현체 작성.
BoardService.javaList<BoardExcelVO> getAllBoardsExcelList();BoardServiceImpl.java
@Override @Transactional(readOnly = true) public List<BoardExcelVO> getAllBoardsExcelList() { return boardMapper.getAllBoardsListExcel(); }컨트롤러 작성
BoardController.java@GetMapping("/excel/download") public ResponseEntity<byte[]> excelDownload() throws IOException { List<BoardExcelVO> result = boardService.getAllBoardsExcelList(); ExcelExporter excelExporter = new ExcelExporter("board"); excelExporter.addDataList(result); return excelExporter.toHttpResponse(); }
엑셀 업로드
프로젝트 생성(chamomile-sample-boot-basic)
dependency 등록(pom.xml)
<dependency> <groupId>net.lotte.chamomile.module</groupId> <artifactId>chamomile-file-excel</artifactId> </dependency>yml 파일 작성(application.yml)
chmm: file: repositoryPath: /files allowExtension: "" maxSize: 20MB directoryDateNameFormat: yyyy/MM엑셀용 VO 생성
BoardExcelVO.java@Getter @Setter @NoArgsConstructor @SuperBuilder public class BoardExcelVO extends TimeAuthorLog { private Long id; private String title; private String content; public BoardExcelVO(Long id, String title, String content) { this.id = id; this.title = title; this.content = content; } }mapper 및 쿼리 작성
BoardMapper.javavoid insertResourceExcel(List<ResourceExcelVO> command, Batchable batchable);resources/sql/service/BoardMapper.xml
<!-- Create --> <insert id="insertBoardExcel" parameterType="demo.board.domain.Board"> INSERT INTO BOARD (ID, TITLE, CONTENT, SYS_INSERT_DTM, SYS_UPDATE_DTM) VALUES (#{id}, #{title}, #{content}, #{sysInsertDtm}, #{sysUpdateDtm}) </insert>서비스 인터페이스 및 서비스 구현체 작성.
BoardService.javavoid createBoard(List<BoardExcelVO> request);BoardServiceImpl.java
@Override public void createBoard(List<BoardExcelVO> request) { request.forEach(TimeAuthorLog::onCreate); boardMapper.insertBoardExcel(request, new BatchRequest(1000)); }컨트롤러 작성
BoardController.java@PostMapping(value = "/excel/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ChamomileResponse<Void> excelUpload(@RequestParam("file") MultipartFile file) { FileMetadataInfoEntity fileVo = fileUploader.fileUpload(file); List<BoardExcelVO> list = new ExcelImporter().toCustomClass(fileVo.getRealFilePath(), BoardExcelVO.class); boardService.createBoard(list); return new ChamomileResponse<>(); }
엑셀 API
클래스 | 메서드명 | 파라미터 | 반환값 | 설명 |
---|---|---|---|---|
ExcelCommonUtil | getColumnInfo | Sheet sheet, int firstRowIndex | List\<Column\> | 지정된 sheet의 요청된 행에서 column정보를 얻어온다. |
getExcel | String path | Workbook | 파일을 읽어 workbook객체를 반환한다. | |
getSheets | Workbook workbook | List\<Sheet\> | workbook으로 부터 sheet정보들을 읽어와 list로 반환한다. | |
invokeMethod | String fieldName, Object obj | Object | 필드명으로 get메서드를 실행하여 값을 반환한다. | |
getListFromExcel | SpreadSheet ss, String sheetName, Class\<?\> clazz | List\<?\> | 엑셀에서 추출한 SpreadSheet객체를 List형태로 반환한다. | |
arrangeFields | Class\<?\> clazz | List\<Field\> | excel export시 annotation에 index로 정의된 순서대로 정렬 한 후 annotation이 정의된 field목록들만 반환한다. | |
ExcelDataHandler | makeViewTitle | List\<?\> data, Sheet sheet, Row row, Cell cell | 타이틀 행을 만든다. 설정된 최대 열 이상의 데이터는 무시된다. | |
makeViewRows | List\<?\> data, Sheet sheet, Row row, Cell cell, Locale locale | int | 데이터 타입별로 알맞게 각 행들을 만든다. 설정된 최대 행/최대 열 이상의 데이터는 무시된다. | |
getRows | Sheet sheet, int firstRowIndex | List\<RowElement\> | 엑셀 sheet에서 행/셀 데이터를 추출하여 RowElement로 변환한다. | |
\- ExcelFile - ExcelViewer | setLocale | Locale locale | 로케일정보를 저장한다. | |
setDocName (ExcelViewer에만 해당됨) | String docName | 파일명을 지정한다. | ||
addData | List\<?\> data | 조회된 데이터를 추가한다. 이 때 sheet명은 annotation에 지정된 이름으로 한다. 중복될 경우 뒤에 ‘\#’이 자동으로 추가된다. | ||
addData | List\<?\> data, String sheetName | sheet네임을 지정하여 조회된 데이터를 추가한다. 이때 annotatino에 지정된 이름은 무시된다. 중복될 경우 뒤에 ‘\#’이 자동으로 추가된다. | ||
init | 리스트를 초기화해준다. | |||
makeExcelFile (ExcelFile에만 해당됨) | String filePath | 엑셀 파일을 생성하여 서버에 저장한다. | ||
makeExcel | List\<List\<?\>\> data, List\<SheetElement\> sheetInfo, Workbook workbook | 주어진 데이터로 엑셀파일을 구성하는 메서드. 재정의시 해당 메서드를 이용하여 커스터마이징한다. | ||
ImportExcel | importData | String path | SpreadSheet | 엑셀파일의 로컬경로를 매개변수로 넘겨 SpreadSheet객체로 받는다 |
LargeDataExport | createLargeExcel | ResultSet rs, String path | boolean | 대용량 엑셀파일을 생성한다. 이 기능이 완료 되기 전까지 connection, Statement등을 close시키지 말아야 한다. 기존파일은 덮어 쓴다. |
SpreadSheet | addSheet | SheetElement sheet | SpreadSheet객체는 엑셀파일 한 개로 인식하면 된다. 이 메소드는 sheet를 추가한다. | |
get | String sheetName | SheetElement | sheet명으로 객체내의 sheet를가져온다. | |
get | int index | SheetElement | index로 객체내의 sheet를가져온다. | |
getSheets | List\<SheetElement\> | 객체내의 모든 sheet를가져온다. | ||
SheetElement | SheetElement | String sheetName | 생성자. sheet명을 지정하여 생성한다. | |
addRow | RowElement row | sheet내에 행을 추가한다. | ||
setRows | List\<RowElement\> rows | sheet내에 행을 지정한다. | ||
get | int index | RowElement | 지정된 행을 얻어온다. | |
getSheetName | String | 현재 sheet명을 얻어온다. | ||
setSheetName | String sheetName | sheet명을 지정한다. | ||
size | int | 전체 행 수를 반환한다. | ||
getRows | List\<RowElement\> | 전체 행을 반한다. | ||
RowElement | get | String columName | String | 컬럼 이름으로 데이터를 얻어온다. |
get | int index | String | 인덱스 번호로 데이터를 얻어온다. | |
addCell | CellElement cell | 행에 데이터를 추가한다 |
Excel Export시 사용되는 annotation정보
anntation명 | 속성 | 기본값 | 설명 |
---|---|---|---|
ExcelSheet - 클래스에 적용한다. | name | sheet | 기본sheet명이며 엑셀파일 생성시 addData에서 sheet명을 넘길경우 무시된다. |
maxCols | 500 | 최대 열 수 cf.) Excel 97\~2003 형식(*,xls) : 65,536행/256열 Excel 2007\~2013 (*.xlsx/xlsm) : 1,048,576행x16,384열 | |
maxRows | 2000 | 최대 행 수 cf.) Excel 97\~2003 형식(*,xls) : 65,536행/256열 Excel 2007\~2013 (*.xlsx/xlsm) : 1,048,576행x16,384열 | |
foregroundColor | 0x16 (HSSFColor .HSSFColorPredefined .GREY_25_PERCENT .getIndex()) | 배경색 cf. public enum HSSFColorPredefined {BLACK (0x08, -1, 0x000000), BROWN (0x3C, -1, 0x993300), OLIVE_GREEN (0x3B, -1, 0x333300), DARK_GREEN (0x3A, -1, 0x003300), DARK_TEAL (0x38, -1, 0x003366), DARK_BLUE (0x12, 0x20, 0x000080), INDIGO (0x3E, -1, 0x333399), GREY_80_PERCENT (0x3F, -1, 0x333333), ORANGE (0x35, -1, 0xFF6600), DARK_YELLOW (0x13, -1, 0x808000), GREEN (0x11, -1, 0x008000), TEAL (0x15, 0x26, 0x008080), BLUE (0x0C, 0x27, 0x0000FF), BLUE_GREY (0x36, -1, 0x666699), GREY_50_PERCENT (0x17, -1, 0x808080), RED (0x0A, -1, 0xFF0000), LIGHT_ORANGE (0x34, -1, 0xFF9900), LIME (0x32, -1, 0x99CC00), SEA_GREEN (0x39, -1, 0x339966), AQUA (0x31, -1, 0x33CCCC), LIGHT_BLUE (0x30, -1, 0x3366FF), VIOLET (0x14, 0x24, 0x800080), GREY_40_PERCENT (0x37, -1, 0x969696), PINK (0x0E, 0x21, 0xFF00FF), GOLD (0x33, -1, 0xFFCC00), YELLOW (0x0D, 0x22, 0xFFFF00), BRIGHT_GREEN (0x0B, -1, 0x00FF00), TURQUOISE (0x0F, 0x23, 0x00FFFF), DARK_RED (0x10, 0x25, 0x800000), SKY_BLUE (0x28, -1, 0x00CCFF), PLUM (0x3D, 0x19, 0x993366), GREY_25_PERCENT (0x16, -1, 0xC0C0C0), ROSE (0x2D, -1, 0xFF99CC), LIGHT_YELLOW (0x2B, -1, 0xFFFF99), LIGHT_GREEN (0x2A, -1, 0xCCFFCC), LIGHT_TURQUOISE (0x29, 0x1B, 0xCCFFFF), PALE_BLUE (0x2C, -1, 0x99CCFF), LAVENDER (0x2E, -1, 0xCC99FF), WHITE (0x09, -1, 0xFFFFFF), CORNFLOWER_BLUE (0x18, -1, 0x9999FF), LEMON_CHIFFON (0x1A, -1, 0xFFFFCC), MAROON (0x19, -1, 0x7F0000), ORCHID (0x1C, -1, 0x660066), CORAL (0x1D, -1, 0xFF8080), ROYAL_BLUE (0x1E, -1, 0x0066CC), LIGHT_CORNFLOWER_BLUE (0x1F, -1, 0xCCCCFF), TAN (0x2F, -1, 0xFFCC99), AUTOMATIC (0x40, -1, 0x000000); | |
pattern | FillPatternType .SOLID_FOREGROUND | 배경 패턴 cf. public enum FillPatternType { /** No background */NO_FILL (0), /** Solidly filled */SOLID_FOREGROUND (1), /** Small fine dots */FINE_DOTS (2), /** Wide dots */ALT_BARS (3), /** Sparse dots */SPARSE_DOTS (4), /** Thick horizontal bands */THICK_HORZ_BANDS (5), /** Thick vertical bands */THICK_VERT_BANDS (6), /** Thick backward facing diagonals */THICK_BACKWARD_DIAG (7), /** Thick forward facing diagonals */THICK_FORWARD_DIAG (8), /** Large spots */BIG_SPOTS (9), /** Brick-like layout */BRICKS (10), /** Thin horizontal bands */THIN_HORZ_BANDS (11), /** Thin vertical bands */THIN_VERT_BANDS (12), /** Thin backward diagonal */THIN_BACKWARD_DIAG (13), /** Thin forward diagonal */THIN_FORWARD_DIAG (14), /** Squares */SQUARES (15), /** Diamonds */DIAMONDS (16), /** Less Dots */LESS_DOTS (17), /** Least Dots */LEAST_DOTS (18); | |
borderLeft | BorderStyle.THIN | 왼쪽줄 cf. public enum BorderStyle { /** * No border (default) */NONE (0x0), /** * Thin border */THIN (0x1), /** * Medium border */MEDIUM (0x2), /** * dash border */DASHED (0x3), /** * dot border */DOTTED (0x4), /** * Thick border */THICK (0x5), /** * double-line border */DOUBLE (0x6), /** * hair-line border */HAIR (0x7), /** * Medium dashed border */MEDIUM_DASHED (0x8), /** * dash-dot border */DASH_DOT (0x9), /** * medium dash-dot border */MEDIUM_DASH_DOT (0xA), /** * dash-dot-dot border */DASH_DOT_DOT (0xB), /** * medium dash-dot-dot border */MEDIUM_DASH_DOT_DOT (0xC), /** * slanted dash-dot border */SLANTED_DASH_DOT (0xD); | |
borderRight | BorderStyle.THIN | 오른쪽줄 | |
borderTop | BorderStyle.THIN | 윗쪽줄 | |
borderBottom | BorderStyle.THIN | 아래쪽줄 | |
ExcelColumn - 출력 하고자 하는 필드에 적용한다. | name | 공백문자 | 타이틀에 표시할 컬럼명을 입력한다. |
width | 4000 | 컬럼 폭 | |
l10n | 공백 | 지역화(date, currency) | |
privacyMasking | 10 | 개인정보 필드 마스킹 | |
index | 0 | 데이터 표시순서 | |
foregroundColor | 0x16 (HSSFColor .HSSFColorPredefined .GREY_25_PERCENT .getIndex()) | 배경색 | |
pattern | FillPatternType .SOLID_FOREGROUND | 배경패턴 | |
borderLeft | BorderStyle.THIN | 왼쪽줄 | |
borderRight | BorderStyle.THIN | 오른쪽줄 | |
borderTop | BorderStyle.THIN | 윗쪽줄 | |
borderBottom | BorderStyle.THIN | 아래쪽줄 |
대용량 엑셀 파일 다운로드
엑셀 파일로 대용량 데이터를 다운로드 받을 때 저장된 건 수가 많다면 부하가 발생하여 OutOfMemoryError
가 발생한다.

따라서 대용량 데이터를 엑셀로 다운받으려는 경우 다음과 같은 사용법으로 엑셀 다운로드를 진행해야한다. 다음 코드는 어드민 샘플코드에 있는 예제이다.
Mapper 작성(net.lotte.chamomile.admin.resource.domain.ResourceMapper)
List<ResourceExcelVO> findResourceListDataExcel2();쿼리 작성(resources/sql/service/resource.xml)
<select id="findResourceListDataExcel2" resultType="net.lotte.chamomile.admin.resource.domain.ResourceExcelVO"> SELECT RESOURCE_ID resourceId, /* 리소스 아이디 */ RESOURCE_URI resourceUri, /* 리소스 경로 */ RESOURCE_NAME resourceName, /* 리소스 명 */ RESOURCE_HTTPMETHOD resourceHttpMethod, /* http method */ RESOURCE_DESC resourceDesc, /* 리소스_설명 */ SECURITY_ORDER securityOrder, /* 리소스 순서 */ USE_YN useYn, /* 사용 여부 */ SYS_INSERT_DTM sysInsertDtm, /* 시스템_입력_일시 */ SYS_INSERT_USER_ID sysInsertUserId, /* 시스템_입력_사용자_아이디 */ SYS_UPDATE_DTM sysUpdateDtm, /* 시스템_수정_일시 */ SYS_UPDATE_USER_ID sysUpdateUserId /* 시스템_수정_사용자_아이디 */ FROM CHMM_RESOURCE_INFO A </select>컨트롤러 작성(ResourceController.java)
@GetMapping(path = "/testBatchExcel") public Void testBatchExcelResource(HttpServletResponse response) { ExcelStreamResultHandler handler = new ExcelStreamResultHandler(response,"test.xlsx"); BatchExcelMapper batchExcelMapper = new BatchExcelMapper(handler, "net.lotte.chamomile.admin.resource.domain.ResourceMapper.findResourceListDataExcel2"); batchExcelMapper.writeExcelFileOnHttpResponse(); return null; }
해당 코드는 캐모마일 어드민 샘플코드를 실행하여 swagger로 직접 테스트해볼 수 있다.
한계점
엑셀 최대 row를 넘을 수 없기 때문에 100만건 이하의 데이터만 가져올 수 있다.
해당 작업이 진행되는데에 개발 컴퓨터 기준으로 100만건의 데이터를 가져오는데 110초가 소요되었다. 서버의 용량이 낮다면 더욱 많은 시간이 소요될 수 있다.
권장되는 row 수는 5만 row까지 이며, 해당 경우 1초 이내에 작업이 처리되었다. 이 이상의 row의 경우 처리지연을 부담해야한다.
5만 row 이상은 다음과 같은 작업을 권장한다.
async 처리를 통해 화면에서 따로 작업을 진행
batch 솔루션을 통해 작업 진행
상용 솔루션 구매