173 lines
4.5 KiB
TypeScript
173 lines
4.5 KiB
TypeScript
/**
|
|
* 엑셀 내보내기 유틸리티
|
|
* xlsx 라이브러리를 사용하여 데이터를 엑셀 파일로 변환
|
|
*/
|
|
|
|
import * as XLSX from "xlsx";
|
|
|
|
/**
|
|
* 데이터를 엑셀 파일로 내보내기
|
|
* @param data 내보낼 데이터 배열
|
|
* @param fileName 파일명 (기본: "export.xlsx")
|
|
* @param sheetName 시트명 (기본: "Sheet1")
|
|
* @param includeHeaders 헤더 포함 여부 (기본: true)
|
|
*/
|
|
export async function exportToExcel(
|
|
data: Record<string, any>[],
|
|
fileName: string = "export.xlsx",
|
|
sheetName: string = "Sheet1",
|
|
includeHeaders: boolean = true
|
|
): Promise<void> {
|
|
try {
|
|
console.log("📥 엑셀 내보내기 시작:", {
|
|
dataCount: data.length,
|
|
fileName,
|
|
sheetName,
|
|
includeHeaders,
|
|
});
|
|
|
|
if (data.length === 0) {
|
|
throw new Error("내보낼 데이터가 없습니다.");
|
|
}
|
|
|
|
// 워크북 생성
|
|
const workbook = XLSX.utils.book_new();
|
|
|
|
// 데이터를 워크시트로 변환
|
|
const worksheet = XLSX.utils.json_to_sheet(data, {
|
|
header: includeHeaders ? undefined : [],
|
|
skipHeader: !includeHeaders,
|
|
});
|
|
|
|
// 컬럼 너비 자동 조정
|
|
const columnWidths = autoSizeColumns(data);
|
|
worksheet["!cols"] = columnWidths;
|
|
|
|
// 워크시트를 워크북에 추가
|
|
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
|
|
|
|
// 파일 다운로드
|
|
XLSX.writeFile(workbook, fileName);
|
|
|
|
console.log("✅ 엑셀 내보내기 완료:", fileName);
|
|
} catch (error) {
|
|
console.error("❌ 엑셀 내보내기 실패:", error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 컬럼 너비 자동 조정
|
|
*/
|
|
function autoSizeColumns(data: Record<string, any>[]): Array<{ wch: number }> {
|
|
if (data.length === 0) return [];
|
|
|
|
const keys = Object.keys(data[0]);
|
|
const columnWidths: Array<{ wch: number }> = [];
|
|
|
|
keys.forEach((key) => {
|
|
// 헤더 길이
|
|
let maxWidth = key.length;
|
|
|
|
// 데이터 길이 확인
|
|
data.forEach((row) => {
|
|
const value = row[key];
|
|
const valueLength = value ? String(value).length : 0;
|
|
maxWidth = Math.max(maxWidth, valueLength);
|
|
});
|
|
|
|
// 최소 10, 최대 50으로 제한
|
|
columnWidths.push({ wch: Math.min(Math.max(maxWidth, 10), 50) });
|
|
});
|
|
|
|
return columnWidths;
|
|
}
|
|
|
|
/**
|
|
* 엑셀 파일을 읽어서 JSON 데이터로 변환
|
|
* @param file 읽을 파일
|
|
* @param sheetName 읽을 시트명 (기본: 첫 번째 시트)
|
|
* @returns JSON 데이터 배열
|
|
*/
|
|
export async function importFromExcel(
|
|
file: File,
|
|
sheetName?: string
|
|
): Promise<Record<string, any>[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = (e) => {
|
|
try {
|
|
const data = e.target?.result;
|
|
if (!data) {
|
|
reject(new Error("파일을 읽을 수 없습니다."));
|
|
return;
|
|
}
|
|
|
|
// 워크북 읽기
|
|
const workbook = XLSX.read(data, { type: "binary" });
|
|
|
|
// 시트 선택 (지정된 시트 또는 첫 번째 시트)
|
|
const targetSheetName = sheetName || workbook.SheetNames[0];
|
|
const worksheet = workbook.Sheets[targetSheetName];
|
|
|
|
if (!worksheet) {
|
|
reject(new Error(`시트 "${targetSheetName}"를 찾을 수 없습니다.`));
|
|
return;
|
|
}
|
|
|
|
// JSON으로 변환
|
|
const jsonData = XLSX.utils.sheet_to_json(worksheet);
|
|
|
|
console.log("✅ 엑셀 가져오기 완료:", {
|
|
sheetName: targetSheetName,
|
|
rowCount: jsonData.length,
|
|
});
|
|
|
|
resolve(jsonData as Record<string, any>[]);
|
|
} catch (error) {
|
|
console.error("❌ 엑셀 가져오기 실패:", error);
|
|
reject(error);
|
|
}
|
|
};
|
|
|
|
reader.onerror = () => {
|
|
reject(new Error("파일 읽기 중 오류가 발생했습니다."));
|
|
};
|
|
|
|
reader.readAsBinaryString(file);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 엑셀 파일의 시트 목록 가져오기
|
|
*/
|
|
export async function getExcelSheetNames(file: File): Promise<string[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = (e) => {
|
|
try {
|
|
const data = e.target?.result;
|
|
if (!data) {
|
|
reject(new Error("파일을 읽을 수 없습니다."));
|
|
return;
|
|
}
|
|
|
|
const workbook = XLSX.read(data, { type: "binary" });
|
|
resolve(workbook.SheetNames);
|
|
} catch (error) {
|
|
console.error("❌ 시트 목록 가져오기 실패:", error);
|
|
reject(error);
|
|
}
|
|
};
|
|
|
|
reader.onerror = () => {
|
|
reject(new Error("파일 읽기 중 오류가 발생했습니다."));
|
|
};
|
|
|
|
reader.readAsBinaryString(file);
|
|
});
|
|
}
|
|
|