
엑셀 파일을 동적으로 생성하여 다운로드할 수 있는 기능을 구현해보겠습니다.
이 튜토리얼에서는 Kotlin을 사용하여 Apache POI 라이브러리로 엑셀 파일을 생성하는 방법을
소개합니다.
개발 환경은 IntelliJ IDEA이며, 빌드 도구로는 Gradle을 사용합니다.
먼저, build.gradle.kts 파일에 Apache POI 라이브러리를 추가합니다.
Apache POI는 엑셀 파일을 다루기 위한 라이브러리입니다.
dependencies {
implementation("org.apache.poi:poi:3.17")
implementation("org.apache.poi:poi-ooxml:3.17") // XSSF를 사용할 때 필요
}
엑셀 파일 다운로드를 처리할 컨트롤러를 설정합니다.
이 컨트롤러는 /download/excel 경로로 요청을 받아 엑셀 파일을 반환합니다.
package kr.naver.web.controller
import kr.naver.web.service.BoardExcelView
import kr.naver.web.service.MemberExcelView
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.servlet.ModelAndView
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
class ExcelController {
@Autowired
private lateinit var boardExcelView: BoardExcelView
@Autowired
private lateinit var memberExcelView: MemberExcelView
@GetMapping("/download/board")
fun downloadBoardExcel(
@RequestParam("param") param: String,
@RequestParam("startDay") startDay: String,
@RequestParam("endDay") endDay: String,
@RequestParam("sheetName", defaultValue = "BoardData") sheetName: String,
@RequestParam("fileName", defaultValue = "게시판_데이터") fileName: String
): ModelAndView {
return ModelAndView(boardExcelView).apply {
addObject("title", "게시판")
addObject("headerTitles", listOf("번호", "내용"))
addObject("startDate", startDay)
addObject("endDate", endDay)
addObject("sheetName", sheetName)
addObject("fileName", "$fileName.xls")
}
}
@GetMapping("/download/member")
fun downloadMemberExcel(
@RequestParam("param") param: String,
@RequestParam("startDay") startDay: String,
@RequestParam("endDay") endDay: String,
@RequestParam("sheetName", defaultValue = "MemberData") sheetName: String,
@RequestParam("fileName", defaultValue = "회원_데이터") fileName: String
): ModelAndView {
return ModelAndView(memberExcelView).apply {
addObject("title", "회원")
addObject("headerTitles", listOf("회원ID", "이름", "가입일"))
addObject("startDate", startDay)
addObject("endDate", endDay)
addObject("sheetName", sheetName)
addObject("fileName", "$fileName.xls")
}
}
}
}
엑셀 다운로드 기능을 사용할 수 있도록 HTML에 링크를 추가합니다.
<a href="/download/excel">엑셀 다운로드</a>
엑셀 파일을 생성하는 로직은 Service 클래스에서 구현합니다.
이 클래스는 AbstractXlsView를 상속받아 엑셀 문서를 생성하고,
스타일을 적용하는 방법을 보여줍니다.
package kr.naver.web.service
import org.apache.poi.hssf.util.HSSFColor
import org.apache.poi.ss.usermodel.*
import org.springframework.web.servlet.view.document.AbstractXlsView
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
abstract class AbstractExcelView<T> : AbstractXlsView() {
abstract fun getData(request: HttpServletRequest): List<T>
abstract fun fillData(sheet: Sheet, items: List<T>, bodyStyle: CellStyle)
@Throws(Exception::class)
override fun buildExcelDocument(model: Map<String, Any>, workbook: Workbook, request: HttpServletRequest, response: HttpServletResponse) {
val title = model["title"] as String
val headerTitles = model["headerTitles"] as List<String>
val startDateStr = model["startDate"] as String
val endDateStr = model["endDate"] as String
val sheetName = model["sheetName"] as String? ?: "Sheet1"
val fileName = model["fileName"] as String? ?: "$title_${LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))}.xls"
val items = getData(request)
response.setHeader("Content-Disposition", "attachment; filename=\"$fileName\"")
val titleStyle = createTitleStyle(workbook)
val headStyle = createHeadStyle(workbook)
val bodyStyle = createBodyStyle(workbook)
val sheet = workbook.createSheet(sheetName).apply {
isDisplayGridlines = false
autoSizeColumn(0)
}
createHeader(sheet, title, headerTitles, startDateStr, endDateStr, titleStyle, headStyle)
fillData(sheet, items, bodyStyle)
addTotalRow(sheet, items.size, bodyStyle)
}
private fun createTitleStyle(workbook: Workbook): CellStyle {
return workbook.createCellStyle().apply {
val font = workbook.createFont().apply {
bold = true
fontHeightInPoints = 16
}
setFont(font)
}
}
private fun createHeadStyle(workbook: Workbook): CellStyle {
return workbook.createCellStyle().apply {
val font = workbook.createFont().apply {
color = IndexedColors.WHITE.index
}
alignment = HorizontalAlignment.CENTER
borderTop = BorderStyle.THIN
borderBottom = BorderStyle.THIN
borderLeft = BorderStyle.THIN
borderRight = BorderStyle.THIN
fillForegroundColor = HSSFColor.HSSFColorPredefined.GREY_80_PERCENT.index
fillPattern = FillPatternType.SOLID_FOREGROUND
setFont(font)
}
}
private fun createBodyStyle(workbook: Workbook): CellStyle {
return workbook.createCellStyle().apply {
dataFormat = workbook.createDataFormat().getFormat("#,##0")
borderTop = BorderStyle.THIN
borderBottom = BorderStyle.THIN
borderLeft = BorderStyle.THIN
borderRight = BorderStyle.THIN
}
}
private fun createHeader(sheet: Sheet, title: String, headerTitles: List<String>, startDateStr: String, endDateStr: String, titleStyle: CellStyle, headStyle: CellStyle) {
val row0 = sheet.createRow(0).apply {
createCell(0).apply {
setCellValue(title)
cellStyle = titleStyle
}
}
val row1 = sheet.createRow(1).apply {
createCell(0).setCellValue("조회기간 : ${formatDate(startDateStr)} - ${formatDate(endDateStr)}")
}
val row3 = sheet.createRow(3).apply {
headerTitles.forEachIndexed { index, headerTitle ->
createCell(index).apply {
setCellValue(headerTitle)
cellStyle = headStyle
}
}
}
}
private fun formatDate(dateStr: String): String {
return try {
LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyyMMdd")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
} catch (e: Exception) {
"Invalid Date"
}
}
private fun addTotalRow(sheet: Sheet, itemCount: Int, bodyStyle: CellStyle) {
val rowEnd = sheet.createRow(4 + itemCount)
rowEnd.createCell(0).apply {
setCellValue("총계")
cellStyle = bodyStyle
}
rowEnd.createCell(1).apply {
setCellType(CellType.NUMERIC)
setCellValue(sheet.rowIterator().asSequence()
.drop(4)
.take(itemCount)
.sumOf { row -> row.getCell(1).numericCellValue })
cellStyle = bodyStyle
}
}
}
Spring Boot 2.0을 이용한 엑셀 다운로드 구현 방법
스프링 부트 2.0 엑셀 다운로드 구현2
[java] Excel다운로드 기능구현3
Java Excel 엑셀 다운로드 기능 구현
[Java] POI 라이브러리 사용 : 엑셀 다운로드 로직 구현하기
POI를 이용하여 엑셀 출력하기
[POI] Excel cell style
[java] 엑셀파일쓰기 / 스타일적용
java excel poi (눈금선,병합,배경색,테두리,글씨체,사이즈,금액,천단위)
[Spring] 스프링 엑셀 POI 스타일, 폰트 적용(POI 라이브러리 2편)
이 튜토리얼을 통해 Kotlin과 Apache POI를 이용하여 동적으로 엑셀 파일을 생성하고
다운로드하는 방법을 배웠습니다.
실제로 데이터를 기반으로 엑셀 파일을 만들면서 셀 스타일을 설정하는 부분이 복잡할 수 있지만,
다양한 셀 스타일과 데이터 형식 설정을 통해 원하는 형식으로 엑셀 파일을 만들 수 있었습니다.
이 과정에서 POI 라이브러리의 강력함과 유연함을 느낄 수 있었습니다.