diff --git a/frontend/lib/utils/improvedButtonActionExecutor.ts b/frontend/lib/utils/improvedButtonActionExecutor.ts index 9b4388f4..0e84956b 100644 --- a/frontend/lib/utils/improvedButtonActionExecutor.ts +++ b/frontend/lib/utils/improvedButtonActionExecutor.ts @@ -250,13 +250,13 @@ export class ImprovedButtonActionExecutor { throw new Error(`관계 정보를 찾을 수 없습니다: ${config.relationshipId}`); } - console.log(`📋 관계 데이터 로드 완료:`, relationshipData); + console.log("📋 관계 데이터 로드 완료:", relationshipData); // 2. 관계 타입에 따른 실행 const relationships = relationshipData.relationships; const connectionType = relationships.connectionType; - console.log(`🔍 관계 상세 정보:`, { + console.log("🔍 관계 상세 정보:", { connectionType, hasExternalCallConfig: !!relationships.externalCallConfig, externalCallConfig: relationships.externalCallConfig, @@ -308,7 +308,7 @@ export class ImprovedButtonActionExecutor { const response = await apiClient.get(`/dataflow-diagrams/${relationshipId}`); - console.log(`✅ 관계 데이터 조회 성공:`, response.data); + console.log("✅ 관계 데이터 조회 성공:", response.data); if (!response.data.success) { throw new Error(response.data.message || "관계 데이터 조회 실패"); @@ -330,10 +330,10 @@ export class ImprovedButtonActionExecutor { context: ButtonExecutionContext, ): Promise { try { - console.log(`🔍 외부 호출 실행 시작 - relationships 구조:`, relationships); + console.log("🔍 외부 호출 실행 시작 - relationships 구조:", relationships); const externalCallConfig = relationships.externalCallConfig; - console.log(`🔍 externalCallConfig:`, externalCallConfig); + console.log("🔍 externalCallConfig:", externalCallConfig); if (!externalCallConfig) { console.error("❌ 외부 호출 설정이 없습니다. relationships 구조:", relationships); @@ -368,7 +368,7 @@ export class ImprovedButtonActionExecutor { } // 백엔드 프록시를 통한 외부 API 호출 (CORS 문제 해결) - console.log(`🌐 백엔드 프록시를 통한 외부 API 호출 준비:`, { + console.log("🌐 백엔드 프록시를 통한 외부 API 호출 준비:", { originalUrl: restApiSettings.apiUrl, method: restApiSettings.httpMethod || "GET", headers, @@ -393,24 +393,24 @@ export class ImprovedButtonActionExecutor { templateData: restApiSettings.httpMethod !== "GET" && requestBody ? JSON.parse(requestBody) : formData, }; - console.log(`📤 백엔드로 전송할 데이터:`, requestPayload); + console.log("📤 백엔드로 전송할 데이터:", requestPayload); - const proxyResponse = await apiClient.post(`/external-calls/execute`, requestPayload); + const proxyResponse = await apiClient.post("/external-calls/execute", requestPayload); - console.log(`📡 백엔드 프록시 응답:`, proxyResponse.data); + console.log("📡 백엔드 프록시 응답:", proxyResponse.data); if (!proxyResponse.data.success) { throw new Error(`프록시 API 호출 실패: ${proxyResponse.data.error || proxyResponse.data.message}`); } const responseData = proxyResponse.data.result; - console.log(`✅ 외부 API 호출 성공 (프록시):`, responseData); + console.log("✅ 외부 API 호출 성공 (프록시):", responseData); // 데이터 매핑 처리 (inbound mapping) if (externalCallConfig.dataMappingConfig?.inboundMapping) { console.log(`📥 데이터 매핑 설정 발견 - HTTP 메서드: ${restApiSettings.httpMethod}`); - console.log(`📥 매핑 설정:`, externalCallConfig.dataMappingConfig.inboundMapping); - console.log(`📥 응답 데이터:`, responseData); + console.log("📥 매핑 설정:", externalCallConfig.dataMappingConfig.inboundMapping); + console.log("📥 응답 데이터:", responseData); await this.processInboundMapping(externalCallConfig.dataMappingConfig.inboundMapping, responseData, context); } else { @@ -443,7 +443,7 @@ export class ImprovedButtonActionExecutor { context: ButtonExecutionContext, ): Promise { try { - console.log(`💾 데이터 저장 실행 시작`); + console.log("💾 데이터 저장 실행 시작"); // 제어 조건 확인 const controlConditions = relationships.controlConditions || []; @@ -520,11 +520,13 @@ export class ImprovedButtonActionExecutor { ): Promise { try { console.log(`🔧 데이터 액션 실행: ${action.name} (${action.actionType})`); - console.log(`📥 받은 formData:`, formData); + console.log("📥 받은 formData:", formData); + console.log("📥 formData 키들:", Object.keys(formData)); - // 🔥 UPDATE 액션의 경우 formData를 기본으로 시작 + // 🔥 UPDATE 액션의 경우 formData를 기본으로 시작 (기본키 포함) const mappedData: Record = action.actionType === "update" ? { ...formData } : {}; + // 필드 매핑 처리 (기존 데이터에 덮어쓰기) for (const mapping of action.fieldMappings) { if (mapping.valueType === "static") { // 정적 값 처리 @@ -533,19 +535,22 @@ export class ImprovedButtonActionExecutor { value = new Date().toISOString(); } mappedData[mapping.targetField] = value; + console.log(`🔧 정적 값 매핑: ${mapping.targetField} = ${value}`); } else { // 필드 매핑 처리 const sourceField = mapping.fromField?.columnName; if (sourceField && formData[sourceField] !== undefined) { mappedData[mapping.toField.columnName] = formData[sourceField]; + console.log(`🔧 필드 매핑: ${sourceField} → ${mapping.toField.columnName} = ${formData[sourceField]}`); } } } - console.log(`📋 매핑된 데이터:`, mappedData); - console.log(`🔑 기본키 포함 여부 체크:`, { + console.log("📋 최종 매핑된 데이터:", mappedData); + console.log("🔑 기본키 포함 여부 체크:", { hasId: "id" in mappedData, keys: Object.keys(mappedData), + values: Object.values(mappedData), }); // 대상 연결 정보 @@ -616,7 +621,7 @@ export class ImprovedButtonActionExecutor { formData: Record, context: ButtonExecutionContext, ): boolean { - console.log(`🔍 조건 평가 시작:`, { + console.log("🔍 조건 평가 시작:", { conditions, formDataKeys: Object.keys(formData), formData, @@ -628,7 +633,7 @@ export class ImprovedButtonActionExecutor { const conditionValue = condition.value; const operator = condition.operator; - console.log(`🔍 개별 조건 검증:`, { + console.log("🔍 개별 조건 검증:", { field: condition.field, operator, expectedValue: conditionValue, @@ -664,13 +669,13 @@ export class ImprovedButtonActionExecutor { if (!conditionMet) { console.log(`❌ 조건 불만족: ${condition.field} ${operator} ${conditionValue} (실제값: ${fieldValue})`); - console.log(`❌ 사용 가능한 필드들:`, Object.keys(formData)); - console.log(`❌ 전체 formData:`, formData); + console.log("❌ 사용 가능한 필드들:", Object.keys(formData)); + console.log("❌ 전체 formData:", formData); return false; } } - console.log(`✅ 모든 조건 만족`); + console.log("✅ 모든 조건 만족"); return true; } @@ -682,25 +687,25 @@ export class ImprovedButtonActionExecutor { // null이나 undefined인 경우 if (!responseData) { - console.log(`⚠️ 응답 데이터가 null 또는 undefined`); + console.log("⚠️ 응답 데이터가 null 또는 undefined"); return []; } // 이미 배열인 경우 (직접 배열 응답) if (Array.isArray(responseData)) { - console.log(`✅ 직접 배열 응답 감지`); + console.log("✅ 직접 배열 응답 감지"); return responseData; } // 문자열인 경우 JSON 파싱 시도 if (typeof responseData === "string") { - console.log(`🔄 JSON 문자열 파싱 시도`); + console.log("🔄 JSON 문자열 파싱 시도"); try { const parsed = JSON.parse(responseData); - console.log(`✅ JSON 파싱 성공, 재귀 호출`); + console.log("✅ JSON 파싱 성공, 재귀 호출"); return this.extractActualData(parsed); } catch (error) { - console.log(`⚠️ JSON 파싱 실패, 원본 문자열 반환:`, error); + console.log("⚠️ JSON 파싱 실패, 원본 문자열 반환:", error); return [responseData]; } } @@ -733,20 +738,20 @@ export class ImprovedButtonActionExecutor { // 추출된 데이터가 문자열인 경우 JSON 파싱 시도 if (typeof extractedData === "string") { - console.log(`🔄 추출된 데이터가 JSON 문자열, 파싱 시도`); + console.log("🔄 추출된 데이터가 JSON 문자열, 파싱 시도"); try { const parsed = JSON.parse(extractedData); - console.log(`✅ JSON 파싱 성공, 재귀 호출`); + console.log("✅ JSON 파싱 성공, 재귀 호출"); return this.extractActualData(parsed); } catch (error) { - console.log(`⚠️ JSON 파싱 실패, 원본 문자열 반환:`, error); + console.log("⚠️ JSON 파싱 실패, 원본 문자열 반환:", error); return [extractedData]; } } // 추출된 데이터가 객체이고 또 다른 중첩 구조일 수 있으므로 재귀 호출 if (typeof extractedData === "object" && !Array.isArray(extractedData)) { - console.log(`🔄 중첩된 객체 감지, 재귀 추출 시도`); + console.log("🔄 중첩된 객체 감지, 재귀 추출 시도"); return this.extractActualData(extractedData); } @@ -759,14 +764,14 @@ export class ImprovedButtonActionExecutor { const arrayValue = objectValues.find((value) => Array.isArray(value)); if (arrayValue) { - console.log(`✅ 객체 값 중 배열 발견`); + console.log("✅ 객체 값 중 배열 발견"); return arrayValue; } // 객체의 값들 중에서 객체를 찾아서 재귀 탐색 for (const value of objectValues) { if (value && typeof value === "object" && !Array.isArray(value)) { - console.log(`🔄 객체 값에서 재귀 탐색`); + console.log("🔄 객체 값에서 재귀 탐색"); const nestedResult = this.extractActualData(value); if (Array.isArray(nestedResult) && nestedResult.length > 0) { return nestedResult; @@ -775,7 +780,7 @@ export class ImprovedButtonActionExecutor { } // 모든 시도가 실패한 경우, 원본 객체를 단일 항목 배열로 반환 - console.log(`📦 원본 객체를 단일 항목으로 처리`); + console.log("📦 원본 객체를 단일 항목으로 처리"); return [responseData]; } @@ -788,38 +793,38 @@ export class ImprovedButtonActionExecutor { context: ButtonExecutionContext, ): Promise { try { - console.log(`📥 인바운드 데이터 매핑 처리 시작`); - console.log(`📥 원본 응답 데이터:`, responseData); + console.log("📥 인바운드 데이터 매핑 처리 시작"); + console.log("📥 원본 응답 데이터:", responseData); const targetTable = inboundMapping.targetTable; const fieldMappings = inboundMapping.fieldMappings || []; const insertMode = inboundMapping.insertMode || "insert"; - console.log(`📥 매핑 설정:`, { + console.log("📥 매핑 설정:", { targetTable, fieldMappings, insertMode, }); // 응답 데이터에서 실제 데이터 추출 (다양한 구조 지원) - let actualData = this.extractActualData(responseData); + const actualData = this.extractActualData(responseData); - console.log(`📥 추출된 실제 데이터:`, actualData); + console.log("📥 추출된 실제 데이터:", actualData); // 배열이 아닌 경우 배열로 변환 const dataArray = Array.isArray(actualData) ? actualData : [actualData]; - console.log(`📥 처리할 데이터 배열:`, dataArray); + console.log("📥 처리할 데이터 배열:", dataArray); if (dataArray.length === 0) { - console.log(`⚠️ 처리할 데이터가 없습니다`); + console.log("⚠️ 처리할 데이터가 없습니다"); return; } for (const item of dataArray) { const mappedData: Record = {}; - console.log(`📥 개별 아이템 처리:`, item); + console.log("📥 개별 아이템 처리:", item); // 필드 매핑 적용 for (const mapping of fieldMappings) { @@ -831,17 +836,17 @@ export class ImprovedButtonActionExecutor { } } - console.log(`📋 매핑된 데이터:`, mappedData); + console.log("📋 매핑된 데이터:", mappedData); // 매핑된 데이터가 비어있지 않은 경우에만 저장 if (Object.keys(mappedData).length > 0) { await this.saveDataToTable(targetTable, mappedData, insertMode); } else { - console.log(`⚠️ 매핑된 데이터가 비어있어 저장을 건너뜁니다`); + console.log("⚠️ 매핑된 데이터가 비어있어 저장을 건너뜁니다"); } } - console.log(`✅ 인바운드 데이터 매핑 완료`); + console.log("✅ 인바운드 데이터 매핑 완료"); } catch (error) { console.error("인바운드 데이터 매핑 오류:", error); throw error;