Files
sheeteasyAI/.cursor/rules/xlsx_debug.mdc
sheetEasy AI Team 3a8c6af7ea feat: 파일프로세서 개선 - 안정적인 Excel 파일 처리
- 이전 잘 작동하던 코드 로직을 현재 프로세서에 적용
- LuckyExcel 우선 시도 + SheetJS Fallback 패턴 도입
- CSV, XLS, XLSX 모든 형식에 대한 안정적 처리
- 한글 시트명 정규화 및 워크북 구조 검증 강화
- 복잡한 SheetJS 옵션 단순화로 안정성 향상
- 에러 발생 시 빈 시트 생성으로 앱 중단 방지
- 테스트 환경 및 Cursor 규칙 업데이트

Technical improvements:
- convertSheetJSToLuckyExcel 함수로 안정적 데이터 변환
- UTF-8 codepage 설정으로 한글 지원 강화
- validateWorkbook 함수로 방어적 프로그래밍 적용
2025-06-20 14:32:33 +09:00

130 lines
3.9 KiB
Plaintext

---
description:
globs:
alwaysApply: false
---
# XLSX 파일 처리 오류 방지 및 디버깅
## **문제 상황**
- SheetJS로 읽은 XLSX 파일에서 `workbook.Sheets`가 undefined이거나 올바른 객체가 아닌 경우
- "파일에 유효한 시트가 없습니다" 오류가 발생하는 경우
- 한글 파일명이나 특수 문자가 포함된 XLSX 파일 처리 시 문제
## **필수 검증 단계**
### **1. 워크북 객체 검증**
```typescript
// ✅ DO: 워크북 구조 전체 디버깅
console.log("🔍 워크북 구조 분석:");
console.log("- workbook 존재:", !!workbook);
console.log("- workbook 전체 키:", Object.keys(workbook));
console.log("- workbook.Sheets 존재:", !!workbook.Sheets);
console.log("- workbook.Sheets 타입:", typeof workbook.Sheets);
// ❌ DON'T: 단순한 truthy 체크만 하지 말 것
if (workbook.Sheets) { ... }
```
### **2. SheetJS 읽기 옵션 최적화**
```typescript
// ✅ DO: 안전한 SheetJS 옵션 사용
workbook = XLSX.read(arrayBuffer, {
type: "array",
cellText: false, // 텍스트 셀 비활성화
cellDates: true, // 날짜 형식 유지
sheetStubs: false, // 빈 셀 스텁 비활성화
codepage: 65001, // UTF-8 설정
bookProps: false, // 워크북 속성 비활성화
bookSheets: true, // 시트 정보만 활성화
raw: false, // 원시 값 비활성화
WTF: false // 엄격 모드 비활성화
});
// ❌ DON'T: 기본 옵션만 사용하지 말 것
workbook = XLSX.read(arrayBuffer);
```
### **3. 단계별 오류 처리**
```typescript
// ✅ DO: 각 단계별 구체적 오류 메시지
if (!workbook) {
throw new Error("파일에서 워크북을 생성할 수 없습니다.");
}
if (!workbook.Sheets || typeof workbook.Sheets !== "object") {
console.error("❌ Sheets 속성 오류:", {
exists: !!workbook.Sheets,
type: typeof workbook.Sheets,
value: workbook.Sheets
});
throw new Error("파일에 유효한 시트가 없습니다.");
}
// ❌ DON'T: 일반적인 오류 메시지만 사용하지 말 것
throw new Error("파일 처리 실패");
```
### **4. 개별 시트 검증**
```typescript
// ✅ DO: 각 시트의 구조 확인
workbook.SheetNames.forEach((sheetName, index) => {
const sheet = workbook.Sheets[sheetName];
console.log(`- 시트 [${index}] "${sheetName}":`, {
exists: !!sheet,
type: typeof sheet,
keys: sheet ? Object.keys(sheet).slice(0, 5) : []
});
});
// ❌ DON'T: 시트 존재만 확인하고 내용 검증 생략하지 말 것
const hasSheets = workbook.SheetNames.length > 0;
```
## **한글 파일 처리 특수 사항**
### **1. 인코딩 처리**
```typescript
// ✅ DO: UTF-8 실패 시 CP949로 재시도 (XLS의 경우)
try {
workbook = XLSX.read(arrayBuffer, { codepage: 65001 }); // UTF-8
} catch (error) {
if (isXLS) {
workbook = XLSX.read(arrayBuffer, { codepage: 949 }); // CP949
}
}
```
### **2. 파일명 및 시트명 검증**
```typescript
// ✅ DO: 한글 시트명 안전 처리
const safeSheetName = sheetName || `Sheet${index + 1}`;
console.log(`🔍 처리 중인 시트: "${safeSheetName}"`);
// ❌ DON'T: 시트명을 검증 없이 직접 사용하지 말 것
console.log(`처리 중: ${sheetName}`);
```
## **성능 최적화**
### **1. 메모리 효율적 처리**
```typescript
// ✅ DO: 큰 파일 처리 시 메모리 체크
if (arrayBuffer.byteLength > 50 * 1024 * 1024) { // 50MB
console.warn("⚠️ 대용량 파일 처리 중...");
}
// ✅ DO: 변환 결과 검증
if (!xlsxBuffer || xlsxBuffer.byteLength === 0) {
throw new Error("XLSX 변환 결과가 비어있습니다.");
}
```
## **테스트 고려사항**
- 한글 파일명이 포함된 XLSX 파일 테스트
- 빈 시트가 포함된 파일 테스트
- 손상된 XLSX 파일 테스트
- 대용량 파일 처리 테스트
- 특수 문자가 포함된 시트명 테스트
참고: [xls_processing.mdc](mdc:.cursor/rules/xls_processing.mdc)와 연계하여 사용