액션 노드들 로직 구현
This commit is contained in:
332
docs/node-action-target-selection-plan.md
Normal file
332
docs/node-action-target-selection-plan.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# 액션 노드 타겟 선택 시스템 개선 계획
|
||||
|
||||
## 📋 현재 문제점
|
||||
|
||||
### 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<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
|
||||
```typescript
|
||||
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
|
||||
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 고려
|
||||
|
||||
Reference in New Issue
Block a user