9.3 KiB
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.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
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
- Phase 1: 타입 정의 및 기본 UI
- Phase 2: 내부 DB 타입 (기존 유지)
- Phase 3: 외부 DB 타입
Medium Priority
- Phase 4: REST API 타입
- Phase 5: 노드 시각화
Low Priority
- 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 고려