사용자를 위해 클라이언트에서 만들어진 데이터를 csv 파일로 만들어 다운로드 가능하게 하기 위해서는 어떻게 해야 할까?
큰 흐름은 아래와 같다.
<button
onClick={onCsvownload}
style={{
background: "none",
border: "none",
cursor: "pointer",
padding: 0,
}}
>
<RiFileExcel2Line size={24} style={{ color: "green" }} />
</button>
위와 같은 테이블이 있고 우측 상단에 excel 아이콘을 누르면 테이블의 내용을 저장하고자 한다.
그럼 예제 코드를 살펴보자.
const onCsvDownload = async () => {
try {
const csvData = [
[
"No",
"Pos Invoice No",
"Amount",
"Tax",
"VAT",
"Cash Amount",
"Card Amount",
"Order Time",
"Buy Type",
],
...ordersData.map((item, index) => [
index + 1,
item.pos_invoice_no,
item.amount,
item.tax,
item.vat,
item.od_cash_amount,
item.od_card_amount,
item.od_time,
convertBuyType(item.buyType),
]),
];
console.log("csvData", csvData);
console.log("json", JSON.stringify(csvData));
const config = {
method: "post",
url: "/api2/csv",
data: {
csv_data: csvData,
file_name: `orders_${activeOrderTab}_${selectedDate}.csv`,
},
responseType: "blob",
};
const response = await axios.request(config);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute(
"download",
`orders_${activeOrderTab}_${selectedDate}.csv`
);
document.body.appendChild(link);
link.click();
link.remove();
} catch (error) {
console.error("Error downloading Excel:", error);
if (error.response) {
console.error("Response data:", error.response.data);
console.error("Response status:", error.response.status);
}
}
}
window.URL.createObjectURL(new Blob([response.data]))
: 서버로부터 받은 응답 데이터를 Blob
객체로 변환한 후, 브라우저의 메모리에 URL 객체를 생성한다.const link = document.createElement("a");
: a
태그를 동적으로 생성한다.link.href = url;
: 생성한 URL 객체를 a
태그의 href
속성에 할당한다.link.setAttribute("download", "filename.csv");
: download
속성을 설정하여 파일 이름을 지정한다.document.body.appendChild(link);
: a
태그를 문서에 추가한다.link.click();
: 파일 다운로드를 트리거한다.link.remove();
: 다운로드 후 a
태그를 문서에서 제거한다.//controller.java
@PostMapping("/csv")
public ResponseEntity<InputStreamResource> downloadCsv(@RequestBody ExcelDownloadRequestDto request) {
ByteArrayInputStream byteArrayInputStream = taxExcelService.generateCsv(request.csvData());
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + request.fileName());
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.parseMediaType("text/csv"))
.body(new InputStreamResource(byteArrayInputStream));
}
ByteArrayInputStream byteArrayInputStream = taxExcelService.generateCsv(request.csvData());
: generateCsv
메서드를 호출하여 CSV 데이터를 바이트 배열로 변환한다.HttpHeaders headers = new HttpHeaders();
: HTTP 헤더를 설정한다.headers.add("Content-Disposition", "attachment; filename=" + request.fileName());
: 파일 이름을 지정하여 다운로드를 위한 헤더를 추가한다.return ResponseEntity.ok().headers(headers).contentType(MediaType.parseMediaType("text/csv")).body(new InputStreamResource(byteArrayInputStream));
: HTTP 200 응답과 함께 CSV 파일 데이터를 반환한다.//service.java
public ByteArrayInputStream generateCsv(List<List<String>> data) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(out), CSVFormat.DEFAULT);
for (List<String> record : data) {
csvPrinter.printRecord(record);
}
csvPrinter.flush();
return new ByteArrayInputStream(out.toByteArray());
} catch (IllegalArgumentException e) {
log.error("Error generating CSV: {}", e.getMessage());
throw e;
} catch (IOException e) {
log.error("Error generating CSV: {}", e.getMessage());
throw new RuntimeException("Failed to generate CSV file: " + e.getMessage());
}
}
generateCsv
: CSV 데이터를 받아서 바이트 배열로 변환하는 메서드이다.CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(out), CSVFormat.DEFAULT);
: CSV 프린터를 생성하여 데이터를 작성한다.csvPrinter.printRecord(record);
: 데이터를 순회하며 CSV 레코드를 작성한다.이제 버튼을 클릭해보면 설정한 파일명이 다운로드에 나타나고
작성한 csv 파일 내용을 확인할 수 있다.