데이터 테이블 첨부파일 연계

This commit is contained in:
kjs
2025-09-06 00:16:27 +09:00
parent d73be8a4d3
commit 0b38f349aa
10 changed files with 1015 additions and 166 deletions

View File

@@ -8,7 +8,8 @@ import { useAuth } from "@/hooks/useAuth";
interface FileUploadProps {
component: FileComponent;
onUpdateComponent: (updates: Partial<FileComponent>) => void;
onUpdateComponent?: (updates: Partial<FileComponent>) => void;
onFileUpload?: (files: AttachedFileInfo[]) => void; // 파일 업로드 완료 콜백
userInfo?: any; // 사용자 정보 (선택적)
}
@@ -16,7 +17,7 @@ interface FileUploadProps {
* 독립적인 File 컴포넌트
* attach_file_info 테이블 기반 파일 관리
*/
export function FileUpload({ component, onUpdateComponent, userInfo }: FileUploadProps) {
export function FileUpload({ component, onUpdateComponent, onFileUpload, userInfo }: FileUploadProps) {
const [isDragOver, setIsDragOver] = useState(false);
const [uploadQueue, setUploadQueue] = useState<File[]>([]);
const [localUploadedFiles, setLocalUploadedFiles] = useState<AttachedFileInfo[]>(component.uploadedFiles || []);
@@ -110,16 +111,43 @@ export function FileUpload({ component, onUpdateComponent, userInfo }: FileUploa
// 파일 확장자 검증
const isFileTypeAllowed = (file: File): boolean => {
const fileName = file.name.toLowerCase();
return fileConfig.accept.some((accept) => {
if (accept.startsWith(".")) {
return fileName.endsWith(accept);
console.log("🔍 파일 타입 검증:", {
fileName: file.name,
fileType: file.type,
acceptRules: fileConfig.accept,
});
const result = fileConfig.accept.some((accept) => {
// 모든 파일 허용 (와일드카드)
if (accept === "*/*" || accept === "*") {
console.log("✅ 와일드카드 매칭:", accept);
return true;
}
// 확장자 기반 검증 (.jpg, .png 등)
if (accept.startsWith(".")) {
const matches = fileName.endsWith(accept.toLowerCase());
console.log(`${matches ? "✅" : "❌"} 확장자 검증:`, accept, "→", matches);
return matches;
}
// MIME 타입 기반 검증 (image/*, text/* 등)
if (accept.includes("/*")) {
const type = accept.split("/")[0];
return file.type.startsWith(type);
const matches = file.type.startsWith(type);
console.log(`${matches ? "✅" : "❌"} MIME 타입 검증:`, accept, "→", matches);
return matches;
}
return file.type === accept;
// 정확한 MIME 타입 매칭 (image/jpeg, application/pdf 등)
const matches = file.type === accept;
console.log(`${matches ? "✅" : "❌"} 정확한 MIME 매칭:`, accept, "→", matches);
return matches;
});
console.log(`🎯 최종 검증 결과:`, result);
return result;
};
// 파일 선택 핸들러
@@ -184,9 +212,18 @@ export function FileUpload({ component, onUpdateComponent, userInfo }: FileUploa
setUploadQueue((prev) => [...prev, ...validFiles]);
if (fileConfig.autoUpload) {
console.log("🚀 자동 업로드 시작");
console.log("🚀 자동 업로드 시작:", {
autoUpload: fileConfig.autoUpload,
filesCount: validFiles.length,
fileNames: validFiles.map((f) => f.name),
});
// 자동 업로드 실행
validFiles.forEach(uploadFile);
} else {
console.log("⏸️ 자동 업로드 비활성화:", {
autoUpload: fileConfig.autoUpload,
filesCount: validFiles.length,
});
}
} else {
console.log("❌ 업로드할 유효한 파일이 없음");
@@ -270,6 +307,45 @@ export function FileUpload({ component, onUpdateComponent, userInfo }: FileUploa
formData.append("writer", "system");
}
// 프론트엔드 파일 타입 설정을 백엔드로 전송
if (fileConfig.accept && fileConfig.accept.length > 0) {
const acceptString = fileConfig.accept.join(",");
formData.append("accept", acceptString);
console.log("✅ 허용 파일 타입 추가:", acceptString);
}
// 자동 연결 정보 추가
if (fileConfig.autoLink) {
formData.append("autoLink", "true");
console.log("✅ 자동 연결 활성화: true");
if (fileConfig.linkedTable) {
formData.append("linkedTable", fileConfig.linkedTable);
console.log("✅ 연결 테이블 추가:", fileConfig.linkedTable);
}
if (fileConfig.linkedField) {
formData.append("linkedField", fileConfig.linkedField);
console.log("✅ 연결 필드 추가:", fileConfig.linkedField);
}
if (fileConfig.recordId) {
formData.append("recordId", fileConfig.recordId);
console.log("✅ 레코드 ID 추가:", fileConfig.recordId);
}
// 가상 파일 컬럼 정보 추가
if (fileConfig.isVirtualFileColumn) {
formData.append("isVirtualFileColumn", "true");
console.log("✅ 가상 파일 컬럼 활성화: true");
if (fileConfig.columnName) {
formData.append("columnName", fileConfig.columnName);
console.log("✅ 컬럼명 추가:", fileConfig.columnName);
}
}
}
// FormData 내용 디버깅
console.log("📋 FormData 내용 확인:");
for (const [key, value] of formData.entries()) {
@@ -357,9 +433,17 @@ export function FileUpload({ component, onUpdateComponent, userInfo }: FileUploa
// 로컬 상태 업데이트
setLocalUploadedFiles(updatedFiles);
onUpdateComponent({
uploadedFiles: updatedFiles,
});
// 컴포넌트 업데이트 (옵셔널)
if (onUpdateComponent) {
onUpdateComponent({
uploadedFiles: updatedFiles,
});
}
// 파일 업로드 완료 콜백 호출 (모달에서 사용)
if (onFileUpload) {
onFileUpload(updatedFiles);
}
// 업로드 큐에서 제거
setUploadQueue((prev) => prev.filter((f) => f !== file));