# 제어관리 시스템 트랜잭션 및 조건부 실행 개선방안 ## 🚨 현재 문제점 분석 ### 1. 트랜잭션 처리 부재 **문제**: 여러 액션 중 하나가 실패해도 이전 액션들이 그대로 유지됨 #### 현재 상황: ``` 저장액션1 (성공) → 저장액션2 (실패) 결과: 저장액션1의 데이터는 DB에 그대로 남아있음 (데이터 불일치) ``` #### 예시 시나리오: 1. **고객정보 저장** (성공) 2. **주문정보 저장** (실패) 3. **결제정보 저장** (실행되지 않음) → 고객정보만 저장되어 데이터 정합성 깨짐 ### 2. 조건부 실행 로직 부재 **문제**: AND/OR 조건에 따른 유연한 액션 실행이 불가능 #### 현재 한계: - 모든 액션이 순차적으로 실행됨 - 하나 실패하면 전체 중단 - 대안 액션 실행 불가 #### 원하는 동작: ``` 액션그룹1: (저장액션1 AND 저장액션2) OR 저장액션3 → 저장액션1,2가 모두 성공하면 완료 → 둘 중 하나라도 실패하면 저장액션3 실행 ``` ## 🎯 해결방안 설계 ## Phase 1: 트랜잭션 관리 시스템 구축 ### 1.1 트랜잭션 단위 정의 ```typescript // frontend/types/control-management.ts export interface TransactionGroup { id: string; name: string; description?: string; actions: DataflowAction[]; rollbackStrategy: RollbackStrategy; executionMode: "sequential" | "parallel"; onFailure: FailureHandling; } export type RollbackStrategy = | "none" // 롤백 안함 (현재 방식) | "partial" // 실패한 액션만 롤백 | "complete"; // 전체 트랜잭션 롤백 export type FailureHandling = | "stop" // 실패 시 중단 (현재 방식) | "continue" // 실패해도 계속 진행 | "alternative"; // 대안 액션 실행 ``` ### 1.2 조건부 실행 로직 구조 ```typescript export interface ConditionalExecutionPlan { id: string; name: string; conditions: ExecutionCondition[]; logic: "AND" | "OR" | "CUSTOM"; customLogic?: string; // "(A AND B) OR (C AND D)" } export interface ExecutionCondition { id: string; type: "action_group" | "validation" | "data_check"; // 액션 그룹 조건 actionGroup?: TransactionGroup; // 검증 조건 validation?: { field: string; operator: ConditionOperator; value: unknown; }; // 성공/실패 조건 expectedResult: "success" | "failure" | "any"; } ``` ### 1.3 액션 실행 결과 추적 ```typescript export interface ActionExecutionResult { actionId: string; transactionId: string; status: "pending" | "running" | "success" | "failed" | "rolled_back"; startTime: Date; endTime?: Date; result?: unknown; error?: { code: string; message: string; details?: unknown; }; rollbackData?: unknown; // 롤백을 위한 데이터 } export interface TransactionExecutionState { transactionId: string; status: | "pending" | "running" | "success" | "failed" | "rolling_back" | "rolled_back"; actions: ActionExecutionResult[]; rollbackActions?: ActionExecutionResult[]; startTime: Date; endTime?: Date; } ``` ## Phase 2: 고급 조건부 실행 시스템 ### 2.1 조건부 액션 그룹 정의 ```typescript export interface ConditionalActionGroup { id: string; name: string; description?: string; // 실행 조건 executionCondition: { type: "always" | "conditional" | "fallback"; conditions?: DataflowCondition[]; logic?: "AND" | "OR"; }; // 액션들 actions: DataflowAction[]; // 성공/실패 조건 정의 successCriteria: { type: "all_success" | "any_success" | "custom"; customLogic?: string; // "action1 AND (action2 OR action3)" }; // 다음 단계 정의 onSuccess?: { nextGroup?: string; completeTransaction?: boolean; }; onFailure?: { retryCount?: number; fallbackGroup?: string; rollbackStrategy?: RollbackStrategy; }; } ``` ### 2.2 복잡한 실행 계획 예시 ```typescript // 예시: 주문 처리 시스템 const orderProcessingPlan: ConditionalExecutionPlan = { id: "order_processing", name: "주문 처리", conditions: [ { id: "primary_payment", type: "action_group", actionGroup: { id: "payment_group_1", name: "주결제 수단", actions: [ { type: "database", operation: "UPDATE", tableName: "customer" /* ... */, }, { type: "database", operation: "INSERT", tableName: "orders" /* ... */, }, { type: "api", endpoint: "/payment/card" /* ... */ }, ], rollbackStrategy: "complete", executionMode: "sequential", }, expectedResult: "success", }, { id: "alternative_payment", type: "action_group", actionGroup: { id: "payment_group_2", name: "대안 결제 수단", actions: [ { type: "api", endpoint: "/payment/bank" /* ... */ }, { type: "database", operation: "UPDATE", tableName: "orders" /* ... */, }, ], rollbackStrategy: "complete", executionMode: "sequential", }, expectedResult: "success", }, ], logic: "OR", // primary_payment OR alternative_payment customLogic: "primary_payment OR alternative_payment", }; ``` ## Phase 3: 트랜잭션 실행 엔진 구현 ### 3.1 트랜잭션 매니저 클래스 ```typescript // frontend/lib/services/transactionManager.ts export class TransactionManager { private activeTransactions: Map = new Map(); private rollbackHandlers: Map = new Map(); /** * 트랜잭션 실행 */ async executeTransaction( plan: ConditionalExecutionPlan, context: ExtendedControlContext ): Promise { const transactionId = this.generateTransactionId(); const state: TransactionExecutionState = { transactionId, status: "pending", actions: [], startTime: new Date(), }; this.activeTransactions.set(transactionId, state); try { state.status = "running"; // 조건부 실행 로직 평가 const executionResult = await this.evaluateExecutionPlan( plan, context, transactionId ); if (executionResult.success) { state.status = "success"; } else { state.status = "failed"; // 실패 시 롤백 처리 if (executionResult.requiresRollback) { await this.rollbackTransaction(transactionId); } } state.endTime = new Date(); return executionResult; } catch (error) { state.status = "failed"; state.endTime = new Date(); await this.rollbackTransaction(transactionId); throw error; } finally { // 트랜잭션 정리 (일정 시간 후) setTimeout(() => this.cleanupTransaction(transactionId), 300000); // 5분 후 } } /** * 실행 계획 평가 */ private async evaluateExecutionPlan( plan: ConditionalExecutionPlan, context: ExtendedControlContext, transactionId: string ): Promise { const results: Map = new Map(); // 각 조건별로 실행 for (const condition of plan.conditions) { const result = await this.executeCondition( condition, context, transactionId ); results.set(condition.id, result.success); // 실패 시 즉시 중단할지 결정 if (!result.success && this.shouldStopOnFailure(plan, condition)) { return { success: false, message: `조건 ${condition.id} 실행 실패`, requiresRollback: true, results: Array.from(results.entries()), }; } } // 전체 로직 평가 const overallSuccess = this.evaluateLogic( plan.logic, plan.customLogic, results ); return { success: overallSuccess, message: overallSuccess ? "모든 조건 실행 성공" : "조건 실행 실패", requiresRollback: !overallSuccess, results: Array.from(results.entries()), }; } /** * 개별 조건 실행 */ private async executeCondition( condition: ExecutionCondition, context: ExtendedControlContext, transactionId: string ): Promise<{ success: boolean; result?: unknown }> { if (condition.type === "action_group" && condition.actionGroup) { return await this.executeActionGroup( condition.actionGroup, context, transactionId ); } // 다른 조건 타입들 처리... return { success: true }; } /** * 액션 그룹 실행 */ private async executeActionGroup( group: TransactionGroup, context: ExtendedControlContext, transactionId: string ): Promise<{ success: boolean; result?: unknown }> { const state = this.activeTransactions.get(transactionId)!; const groupResults: ActionExecutionResult[] = []; try { if (group.executionMode === "sequential") { // 순차 실행 for (const action of group.actions) { const result = await this.executeAction( action, context, transactionId ); groupResults.push(result); state.actions.push(result); if (result.status === "failed" && group.onFailure === "stop") { throw new Error( `액션 ${action.id} 실행 실패: ${result.error?.message}` ); } } } else { // 병렬 실행 const promises = group.actions.map((action) => this.executeAction(action, context, transactionId) ); const results = await Promise.allSettled(promises); results.forEach((result, index) => { const actionResult: ActionExecutionResult = { actionId: group.actions[index].id, transactionId, status: result.status === "fulfilled" && result.value.status === "success" ? "success" : "failed", startTime: new Date(), endTime: new Date(), result: result.status === "fulfilled" ? result.value.result : undefined, error: result.status === "rejected" ? { code: "EXECUTION_ERROR", message: result.reason } : undefined, }; groupResults.push(actionResult); state.actions.push(actionResult); }); } // 성공 기준 평가 const success = this.evaluateSuccessCriteria( group.successCriteria, groupResults ); if (!success && group.rollbackStrategy === "complete") { // 그룹 내 모든 액션 롤백 await this.rollbackActionGroup(group, groupResults, transactionId); } return { success, result: groupResults }; } catch (error) { // 오류 발생 시 롤백 if (group.rollbackStrategy !== "none") { await this.rollbackActionGroup(group, groupResults, transactionId); } return { success: false, result: error }; } } /** * 개별 액션 실행 */ private async executeAction( action: DataflowAction, context: ExtendedControlContext, transactionId: string ): Promise { const result: ActionExecutionResult = { actionId: action.id, transactionId, status: "running", startTime: new Date(), }; try { // 액션 타입별 실행 let executionResult: unknown; switch (action.type) { case "database": executionResult = await this.executeDatabaseAction(action, context); // 롤백 데이터 저장 (UPDATE/DELETE의 경우) if (action.operation === "UPDATE" || action.operation === "DELETE") { result.rollbackData = await this.captureRollbackData( action, context ); } break; case "api": executionResult = await this.executeApiAction(action, context); break; case "notification": executionResult = await this.executeNotificationAction( action, context ); break; default: throw new Error(`Unsupported action type: ${action.type}`); } result.status = "success"; result.result = executionResult; result.endTime = new Date(); return result; } catch (error) { result.status = "failed"; result.error = { code: "ACTION_EXECUTION_ERROR", message: error.message, details: error, }; result.endTime = new Date(); return result; } } /** * 트랜잭션 롤백 */ private async rollbackTransaction(transactionId: string): Promise { const state = this.activeTransactions.get(transactionId); if (!state) return; state.status = "rolling_back"; // 성공한 액션들을 역순으로 롤백 const successfulActions = state.actions .filter((action) => action.status === "success") .reverse(); const rollbackResults: ActionExecutionResult[] = []; for (const action of successfulActions) { try { const rollbackResult = await this.rollbackAction(action); rollbackResults.push(rollbackResult); } catch (error) { console.error(`롤백 실패: ${action.actionId}`, error); // 롤백 실패는 로그만 남기고 계속 진행 } } state.rollbackActions = rollbackResults; state.status = "rolled_back"; state.endTime = new Date(); } /** * 개별 액션 롤백 */ private async rollbackAction( action: ActionExecutionResult ): Promise { // 롤백 액션 실행 // 이 부분은 액션 타입별로 구체적인 롤백 로직 구현 필요 return { actionId: `rollback_${action.actionId}`, transactionId: action.transactionId, status: "success", startTime: new Date(), endTime: new Date(), result: "롤백 완료", }; } /** * 로직 평가 (AND/OR/CUSTOM) */ private evaluateLogic( logic: "AND" | "OR" | "CUSTOM", customLogic: string | undefined, results: Map ): boolean { switch (logic) { case "AND": return Array.from(results.values()).every((result) => result); case "OR": return Array.from(results.values()).some((result) => result); case "CUSTOM": if (!customLogic) return false; return this.evaluateCustomLogic(customLogic, results); default: return false; } } /** * 커스텀 로직 평가 */ private evaluateCustomLogic( logic: string, results: Map ): boolean { // "(A AND B) OR (C AND D)" 형태의 로직 파싱 및 평가 let expression = logic; // 변수를 실제 결과값으로 치환 for (const [id, result] of results) { expression = expression.replace( new RegExp(`\\b${id}\\b`, "g"), result.toString() ); } // AND/OR를 JavaScript 연산자로 변환 expression = expression .replace(/\bAND\b/g, "&&") .replace(/\bOR\b/g, "||") .replace(/\btrue\b/g, "true") .replace(/\bfalse\b/g, "false"); try { // 안전한 평가를 위해 Function 생성자 사용 return new Function(`return ${expression}`)(); } catch (error) { console.error("커스텀 로직 평가 오류:", error); return false; } } // ... 기타 헬퍼 메서드들 } export interface TransactionExecutionResult { success: boolean; message: string; requiresRollback: boolean; results: [string, boolean][]; } export interface RollbackHandler { actionId: string; rollbackFn: () => Promise; } ``` ### 3.2 데이터베이스 액션 실행기 ```typescript // frontend/lib/services/databaseActionExecutor.ts export class DatabaseActionExecutor { /** * 데이터베이스 액션 실행 */ static async executeAction( action: DataflowAction, context: ExtendedControlContext ): Promise { const { tableName, operation, fields, conditions } = action; switch (operation) { case "INSERT": return await this.executeInsert(tableName!, fields!, context); case "UPDATE": return await this.executeUpdate( tableName!, fields!, conditions!, context ); case "DELETE": return await this.executeDelete(tableName!, conditions!, context); case "SELECT": return await this.executeSelect( tableName!, fields!, conditions!, context ); default: throw new Error(`Unsupported database operation: ${operation}`); } } /** * 롤백 데이터 캡처 */ static async captureRollbackData( action: DataflowAction, context: ExtendedControlContext ): Promise { const { tableName, conditions } = action; if (action.operation === "UPDATE" || action.operation === "DELETE") { // 변경 전 데이터를 조회하여 저장 return await this.executeSelect(tableName!, ["*"], conditions!, context); } return null; } /** * 롤백 실행 */ static async executeRollback( originalAction: ActionExecutionResult, rollbackData: unknown ): Promise { // 원본 액션의 반대 작업 수행 // INSERT -> DELETE // UPDATE -> UPDATE (원본 데이터로) // DELETE -> INSERT (원본 데이터로) // 구체적인 롤백 로직 구현... } // ... 개별 operation 구현 메서드들 } ``` ## Phase 4: 사용자 인터페이스 개선 ### 4.1 조건부 실행 설정 UI ```typescript // frontend/components/screen/config-panels/ConditionalExecutionPanel.tsx export const ConditionalExecutionPanel: React.FC<{ config: ButtonDataflowConfig; onConfigChange: (config: ButtonDataflowConfig) => void; }> = ({ config, onConfigChange }) => { return (
{/* 실행 모드 선택 */}
{/* 트랜잭션 설정 */}
{/* 액션 그룹 설정 */}
{/* 조건부 로직 설정 */}
); }; ``` ### 4.2 트랜잭션 모니터링 UI ```typescript // frontend/components/screen/TransactionMonitor.tsx export const TransactionMonitor: React.FC = () => { const [transactions, setTransactions] = useState( [] ); return (

트랜잭션 실행 현황

{transactions.map((transaction) => (
트랜잭션 {transaction.transactionId} {transaction.status}
{transaction.actions.map((action) => (
{action.actionId} {action.status}
))}
{transaction.status === "failed" && ( )}
))}
); }; ``` ## 📋 구현 우선순위 ### 🔥 즉시 구현 (Critical) 1. **TransactionManager 기본 구조** - 트랜잭션 단위 실행 2. **롤백 메커니즘** - 실패 시 이전 상태 복구 3. **AND/OR 조건부 실행** - 기본적인 조건부 로직 ### ⚡ 단기 구현 (High) 4. **데이터베이스 액션 실행기** - 실제 DB 작업 처리 5. **에러 핸들링 강화** - 상세한 오류 정보 제공 6. **트랜잭션 상태 추적** - 실행 과정 모니터링 ### 📅 중장기 구현 (Medium) 7. **복잡한 조건부 로직** - 커스텀 로직 지원 8. **병렬 실행 지원** - 성능 최적화 9. **모니터링 UI** - 실시간 트랜잭션 추적 ## 💡 기대 효과 ### 데이터 일관성 보장 - 트랜잭션 롤백으로 부분 실행 방지 - All-or-Nothing 원칙 적용 - 데이터 정합성 확보 ### 유연한 비즈니스 로직 - 복잡한 조건부 실행 지원 - 대안 액션 자동 실행 - 비즈니스 요구사항 정확한 반영 ### 시스템 안정성 향상 - 실패 시 자동 복구 - 상세한 실행 로그 - 문제 상황 신속 파악 이 개선방안에 대한 의견이나 우선순위 조정이 필요한 부분이 있으시면 말씀해 주세요!