xlsx 파일 주입 완료

This commit is contained in:
sheetEasy AI Team
2025-06-24 16:38:17 +09:00
parent 164db92e06
commit 105265a384
7 changed files with 2991 additions and 196 deletions

View File

@@ -0,0 +1,326 @@
---
description:
globs:
alwaysApply: false
---
# Univer CE 초기화 및 인스턴스 관리 규칙
## **핵심 원칙**
- **단일 인스턴스**: 컴포넌트당 하나의 Univer 인스턴스만 유지
- **중복 방지**: 플러그인 등록과 초기화 중복 실행 방지
- **적절한 정리**: 메모리 누수 방지를 위한 dispose 패턴
## **초기화 패턴**
### ✅ DO: 안전한 초기화
```typescript
// 인스턴스 존재 확인 후 초기화
useEffect(() => {
if (!univerRef.current) {
initializeUniver();
}
}, []);
// 처리 중 상태 확인으로 중복 방지
const processFile = useCallback(async (file: File) => {
if (isProcessing) {
console.log("이미 파일 처리 중입니다.");
return;
}
setIsProcessing(true);
// ... 파일 처리 로직
}, [isProcessing]);
```
### ❌ DON'T: 중복 초기화 유발
```typescript
// 조건 없는 초기화 (중복 실행 위험)
useEffect(() => {
initializeUniver(); // 매번 실행됨
}, []);
// 상태 확인 없는 처리
const processFile = useCallback(async (file: File) => {
// isProcessing 확인 없이 바로 실행
setIsProcessing(true);
}, []);
```
## **인스턴스 관리**
### ✅ DO: 적절한 dispose 패턴
```typescript
// 새 인스턴스 생성 전 기존 인스턴스 정리
if (univerRef.current) {
univerRef.current.dispose();
univerRef.current = null;
}
// 컴포넌트 언마운트 시 정리
useEffect(() => {
return () => {
if (univerRef.current) {
univerRef.current.dispose();
univerRef.current = null;
}
};
}, []);
```
### ❌ DON'T: 메모리 누수 위험
```typescript
// dispose 없이 새 인스턴스 생성
univerRef.current = univer.createUnit(UnitType.UNIVER_SHEET, workbook);
// 정리 로직 없는 컴포넌트
// useEffect cleanup 누락
```
## **파일 처리 최적화**
### ✅ DO: 중복 처리 방지
```typescript
// 파일 입력 초기화로 재선택 가능
finally {
if (event.target) {
event.target.value = "";
}
}
// 의존성 배열에 상태 포함
const processFile = useCallback(async (file: File) => {
// ... 로직
}, [isProcessing]);
```
## **REDI 중복 로드 방지**
### ✅ DO: 라이브러리 중복 확인
```typescript
// 개발 환경에서 REDI 중복 로드 경고 모니터링
// 동일한 라이브러리 버전 사용 확인
// 번들링 시 중복 제거 설정
```
### ❌ DON'T: 무시하면 안 되는 경고
```typescript
// REDI 중복 로드 경고 무시
// 서로 다른 버전의 동일 라이브러리 사용
// 번들 중복 제거 설정 누락
```
## **디버깅 및 로깅**
### ✅ DO: 의미있는 로그
```typescript
console.log("초기화할 워크북 데이터:", workbookData);
console.log("Univer 인스턴스 생성 완료");
console.log("플러그인 등록 완료");
```
### ❌ DON'T: 과도한 로깅
```typescript
// 매 렌더링마다 로그 출력
// 민감한 데이터 로깅
// 프로덕션 환경 디버그 로그 유지
```
## **성능 최적화**
- **useCallback**: 이벤트 핸들러 메모이제이션
- **의존성 최적화**: 필요한 의존성만 포함
- **조건부 실행**: 불필요한 재실행 방지
- **메모리 관리**: 적절한 dispose와 cleanup
이 규칙을 따르면 Univer CE가 안정적으로 작동하고 메모리 누수 없이 파일 처리가 가능합니다.
# Univer CE REDI 중복 로드 오류 완전 방지 규칙
## **문제 원인**
- Univer CE의 REDI 시스템에서 "Identifier already exists" 오류는 동일한 서비스가 중복으로 등록될 때 발생
- 컴포넌트 재마운트, HMR(Hot Module Reload), 또는 동시 초기화 시도로 인한 중복 인스턴스 생성
- 브라우저 캐시에 잔존하는 이전 REDI 등록 정보
## **필수 해결 패턴**
### **1. 전역 인스턴스 관리자 패턴 사용**
```typescript
// ✅ DO: 모듈 레벨에서 단일 인스턴스 보장
let globalUniver: Univer | null = null;
let globalInitializing = false;
let globalDisposing = false;
const UniverseManager = {
async createInstance(container: HTMLElement): Promise<Univer> {
if (globalUniver) return globalUniver;
if (globalInitializing) {
// 초기화 완료까지 대기
while (globalInitializing) {
await new Promise((resolve) => setTimeout(resolve, 50));
}
if (globalUniver) return globalUniver;
}
globalInitializing = true;
try {
// Univer 인스턴스 생성 로직
globalUniver = new Univer({...});
return globalUniver;
} finally {
globalInitializing = false;
}
},
getInstance(): Univer | null {
return globalUniver;
}
};
// ❌ DON'T: 컴포넌트마다 개별 인스턴스 생성
const MyComponent = () => {
const [univer, setUniver] = useState<Univer | null>(null);
useEffect(() => {
const newUniver = new Univer({...}); // 중복 생성 위험
setUniver(newUniver);
}, []);
};
```
### **2. 상태 기반 중복 초기화 방지**
```typescript
// ✅ DO: 강화된 상태 확인으로 중복 실행 완전 차단
const processFile = useCallback(async (file: File) => {
if (
isProcessing ||
UniverseManager.isInitializing() ||
UniverseManager.isDisposing()
) {
console.log("처리 중이거나 상태 변경 중입니다.");
return; // 중복 실행 차단
}
setIsProcessing(true);
try {
// 파일 처리 로직
} finally {
setIsProcessing(false);
}
}, [isProcessing]); // 의존성 배열에 상태 포함
// ❌ DON'T: 간단한 상태 확인만으로는 불충분
const processFile = useCallback(async (file: File) => {
if (isProcessing) return; // 다른 상태는 확인하지 않음
}, []);
```
### **3. 컴포넌트 마운트 시 기존 인스턴스 재사용**
```typescript
// ✅ DO: 기존 전역 인스턴스 우선 재사용
useEffect(() => {
const existingUniver = UniverseManager.getInstance();
if (existingUniver && !UniverseManager.isInitializing()) {
setIsInitialized(true);
return; // 재사용으로 중복 초기화 방지
}
if (containerRef.current && !UniverseManager.isInitializing()) {
initializeUniver();
}
}, []);
// ❌ DON'T: 매번 새로운 초기화 시도
useEffect(() => {
initializeUniver(); // 기존 인스턴스 무시하고 새로 생성
}, []);
```
### **4. 디버깅을 위한 전역 상태 제공**
```typescript
// ✅ DO: 전역 디버그 객체로 상태 추적 가능
if (typeof window !== "undefined") {
(window as any).__UNIVER_DEBUG__ = {
getGlobalUniver: () => globalUniver,
getGlobalInitializing: () => globalInitializing,
clearGlobalState: () => {
globalUniver = null;
globalInitializing = false;
globalDisposing = false;
},
};
}
```
### **5. 기존 워크북 정리 시 API 호환성 처리**
```typescript
// ✅ DO: try-catch로 API 버전 차이 대응
try {
const existingUnits =
(univer as any).getUnitsForType?.(UniverInstanceType.UNIVER_SHEET) || [];
for (const unit of existingUnits) {
(univer as any).disposeUnit?.(unit.getUnitId());
}
} catch (error) {
console.log("기존 워크북 정리 시 오류 (무시 가능):", error);
}
// ❌ DON'T: API 호환성 고려하지 않은 직접 호출
univer.getUnitsForType(UniverInstanceType.UNIVER_SHEET); // 버전에 따라 실패 가능
```
## **브라우저 캐시 완전 삭제**
### **개발 환경에서 REDI 오류 발생 시 필수 작업**
```bash
# ✅ DO: 브라우저 캐시 완전 삭제
rm -rf node_modules/.vite && rm -rf dist
# 추가적으로 브라우저에서:
# - 강제 새로고침 (Ctrl+Shift+R 또는 Cmd+Shift+R)
# - 개발자 도구 > Application > Storage > Clear storage
```
## **오류 패턴 및 해결책**
### **"Identifier already exists" 오류**
- **원인**: REDI 시스템에서 동일한 식별자의 서비스가 중복 등록
- **해결**: 전역 인스턴스 관리자 패턴으로 단일 인스턴스 보장
### **컴포넌트 재마운트 시 중복 초기화**
- **원인**: useEffect가 매번 새로운 초기화 시도
- **해결**: 기존 전역 인스턴스 존재 확인 후 재사용
### **HMR 환경에서 상태 불안정**
- **원인**: 모듈 재로드 시 전역 상태 초기화
- **해결**: 브라우저 캐시 삭제 + window 객체 기반 상태 추적
## **성능 최적화**
### **초기화 대기 로직**
```typescript
// ✅ DO: Promise 기반 비동기 대기
while (globalInitializing) {
await new Promise((resolve) => setTimeout(resolve, 50));
}
// ❌ DON'T: 동기 대기나 긴 interval
setInterval(() => { /* 체크 로직 */ }, 1000); // 너무 느림
```
### **메모리 누수 방지**
```typescript
// ✅ DO: 컴포넌트 언마운트 시 상태만 정리
useEffect(() => {
return () => {
setIsInitialized(false);
// 전역 인스턴스는 앱 종료 시에만 정리
};
}, []);
```
## **참고사항**
- 이 패턴은 Univer CE의 REDI 아키텍처 특성상 필수적임
- 전역 인스턴스 관리로 브라우저 재로드 없이 안정적 운용 가능
- 개발 환경에서 오류 발생 시 반드시 캐시 삭제 후 재시작