ERP-node/docs/node-action-target-selectio...

9.3 KiB

액션 노드 타겟 선택 시스템 개선 계획

📋 현재 문제점

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 추가

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<string, string>;
  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.tsTargetType 추가
  • 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

private static async executeInsertAction(
  node: FlowNode,
  inputData: any[],
  context: ExecutionContext
): Promise<any[]> {
  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<any[]> {
  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<any[]> {
  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

  1. Phase 4: REST API 타입
  2. Phase 5: 노드 시각화

Low Priority

  1. 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 고려