2025-02-03-TIL
2024-02-03-TIL
Today I Learned
Handling Bulk Excel with Spring Boot
대용량 엑셀을 생성하거나 다운로드할 때 시간이 오래 걸릴 수 있으므로, 사용자 경험을 고려하여 비동기 처리와 최적화를 적용하는 것이 중요합니다. 다음과 같은 방법을 고려할 수 있습니다.
✅ 1. 비동기 처리 (Async Processing)
엑셀 생성 작업이 시간이 오래 걸리는 경우, 비동기 처리를 사용하여 사용자 요청을 바로 응답하고 백그라운드에서 작업을 진행하는 것이 좋습니다.
🔹 Spring Boot (Java)에서 비동기 처리
@Async
를 활용하여 백그라운드에서 엑셀을 생성하고, 완료 후 사용자에게 다운로드 링크 제공- 작업 상태를 Redis나 DB에 저장하고, 진행 상황을 조회할 수 있도록 API 제공
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class ExcelService {
@Async
public CompletableFuture<String> generateExcel() {
try {
// 엑셀 생성 로직 (대용량 데이터 처리)
String filePath = "/tmp/report.xlsx";
createExcel(filePath);
return CompletableFuture.completedFuture(filePath);
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
}
🔹 Go에서 비동기 처리 (goroutine + channel 활용)
1
2
3
4
5
6
7
8
9
10
func generateExcelAsync() <-chan string {
result := make(chan string)
go func() {
defer close(result)
filePath := "/tmp/report.xlsx"
generateExcel(filePath)
result <- filePath
}()
return result
}
🔹 Python에서 비동기 처리 (Celery 활용)
Django 또는 Flask 같은 웹 프레임워크를 사용할 경우 Celery + Redis로 비동기 작업을 처리할 수 있습니다.
1
2
3
4
5
6
7
8
9
from celery import Celery
app = Celery('tasks', broker='redis://localhost:6379/0')
@app.task
def generate_excel():
file_path = "/tmp/report.xlsx"
create_excel(file_path)
return file_path
✅ 2. 스트리밍 방식 다운로드 (Chunked Response)
한 번에 메모리에 로드하지 않고 스트리밍 방식으로 데이터를 처리하면 메모리 부담을 줄이고 빠르게 응답할 수 있습니다.
🔹 Spring Boot Streaming Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@GetMapping("/download")
public ResponseEntity<StreamingResponseBody> downloadFile() {
InputStream inputStream = new FileInputStream(new File("/tmp/report.xlsx"));
StreamingResponseBody responseBody = outputStream -> {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=report.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(responseBody);
}
🔹 Go에서 HTTP Streaming
1
2
3
4
5
6
7
8
9
10
11
12
13
func downloadExcelHandler(w http.ResponseWriter, r *http.Request) {
file, err := os.Open("/tmp/report.xlsx")
if err != nil {
http.Error(w, "File not found.", http.StatusNotFound)
return
}
defer file.Close()
w.Header().Set("Content-Disposition", "attachment; filename=report.xlsx")
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
io.Copy(w, file) // 파일을 스트리밍으로 전송
}
✅ 3. 엑셀 생성 최적화 (Memory 효율적 사용)
대용량 엑셀을 처리할 때 POI
, Openpyxl
등의 라이브러리를 사용할 경우 메모리 사용량을 줄이는 방법을 적용할 수 있습니다.
🔹 Java - Apache POI SXSSFWorkbook 사용
SXSSFWorkbook
을 사용하면 메모리에 모든 데이터를 올리지 않고, 디스크 기반으로 엑셀을 처리할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 메모리에 100개 행만 유지
Sheet sheet = workbook.createSheet("Sheet1");
for (int i = 0; i < 1000000; i++) {
Row row = sheet.createRow(i);
row.createCell(0).setCellValue("Data " + i);
}
FileOutputStream out = new FileOutputStream("large_excel.xlsx");
workbook.write(out);
out.close();
workbook.dispose(); // 사용한 메모리 정리
🔹 Python - openpyxl 대신 Pandas + xlsxwriter 사용
1
2
3
4
import pandas as pd
df = pd.DataFrame({"A": range(1, 1000000), "B": range(1000000, 2000000)})
df.to_excel("large_file.xlsx", index=False, engine="xlsxwriter")
✅ 4. 파일을 미리 생성 후 다운로드 (Pre-generated File)
비동기 작업으로 미리 생성한 파일을 Redis나 S3에 저장하고 다운로드할 수 있도록 하면 빠른 응답이 가능함.
🔹 Java - AWS S3에 업로드 후 다운로드 링크 제공
1
2
String presignedUrl = s3Client.generatePresignedUrl(request).toString();
return ResponseEntity.ok(presignedUrl);
🔹 Python - S3 Pre-signed URL 생성
1
2
3
4
5
6
7
import boto3
s3 = boto3.client('s3')
url = s3.generate_presigned_url('get_object',
Params={'Bucket': 'my-bucket', 'Key': 'large_excel.xlsx'},
ExpiresIn=3600)
print(url) # 다운로드 URL 반환
✅ 최적화 방법 정리
| 방법 | 설명 | 적용 기술 | |——|——|———-| | 비동기 처리 | 백그라운드에서 엑셀 생성 후 다운로드 링크 제공 | Spring @Async
, Go goroutine
, Python Celery
| | 스트리밍 다운로드 | 파일 전체를 메모리에 로드하지 않고 스트리밍 | Java StreamingResponseBody
, Go io.Copy()
, Python send_file()
| | 엑셀 최적화 | 메모리 사용량 최소화 | Java SXSSFWorkbook
, Python xlsxwriter
| | 사전 생성 후 다운로드 | S3, Redis에 저장 후 URL 제공 | AWS S3, Redis, Nginx Static File |
이 중에서 가장 효율적인 방법은 비동기 처리 + 스트리밍 다운로드 + 파일 최적화를 조합하는 것입니다. 프로젝트 환경에 맞게 적용하면 좋을 것 같아요! 🚀
References
- 아 엑셀다운로드 개발,,, 쉽고 빠르게 하고 싶다 (feat. 엑셀 다운로드 모듈 개발기)
- Solving Slow Excel Generation using Apache POI
- Spring Boot Export Data to Excel Example
- Hibernate Bulk loading from excel: suggestions for design or approach for better performance
- Boost JPA Bulk Insert Performance by 90%
- Apache POI Java Excel Performance for Large Spreadsheets
- Generate Excel With POI With Most Optimized Way
- Working with Microsoft Excel in Java
- Spring Batch, Excel ItemReader로 읽어서 JPA ItemWriter 쓰기
- Reading and Writing Excel Data in Spring Boot with Apache POI
- Spring Boot Generate Excel with React fetch blob.
- How to read Excel file in Java Spring Boot and upload into Database?
- How to Generate an Excel Report in a Spring Boot REST API with Apache POI and Kotlin
@Async, Virtual Thread, and Completable Future
- 자바의 Virtual Thread가 나와도 코틀린의 코루틴은 여전히 살아남을까?
- Java Virtual Threads 훑어보기
- Java Virtual Thread (10), @async 어노테이션
- Creating Asynchronous Methods
- How To Do @Async in Spring
- Task 실행과 스케줄링 (TaskExecutor, TaskScheduler)
- ThreadPoolTaskExecutor 설정으로 비동기 처리 효과적으로 하기 출처: https://dev-coco.tistory.com/186 [슬기로운 개발생활:티스토리]
- Task Execution and Scheduling
- A Guide to the Java ExecutorService
- Embracing Virtual Threads
- Using Java Virtuals Threads for Asynchronous Programming
- The Future of Async in Java: CompletableFuture vs Virtual Threads
- Java의 미래, Virtual Thread