# 액션 노드 타겟 선택 시스템 개선 계획 ## 📋 현재 문제점 ### 1. 타겟 타입 구분 부재 - INSERT/UPDATE/DELETE/UPSERT 액션 노드가 타겟 테이블만 선택 가능 - 내부 DB인지, 외부 DB인지, REST API인지 구분 없음 - 실행 시 항상 내부 DB로만 동작 ### 2. 외부 시스템 연동 불가 - 외부 DB에 데이터 저장 불가 - 외부 REST API 호출 불가 - 하이브리드 플로우 구성 불가 (내부 → 외부 데이터 전송) --- ## 🎯 개선 목표 액션 노드에서 다음 3가지 타겟 타입을 선택할 수 있도록 개선: ### 1. 내부 데이터베이스 (Internal DB) - 현재 시스템의 PostgreSQL - 기존 동작 유지 ### 2. 외부 데이터베이스 (External DB) - 외부 커넥션 관리에서 설정한 DB - PostgreSQL, MySQL, Oracle, MSSQL, MariaDB 지원 ### 3. REST API - 외부 REST API 호출 - HTTP 메서드: POST, PUT, PATCH, DELETE - 인증: None, Basic, Bearer Token, API Key --- ## 📐 타입 정의 확장 ### TargetType 추가 ```typescript export type TargetType = "internal" | "external" | "api"; export interface BaseActionNodeData { displayName: string; targetType: TargetType; // 🔥 새로 추가 // targetType === "internal" targetTable?: string; targetTableLabel?: string; // targetType === "external" externalConnectionId?: number; externalConnectionName?: string; externalDbType?: string; externalTargetTable?: string; externalTargetSchema?: string; // targetType === "api" apiEndpoint?: string; apiMethod?: "POST" | "PUT" | "PATCH" | "DELETE"; apiAuthType?: "none" | "basic" | "bearer" | "apikey"; apiAuthConfig?: { username?: string; password?: string; token?: string; apiKey?: string; apiKeyHeader?: string; }; apiHeaders?: Record; apiBodyTemplate?: string; // JSON 템플릿 } ``` --- ## 🎨 UI 설계 ### 1. 타겟 타입 선택 (공통) ``` ┌─────────────────────────────────────┐ │ 타겟 타입 │ │ ○ 내부 데이터베이스 (기본) │ │ ○ 외부 데이터베이스 │ │ ○ REST API │ └─────────────────────────────────────┘ ``` ### 2-A. 내부 DB 선택 시 (기존 UI 유지) ``` ┌─────────────────────────────────────┐ │ 테이블 선택: [검색 가능 Combobox] │ │ 필드 매핑: │ │ • source_field → target_field │ │ • ... │ └─────────────────────────────────────┘ ``` ### 2-B. 외부 DB 선택 시 ``` ┌─────────────────────────────────────┐ │ 외부 커넥션: [🐘 PostgreSQL - 운영DB]│ │ 스키마: [public ▼] │ │ 테이블: [users ▼] │ │ 필드 매핑: │ │ • source_field → target_field │ └─────────────────────────────────────┘ ``` ### 2-C. REST API 선택 시 ``` ┌─────────────────────────────────────┐ │ API 엔드포인트: │ │ [https://api.example.com/users] │ │ │ │ HTTP 메서드: [POST ▼] │ │ │ │ 인증 타입: [Bearer Token ▼] │ │ Token: [••••••••••••••] │ │ │ │ 헤더 (선택): │ │ Content-Type: application/json │ │ + 헤더 추가 │ │ │ │ 바디 템플릿: │ │ { │ │ "name": "{{source.name}}", │ │ "email": "{{source.email}}" │ │ } │ └─────────────────────────────────────┘ ``` --- ## 🔧 구현 단계 ### Phase 1: 타입 정의 및 기본 UI (1-2시간) - [ ] `types/node-editor.ts`에 `TargetType` 추가 - [ ] `InsertActionNodeData` 등 인터페이스 확장 - [ ] 속성 패널에 타겟 타입 선택 라디오 버튼 추가 ### Phase 2: 내부 DB 타입 (기존 유지) - [ ] `targetType === "internal"` 처리 - [ ] 기존 로직 그대로 유지 - [ ] 기본값으로 설정 ### Phase 3: 외부 DB 타입 (2-3시간) - [ ] 외부 커넥션 선택 UI - [ ] 외부 테이블/컬럼 로드 (기존 API 재사용) - [ ] 백엔드: `nodeFlowExecutionService.ts`에 외부 DB 실행 로직 추가 - [ ] `DatabaseConnectorFactory` 활용 ### Phase 4: REST API 타입 (3-4시간) - [ ] API 엔드포인트 설정 UI - [ ] HTTP 메서드 선택 - [ ] 인증 타입별 설정 UI - [ ] 바디 템플릿 에디터 (변수 치환 지원) - [ ] 백엔드: Axios를 사용한 API 호출 로직 - [ ] 응답 처리 및 에러 핸들링 ### Phase 5: 노드 시각화 개선 (1시간) - [ ] 노드에 타겟 타입 아이콘 표시 - 내부 DB: 💾 - 외부 DB: 🔌 + DB 타입 아이콘 - REST API: 🌐 - [ ] 노드 색상 구분 ### Phase 6: 검증 및 테스트 (2시간) - [ ] 타겟 타입별 필수 값 검증 - [ ] 연결 규칙 업데이트 - [ ] 통합 테스트 --- ## 🔍 백엔드 실행 로직 ### nodeFlowExecutionService.ts ```typescript private static async executeInsertAction( node: FlowNode, inputData: any[], context: ExecutionContext ): Promise { const { targetType } = node.data; switch (targetType) { case "internal": return this.executeInternalInsert(node, inputData); case "external": return this.executeExternalInsert(node, inputData); case "api": return this.executeApiInsert(node, inputData); default: throw new Error(`지원하지 않는 타겟 타입: ${targetType}`); } } // 🔥 외부 DB INSERT private static async executeExternalInsert( node: FlowNode, inputData: any[] ): Promise { const { externalConnectionId, externalTargetTable, fieldMappings } = node.data; const connector = await DatabaseConnectorFactory.getConnector( externalConnectionId!, node.data.externalDbType! ); const results = []; for (const row of inputData) { const values = fieldMappings.map(m => row[m.sourceField]); const columns = fieldMappings.map(m => m.targetField); const result = await connector.executeQuery( `INSERT INTO ${externalTargetTable} (${columns.join(", ")}) VALUES (${...})`, values ); results.push(result); } await connector.disconnect(); return results; } // 🔥 REST API INSERT (POST) private static async executeApiInsert( node: FlowNode, inputData: any[] ): Promise { const { apiEndpoint, apiMethod, apiAuthType, apiAuthConfig, apiHeaders, apiBodyTemplate } = node.data; const axios = require("axios"); const headers = { ...apiHeaders }; // 인증 헤더 추가 if (apiAuthType === "bearer" && apiAuthConfig?.token) { headers["Authorization"] = `Bearer ${apiAuthConfig.token}`; } else if (apiAuthType === "apikey" && apiAuthConfig?.apiKey) { headers[apiAuthConfig.apiKeyHeader || "X-API-Key"] = apiAuthConfig.apiKey; } const results = []; for (const row of inputData) { // 템플릿 변수 치환 const body = this.replaceTemplateVariables(apiBodyTemplate, row); const response = await axios({ method: apiMethod || "POST", url: apiEndpoint, headers, data: JSON.parse(body), }); results.push(response.data); } return results; } ``` --- ## 📊 우선순위 ### High Priority 1. **Phase 1**: 타입 정의 및 기본 UI 2. **Phase 2**: 내부 DB 타입 (기존 유지) 3. **Phase 3**: 외부 DB 타입 ### Medium Priority 4. **Phase 4**: REST API 타입 5. **Phase 5**: 노드 시각화 ### Low Priority 6. **Phase 6**: 고급 기능 (재시도, 배치 처리 등) --- ## 🎯 예상 효과 ### 1. 유연성 증가 - 다양한 시스템 간 데이터 연동 가능 - 하이브리드 플로우 구성 (내부 → 외부 → API) ### 2. 사용 사례 확장 ``` [사례 1] 내부 DB → 외부 DB 동기화 TableSource(내부) → DataTransform → INSERT(외부 DB) [사례 2] 내부 DB → REST API 전송 TableSource(내부) → DataTransform → INSERT(REST API) [사례 3] 복합 플로우 TableSource(내부) → INSERT(외부 DB) → INSERT(REST API - 알림) ``` ### 3. 기존 기능과의 호환 - 기본값: `targetType = "internal"` - 기존 플로우 마이그레이션 불필요 --- ## ⚠️ 주의사항 ### 1. 보안 - API 인증 정보 암호화 저장 - 민감 데이터 로깅 방지 ### 2. 에러 핸들링 - 외부 시스템 타임아웃 처리 - 재시도 로직 (선택적) - 부분 실패 처리 (이미 구현됨) ### 3. 성능 - 외부 DB 연결 풀 관리 (이미 구현됨) - REST API Rate Limiting 고려