fix: UPDATE 액션 formData 기본 포함 및 로깅 추가

UPDATE 액션 실행 시:
- formData를 기본으로 복사하여 기본키 포함
- 상세 로깅으로 디버깅 지원
- 백엔드 동적 기본키 조회 구현
This commit is contained in:
kjs 2025-10-01 15:51:13 +09:00
parent 151de4148c
commit d22e83d234
2 changed files with 174 additions and 164 deletions

View File

@ -32,10 +32,20 @@ export async function executeDataAction(
if (connection && connection.id !== 0) {
// 외부 데이터베이스 연결
result = await executeExternalDatabaseAction(tableName, data, actionType, connection);
result = await executeExternalDatabaseAction(
tableName,
data,
actionType,
connection
);
} else {
// 메인 데이터베이스 (현재 시스템)
result = await executeMainDatabaseAction(tableName, data, actionType, companyCode);
result = await executeMainDatabaseAction(
tableName,
data,
actionType,
companyCode
);
}
logger.info(`데이터 액션 실행 완료: ${actionType} on ${tableName}`, result);
@ -45,7 +55,6 @@ export async function executeDataAction(
message: `데이터 액션 실행 완료: ${actionType}`,
data: result,
});
} catch (error: any) {
logger.error("데이터 액션 실행 실패:", error);
res.status(500).json({
@ -73,13 +82,13 @@ async function executeMainDatabaseAction(
};
switch (actionType.toLowerCase()) {
case 'insert':
case "insert":
return await executeInsert(tableName, dataWithCompany);
case 'update':
case "update":
return await executeUpdate(tableName, dataWithCompany);
case 'upsert':
case "upsert":
return await executeUpsert(tableName, dataWithCompany);
case 'delete':
case "delete":
return await executeDelete(tableName, dataWithCompany);
default:
throw new Error(`지원하지 않는 액션 타입: ${actionType}`);
@ -100,25 +109,37 @@ async function executeExternalDatabaseAction(
connection: any
): Promise<any> {
try {
logger.info(`외부 DB 액션 실행: ${connection.name} (${connection.host}:${connection.port})`);
logger.info(
`외부 DB 액션 실행: ${connection.name} (${connection.host}:${connection.port})`
);
logger.info(`테이블: ${tableName}, 액션: ${actionType}`, data);
// 🔥 실제 외부 DB 연결 및 실행 로직 구현
const { MultiConnectionQueryService } = await import('../services/multiConnectionQueryService');
const { MultiConnectionQueryService } = await import(
"../services/multiConnectionQueryService"
);
const queryService = new MultiConnectionQueryService();
let result;
switch (actionType.toLowerCase()) {
case 'insert':
result = await queryService.insertDataToConnection(connection.id, tableName, data);
case "insert":
result = await queryService.insertDataToConnection(
connection.id,
tableName,
data
);
logger.info(`외부 DB INSERT 성공:`, result);
break;
case 'update':
case "update":
// TODO: UPDATE 로직 구현 (조건 필요)
throw new Error('UPDATE 액션은 아직 지원되지 않습니다. 조건 설정이 필요합니다.');
case 'delete':
throw new Error(
"UPDATE 액션은 아직 지원되지 않습니다. 조건 설정이 필요합니다."
);
case "delete":
// TODO: DELETE 로직 구현 (조건 필요)
throw new Error('DELETE 액션은 아직 지원되지 않습니다. 조건 설정이 필요합니다.');
throw new Error(
"DELETE 액션은 아직 지원되지 않습니다. 조건 설정이 필요합니다."
);
default:
throw new Error(`지원하지 않는 액션 타입: ${actionType}`);
}
@ -139,12 +160,15 @@ async function executeExternalDatabaseAction(
/**
* INSERT
*/
async function executeInsert(tableName: string, data: Record<string, any>): Promise<any> {
async function executeInsert(
tableName: string,
data: Record<string, any>
): Promise<any> {
try {
// 동적 테이블 접근을 위한 raw query 사용
const columns = Object.keys(data).join(', ');
const columns = Object.keys(data).join(", ");
const values = Object.values(data);
const placeholders = values.map((_, index) => `$${index + 1}`).join(', ');
const placeholders = values.map((_, index) => `$${index + 1}`).join(", ");
const insertQuery = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders}) RETURNING *`;
@ -154,7 +178,7 @@ async function executeInsert(tableName: string, data: Record<string, any>): Prom
return {
success: true,
action: 'insert',
action: "insert",
tableName,
data: result,
affectedRows: result.length,
@ -168,7 +192,10 @@ async function executeInsert(tableName: string, data: Record<string, any>): Prom
/**
* UPDATE
*/
async function executeUpdate(tableName: string, data: Record<string, any>): Promise<any> {
async function executeUpdate(
tableName: string,
data: Record<string, any>
): Promise<any> {
try {
logger.info(`UPDATE 액션 시작:`, { tableName, receivedData: data });
@ -180,7 +207,9 @@ async function executeUpdate(tableName: string, data: Record<string, any>): Prom
WHERE i.indrelid = $1::regclass AND i.indisprimary
`;
const pkResult = await query<{ column_name: string }>(primaryKeyQuery, [tableName]);
const pkResult = await query<{ column_name: string }>(primaryKeyQuery, [
tableName,
]);
if (!pkResult || pkResult.length === 0) {
throw new Error(`테이블 ${tableName}의 기본키를 찾을 수 없습니다`);
@ -198,7 +227,9 @@ async function executeUpdate(tableName: string, data: Record<string, any>): Prom
receivedData: data,
availableKeys: Object.keys(data),
});
throw new Error(`UPDATE를 위한 기본키 값이 필요합니다 (${primaryKeyColumn})`);
throw new Error(
`UPDATE를 위한 기본키 값이 필요합니다 (${primaryKeyColumn})`
);
}
// 3. 업데이트할 데이터에서 기본키 제외
@ -214,12 +245,15 @@ async function executeUpdate(tableName: string, data: Record<string, any>): Prom
// 4. 동적 UPDATE 쿼리 생성
const setClause = Object.keys(updateData)
.map((key, index) => `${key} = $${index + 1}`)
.join(', ');
.join(", ");
const values = Object.values(updateData);
const updateQuery = `UPDATE ${tableName} SET ${setClause} WHERE ${primaryKeyColumn} = $${values.length + 1} RETURNING *`;
logger.info(`UPDATE 쿼리 실행:`, { query: updateQuery, values: [...values, primaryKeyValue] });
logger.info(`UPDATE 쿼리 실행:`, {
query: updateQuery,
values: [...values, primaryKeyValue],
});
const result = await query<any>(updateQuery, [...values, primaryKeyValue]);
@ -227,7 +261,7 @@ async function executeUpdate(tableName: string, data: Record<string, any>): Prom
return {
success: true,
action: 'update',
action: "update",
tableName,
data: result,
affectedRows: result.length,
@ -241,7 +275,10 @@ async function executeUpdate(tableName: string, data: Record<string, any>): Prom
/**
* UPSERT
*/
async function executeUpsert(tableName: string, data: Record<string, any>): Promise<any> {
async function executeUpsert(
tableName: string,
data: Record<string, any>
): Promise<any> {
try {
// 먼저 INSERT를 시도하고, 실패하면 UPDATE
try {
@ -260,12 +297,15 @@ async function executeUpsert(tableName: string, data: Record<string, any>): Prom
/**
* DELETE
*/
async function executeDelete(tableName: string, data: Record<string, any>): Promise<any> {
async function executeDelete(
tableName: string,
data: Record<string, any>
): Promise<any> {
try {
const { id } = data;
if (!id) {
throw new Error('DELETE를 위한 ID가 필요합니다');
throw new Error("DELETE를 위한 ID가 필요합니다");
}
const deleteQuery = `DELETE FROM ${tableName} WHERE id = $1 RETURNING *`;
@ -276,7 +316,7 @@ async function executeDelete(tableName: string, data: Record<string, any>): Prom
return {
success: true,
action: 'delete',
action: "delete",
tableName,
data: result,
affectedRows: result.length,

View File

@ -65,7 +65,7 @@ export class ImprovedButtonActionExecutor {
static async executeButtonAction(
buttonConfig: ExtendedButtonTypeConfig,
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ButtonExecutionResult> {
console.log("🔥 ImprovedButtonActionExecutor 시작:", {
buttonConfig,
@ -92,15 +92,11 @@ export class ImprovedButtonActionExecutor {
// 1. Before 타이밍 제어 실행
if (executionPlan.beforeControls.length > 0) {
console.log("⏰ Before 제어 실행 시작");
const beforeResults = await this.executeControls(
executionPlan.beforeControls,
formData,
context
);
const beforeResults = await this.executeControls(executionPlan.beforeControls, formData, context);
results.push(...beforeResults);
// Before 제어 중 실패가 있으면 중단
const hasFailure = beforeResults.some(r => !r.success);
const hasFailure = beforeResults.some((r) => !r.success);
if (hasFailure) {
throw new Error("Before 제어 실행 중 오류가 발생했습니다.");
}
@ -109,11 +105,7 @@ export class ImprovedButtonActionExecutor {
// 2. 메인 액션 실행 (replace가 아닌 경우에만)
if (!executionPlan.hasReplaceControl) {
console.log("⚡ 메인 액션 실행:", buttonConfig.actionType);
const mainResult = await this.executeMainAction(
buttonConfig,
formData,
context
);
const mainResult = await this.executeMainAction(buttonConfig, formData, context);
results.push(mainResult);
if (!mainResult.success) {
@ -126,11 +118,7 @@ export class ImprovedButtonActionExecutor {
// 3. After 타이밍 제어 실행
if (executionPlan.afterControls.length > 0) {
console.log("⏰ After 제어 실행 시작");
const afterResults = await this.executeControls(
executionPlan.afterControls,
formData,
context
);
const afterResults = await this.executeControls(executionPlan.afterControls, formData, context);
results.push(...afterResults);
}
@ -210,18 +198,14 @@ export class ImprovedButtonActionExecutor {
private static async executeControls(
controls: ControlConfig[],
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ExecutionResult[]> {
const results: ExecutionResult[] = [];
for (const control of controls) {
try {
// 관계 실행만 지원
const result = await this.executeRelationship(
control.relationshipConfig,
formData,
context
);
const result = await this.executeRelationship(control.relationshipConfig, formData, context);
results.push(result);
@ -255,7 +239,7 @@ export class ImprovedButtonActionExecutor {
contextData?: Record<string, any>;
},
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ExecutionResult> {
try {
console.log(`🔗 관계 실행 시작: ${config.relationshipName} (ID: ${config.relationshipId})`);
@ -301,7 +285,6 @@ export class ImprovedButtonActionExecutor {
}
return result;
} catch (error: any) {
console.error(`❌ 관계 실행 실패: ${config.relationshipName}`, error);
const errorResult = {
@ -328,12 +311,12 @@ export class ImprovedButtonActionExecutor {
console.log(`✅ 관계 데이터 조회 성공:`, response.data);
if (!response.data.success) {
throw new Error(response.data.message || '관계 데이터 조회 실패');
throw new Error(response.data.message || "관계 데이터 조회 실패");
}
return response.data.data;
} catch (error) {
console.error('관계 데이터 조회 오류:', error);
console.error("관계 데이터 조회 오류:", error);
throw error;
}
}
@ -344,7 +327,7 @@ export class ImprovedButtonActionExecutor {
private static async executeExternalCall(
relationships: any,
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ExecutionResult> {
try {
console.log(`🔍 외부 호출 실행 시작 - relationships 구조:`, relationships);
@ -353,30 +336,30 @@ export class ImprovedButtonActionExecutor {
console.log(`🔍 externalCallConfig:`, externalCallConfig);
if (!externalCallConfig) {
console.error('❌ 외부 호출 설정이 없습니다. relationships 구조:', relationships);
throw new Error('외부 호출 설정이 없습니다');
console.error("❌ 외부 호출 설정이 없습니다. relationships 구조:", relationships);
throw new Error("외부 호출 설정이 없습니다");
}
const restApiSettings = externalCallConfig.restApiSettings;
if (!restApiSettings) {
throw new Error('REST API 설정이 없습니다');
throw new Error("REST API 설정이 없습니다");
}
console.log(`🌐 외부 API 호출: ${restApiSettings.apiUrl}`);
// API 호출 준비
const headers: Record<string, string> = {
'Content-Type': 'application/json',
"Content-Type": "application/json",
...restApiSettings.headers,
};
// 인증 처리
if (restApiSettings.authentication?.type === 'api-key') {
headers['Authorization'] = `Bearer ${restApiSettings.authentication.apiKey}`;
if (restApiSettings.authentication?.type === "api-key") {
headers["Authorization"] = `Bearer ${restApiSettings.authentication.apiKey}`;
}
// 요청 바디 준비 (템플릿 처리)
let requestBody = restApiSettings.bodyTemplate || '';
let requestBody = restApiSettings.bodyTemplate || "";
if (requestBody) {
// 간단한 템플릿 치환 ({{변수명}} 형태)
requestBody = requestBody.replace(/\{\{(\w+)\}\}/g, (match: string, key: string) => {
@ -387,9 +370,9 @@ export class ImprovedButtonActionExecutor {
// 백엔드 프록시를 통한 외부 API 호출 (CORS 문제 해결)
console.log(`🌐 백엔드 프록시를 통한 외부 API 호출 준비:`, {
originalUrl: restApiSettings.apiUrl,
method: restApiSettings.httpMethod || 'GET',
method: restApiSettings.httpMethod || "GET",
headers,
body: restApiSettings.httpMethod !== 'GET' ? requestBody : undefined,
body: restApiSettings.httpMethod !== "GET" ? requestBody : undefined,
});
// 백엔드 프록시 API 호출 - GenericApiSettings 형식에 맞게 전달
@ -400,14 +383,14 @@ export class ImprovedButtonActionExecutor {
callType: "rest-api",
apiType: "generic",
url: restApiSettings.apiUrl,
method: restApiSettings.httpMethod || 'POST',
method: restApiSettings.httpMethod || "POST",
headers: restApiSettings.headers || {},
body: requestBody,
authentication: restApiSettings.authentication || { type: 'none' },
authentication: restApiSettings.authentication || { type: "none" },
timeout: restApiSettings.timeout || 30000,
retryCount: restApiSettings.retryCount || 3,
},
templateData: restApiSettings.httpMethod !== 'GET' && requestBody ? JSON.parse(requestBody) : formData,
templateData: restApiSettings.httpMethod !== "GET" && requestBody ? JSON.parse(requestBody) : formData,
};
console.log(`📤 백엔드로 전송할 데이터:`, requestPayload);
@ -429,24 +412,19 @@ export class ImprovedButtonActionExecutor {
console.log(`📥 매핑 설정:`, externalCallConfig.dataMappingConfig.inboundMapping);
console.log(`📥 응답 데이터:`, responseData);
await this.processInboundMapping(
externalCallConfig.dataMappingConfig.inboundMapping,
responseData,
context
);
await this.processInboundMapping(externalCallConfig.dataMappingConfig.inboundMapping, responseData, context);
} else {
console.log(` 데이터 매핑 설정이 없습니다 - HTTP 메서드: ${restApiSettings.httpMethod}`);
}
return {
success: true,
message: '외부 호출 실행 완료',
message: "외부 호출 실행 완료",
executionTime: Date.now() - context.startTime,
data: responseData,
};
} catch (error: any) {
console.error('외부 호출 실행 오류:', error);
console.error("외부 호출 실행 오류:", error);
return {
success: false,
message: `외부 호출 실행 실패: ${error.message}`,
@ -462,7 +440,7 @@ export class ImprovedButtonActionExecutor {
private static async executeDataSave(
relationships: any,
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ExecutionResult> {
try {
console.log(`💾 데이터 저장 실행 시작`);
@ -474,7 +452,7 @@ export class ImprovedButtonActionExecutor {
if (!conditionsMet) {
return {
success: false,
message: '제어 조건을 만족하지 않아 데이터 저장을 건너뜁니다',
message: "제어 조건을 만족하지 않아 데이터 저장을 건너뜁니다",
executionTime: Date.now() - context.startTime,
};
}
@ -498,12 +476,7 @@ export class ImprovedButtonActionExecutor {
continue;
}
const actionResult = await this.executeDataAction(
action,
relationships,
formData,
context
);
const actionResult = await this.executeDataAction(action, relationships, formData, context);
results.push(actionResult);
if (!actionResult.success) {
@ -512,7 +485,7 @@ export class ImprovedButtonActionExecutor {
}
}
const successCount = results.filter(r => r.success).length;
const successCount = results.filter((r) => r.success).length;
const totalCount = results.length;
return {
@ -525,9 +498,8 @@ export class ImprovedButtonActionExecutor {
totalCount,
},
};
} catch (error: any) {
console.error('데이터 저장 실행 오류:', error);
console.error("데이터 저장 실행 오류:", error);
return {
success: false,
message: `데이터 저장 실행 실패: ${error.message}`,
@ -544,19 +516,20 @@ export class ImprovedButtonActionExecutor {
action: any,
relationships: any,
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ExecutionResult> {
try {
console.log(`🔧 데이터 액션 실행: ${action.name} (${action.actionType})`);
console.log(`📥 받은 formData:`, formData);
// 필드 매핑 처리
const mappedData: Record<string, any> = {};
// 🔥 UPDATE 액션의 경우 formData를 기본으로 시작
const mappedData: Record<string, any> = action.actionType === "update" ? { ...formData } : {};
for (const mapping of action.fieldMappings) {
if (mapping.valueType === 'static') {
if (mapping.valueType === "static") {
// 정적 값 처리
let value = mapping.value;
if (value === '#NOW') {
if (value === "#NOW") {
value = new Date().toISOString();
}
mappedData[mapping.targetField] = value;
@ -570,22 +543,21 @@ export class ImprovedButtonActionExecutor {
}
console.log(`📋 매핑된 데이터:`, mappedData);
console.log(`🔑 기본키 포함 여부 체크:`, {
hasId: "id" in mappedData,
keys: Object.keys(mappedData),
});
// 대상 연결 정보
const toConnection = relationships.toConnection;
const targetTable = relationships.toTable?.tableName;
if (!targetTable) {
throw new Error('대상 테이블이 지정되지 않았습니다');
throw new Error("대상 테이블이 지정되지 않았습니다");
}
// 데이터 저장 API 호출
const saveResult = await this.saveDataToTable(
targetTable,
mappedData,
action.actionType,
toConnection
);
const saveResult = await this.saveDataToTable(targetTable, mappedData, action.actionType, toConnection);
return {
success: true,
@ -593,7 +565,6 @@ export class ImprovedButtonActionExecutor {
executionTime: Date.now() - context.startTime,
data: saveResult,
};
} catch (error: any) {
console.error(`데이터 액션 실행 오류: ${action.name}`, error);
return {
@ -612,17 +583,17 @@ export class ImprovedButtonActionExecutor {
tableName: string,
data: Record<string, any>,
actionType: string,
connection?: any
connection?: any,
): Promise<any> {
try {
console.log(`💾 테이블 데이터 저장 시작: ${tableName}`, {
actionType,
data,
connection
connection,
});
// 데이터 저장 API 호출 (apiClient 사용)
const response = await apiClient.post('/dataflow/execute-data-action', {
const response = await apiClient.post("/dataflow/execute-data-action", {
tableName,
data,
actionType,
@ -632,7 +603,7 @@ export class ImprovedButtonActionExecutor {
console.log(`✅ 테이블 데이터 저장 성공: ${tableName}`, response.data);
return response.data;
} catch (error) {
console.error('테이블 데이터 저장 오류:', error);
console.error("테이블 데이터 저장 오류:", error);
throw error;
}
}
@ -643,7 +614,7 @@ export class ImprovedButtonActionExecutor {
private static evaluateConditions(
conditions: any[],
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): boolean {
console.log(`🔍 조건 평가 시작:`, {
conditions,
@ -668,22 +639,22 @@ export class ImprovedButtonActionExecutor {
let conditionMet = false;
switch (operator) {
case '=':
case "=":
conditionMet = fieldValue === conditionValue;
break;
case '!=':
case "!=":
conditionMet = fieldValue !== conditionValue;
break;
case '>':
case ">":
conditionMet = Number(fieldValue) > Number(conditionValue);
break;
case '<':
case "<":
conditionMet = Number(fieldValue) < Number(conditionValue);
break;
case '>=':
case ">=":
conditionMet = Number(fieldValue) >= Number(conditionValue);
break;
case '<=':
case "<=":
conditionMet = Number(fieldValue) <= Number(conditionValue);
break;
default:
@ -722,7 +693,7 @@ export class ImprovedButtonActionExecutor {
}
// 문자열인 경우 JSON 파싱 시도
if (typeof responseData === 'string') {
if (typeof responseData === "string") {
console.log(`🔄 JSON 문자열 파싱 시도`);
try {
const parsed = JSON.parse(responseData);
@ -735,23 +706,23 @@ export class ImprovedButtonActionExecutor {
}
// 객체가 아닌 경우 (숫자 등)
if (typeof responseData !== 'object') {
if (typeof responseData !== "object") {
console.log(`⚠️ 객체가 아닌 응답: ${typeof responseData}`);
return [responseData];
}
// 일반적인 데이터 필드명들을 우선순위대로 확인
const commonDataFields = [
'data', // { data: [...] }
'result', // { result: [...] }
'results', // { results: [...] }
'items', // { items: [...] }
'list', // { list: [...] }
'records', // { records: [...] }
'rows', // { rows: [...] }
'content', // { content: [...] }
'payload', // { payload: [...] }
'response', // { response: [...] }
"data", // { data: [...] }
"result", // { result: [...] }
"results", // { results: [...] }
"items", // { items: [...] }
"list", // { list: [...] }
"records", // { records: [...] }
"rows", // { rows: [...] }
"content", // { content: [...] }
"payload", // { payload: [...] }
"response", // { response: [...] }
];
for (const field of commonDataFields) {
@ -761,7 +732,7 @@ export class ImprovedButtonActionExecutor {
const extractedData = responseData[field];
// 추출된 데이터가 문자열인 경우 JSON 파싱 시도
if (typeof extractedData === 'string') {
if (typeof extractedData === "string") {
console.log(`🔄 추출된 데이터가 JSON 문자열, 파싱 시도`);
try {
const parsed = JSON.parse(extractedData);
@ -774,7 +745,7 @@ export class ImprovedButtonActionExecutor {
}
// 추출된 데이터가 객체이고 또 다른 중첩 구조일 수 있으므로 재귀 호출
if (typeof extractedData === 'object' && !Array.isArray(extractedData)) {
if (typeof extractedData === "object" && !Array.isArray(extractedData)) {
console.log(`🔄 중첩된 객체 감지, 재귀 추출 시도`);
return this.extractActualData(extractedData);
}
@ -785,7 +756,7 @@ export class ImprovedButtonActionExecutor {
// 특별한 필드가 없는 경우, 객체의 값들 중에서 배열을 찾기
const objectValues = Object.values(responseData);
const arrayValue = objectValues.find(value => Array.isArray(value));
const arrayValue = objectValues.find((value) => Array.isArray(value));
if (arrayValue) {
console.log(`✅ 객체 값 중 배열 발견`);
@ -794,7 +765,7 @@ export class ImprovedButtonActionExecutor {
// 객체의 값들 중에서 객체를 찾아서 재귀 탐색
for (const value of objectValues) {
if (value && typeof value === 'object' && !Array.isArray(value)) {
if (value && typeof value === "object" && !Array.isArray(value)) {
console.log(`🔄 객체 값에서 재귀 탐색`);
const nestedResult = this.extractActualData(value);
if (Array.isArray(nestedResult) && nestedResult.length > 0) {
@ -814,7 +785,7 @@ export class ImprovedButtonActionExecutor {
private static async processInboundMapping(
inboundMapping: any,
responseData: any,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<void> {
try {
console.log(`📥 인바운드 데이터 매핑 처리 시작`);
@ -822,12 +793,12 @@ export class ImprovedButtonActionExecutor {
const targetTable = inboundMapping.targetTable;
const fieldMappings = inboundMapping.fieldMappings || [];
const insertMode = inboundMapping.insertMode || 'insert';
const insertMode = inboundMapping.insertMode || "insert";
console.log(`📥 매핑 설정:`, {
targetTable,
fieldMappings,
insertMode
insertMode,
});
// 응답 데이터에서 실제 데이터 추출 (다양한 구조 지원)
@ -872,19 +843,18 @@ export class ImprovedButtonActionExecutor {
console.log(`✅ 인바운드 데이터 매핑 완료`);
} catch (error) {
console.error('인바운드 데이터 매핑 오류:', error);
console.error("인바운드 데이터 매핑 오류:", error);
throw error;
}
}
/**
* 🔥
*/
private static async executeMainAction(
buttonConfig: ExtendedButtonTypeConfig,
formData: Record<string, any>,
context: ButtonExecutionContext
context: ButtonExecutionContext,
): Promise<ExecutionResult> {
try {
// 기존 ButtonActionExecutor 로직을 여기서 호출하거나
@ -918,7 +888,7 @@ export class ImprovedButtonActionExecutor {
private static async handleExecutionError(
error: Error,
results: ExecutionResult[],
buttonConfig: ExtendedButtonTypeConfig
buttonConfig: ExtendedButtonTypeConfig,
): Promise<void> {
console.error("🔄 실행 오류 처리 시작:", error.message);
@ -928,7 +898,7 @@ export class ImprovedButtonActionExecutor {
console.log("🔄 롤백 처리 시작...");
// 성공한 결과들을 역순으로 롤백
const successfulResults = results.filter(r => r.success).reverse();
const successfulResults = results.filter((r) => r.success).reverse();
for (const result of successfulResults) {
try {