Fix file upload errors and improve error handling

- Fix uploadAttachments method to use proper SupabaseHelper methods
- Add addFileAttachment method for proper database insertion
- Improve error handling with detailed logging and user feedback
- Add file upload progress tracking and cleanup on failure
- Fix direct supabase variable access issues
- Better error messages for users and developers

Bug fixes:
• File upload now works properly with Supabase Storage
• Database insertion errors are properly handled
• Failed uploads are cleaned up automatically
• More informative error messages displayed to users

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-19 14:08:28 +09:00
parent 7bae9abe28
commit a0efe6f637
2 changed files with 100 additions and 16 deletions

View File

@@ -358,20 +358,42 @@ class FileManager {
try { try {
this.updateSyncStatus('syncing'); this.updateSyncStatus('syncing');
console.log('파일 데이터 추가 중...', fileData);
const result = await SupabaseHelper.addFile(fileData, this.currentUser.id); const result = await SupabaseHelper.addFile(fileData, this.currentUser.id);
console.log('파일 데이터 추가 성공:', result);
// 첨부파일이 있는 경우 파일 업로드 처리 // 첨부파일이 있는 경우 파일 업로드 처리
if (fileData.files && fileData.files.length > 0) { if (fileData.files && fileData.files.length > 0) {
console.log(`${fileData.files.length}개의 첨부파일 업로드 시작...`);
await this.uploadAttachments(result.id, fileData.files); await this.uploadAttachments(result.id, fileData.files);
console.log('모든 첨부파일 업로드 완료');
} }
this.showNotification('새 자료가 성공적으로 추가되었습니다!', 'success'); this.showNotification('새 자료가 성공적으로 추가되었습니다!', 'success');
await this.loadUserFiles(); // 목록 새로고침 await this.loadUserFiles(); // 목록 새로고침
this.updateSyncStatus('online'); this.updateSyncStatus('online');
this.clearForm(); // 폼 초기화
} catch (error) { } catch (error) {
console.error('파일 추가 오류:', error); console.error('파일 추가 오류:', error);
this.showNotification('파일 추가 중 오류가 발생했습니다.', 'error');
// 더 구체적인 에러 메시지 제공
let errorMessage = '파일 추가 중 오류가 발생했습니다.';
if (error.message) {
errorMessage += ` (${error.message})`;
}
this.showNotification(errorMessage, 'error');
this.updateSyncStatus('error'); this.updateSyncStatus('error');
// 콘솔에 상세 오류 정보 출력
if (error.details) {
console.error('오류 상세:', error.details);
}
if (error.hint) {
console.error('오류 힌트:', error.hint);
}
} }
} }
@@ -463,35 +485,80 @@ class FileManager {
// 파일 업로드 관련 메서드들 // 파일 업로드 관련 메서드들
async uploadAttachments(fileId, attachments) { async uploadAttachments(fileId, attachments) {
if (!isSupabaseConfigured() || !this.currentUser) { if (!isSupabaseConfigured() || !this.currentUser) {
console.log('오프라인 모드: 첨부파일을 base64로 저장합니다.');
return; // 오프라인 모드에서는 base64로 저장된 상태 유지 return; // 오프라인 모드에서는 base64로 저장된 상태 유지
} }
const uploadedFiles = [];
try { try {
for (const attachment of attachments) { for (let i = 0; i < attachments.length; i++) {
// base64 데이터를 Blob으로 변환 const attachment = attachments[i];
const response = await fetch(attachment.data);
const blob = await response.blob();
// 파일 경로 생성 (사용자별/파일ID별 폴더 구조) try {
const fileName = `${Date.now()}_${attachment.name}`; console.log(`파일 업로드 중... (${i + 1}/${attachments.length}): ${attachment.name}`);
const filePath = `${this.currentUser.id}/${fileId}/${fileName}`;
// base64 데이터를 Blob으로 변환
// Supabase Storage에 업로드 const response = await fetch(attachment.data);
await SupabaseHelper.uploadFile(blob, filePath); const blob = await response.blob();
// 데이터베이스에 첨부파일 정보 저장 // 파일 경로 생성 (사용자별/파일ID별 폴더 구조)
if (supabase) { const fileName = `${Date.now()}_${Math.random().toString(36).substr(2, 9)}_${attachment.name}`;
await supabase.from('file_attachments').insert({ const filePath = `${this.currentUser.id}/${fileId}/${fileName}`;
file_id: fileId,
// Supabase Storage에 업로드
const uploadResult = await SupabaseHelper.uploadFile(blob, filePath);
console.log('Storage 업로드 성공:', uploadResult);
// 데이터베이스에 첨부파일 정보 저장
const attachmentResult = await this.addFileAttachment(fileId, {
original_name: attachment.name, original_name: attachment.name,
storage_path: filePath, storage_path: filePath,
file_size: attachment.size || blob.size, file_size: attachment.size || blob.size,
mime_type: attachment.type || blob.type mime_type: attachment.type || blob.type
}); });
uploadedFiles.push(attachmentResult);
console.log('첨부파일 정보 저장 성공:', attachmentResult);
} catch (fileError) {
console.error(`파일 "${attachment.name}" 업로드 실패:`, fileError);
throw new Error(`파일 "${attachment.name}" 업로드에 실패했습니다: ${fileError.message}`);
} }
} }
console.log('모든 첨부파일 업로드 완료:', uploadedFiles);
return uploadedFiles;
} catch (error) { } catch (error) {
console.error('파일 업로드 오류:', error); console.error('파일 업로드 오류:', error);
// 부분적으로 업로드된 파일들 정리 (선택사항)
try {
for (const uploadedFile of uploadedFiles) {
if (uploadedFile.storage_path) {
await SupabaseHelper.deleteStorageFile(uploadedFile.storage_path);
}
}
} catch (cleanupError) {
console.error('업로드 실패 파일 정리 중 오류:', cleanupError);
}
throw error;
}
}
async addFileAttachment(fileId, attachmentData) {
if (!isSupabaseConfigured()) {
return; // 오프라인 모드에서는 처리하지 않음
}
try {
// SupabaseHelper를 통해 첨부파일 정보 저장
const result = await SupabaseHelper.addFileAttachment(fileId, attachmentData);
return result;
} catch (error) {
console.error('첨부파일 정보 저장 오류:', error);
throw error; throw error;
} }
} }

View File

@@ -214,6 +214,23 @@ const SupabaseHelper = {
.remove([filePath]); .remove([filePath]);
if (error) throw error; if (error) throw error;
},
// 첨부파일 정보 추가
async addFileAttachment(fileId, attachmentData) {
if (!supabase) throw new Error('Supabase가 초기화되지 않았습니다.');
const { data, error } = await supabase
.from('file_attachments')
.insert([{
file_id: fileId,
...attachmentData
}])
.select()
.single();
if (error) throw error;
return data;
} }
}; };