432 lines
11 KiB
Markdown
432 lines
11 KiB
Markdown
|
|
# 노드 연결 규칙 설계
|
||
|
|
|
||
|
|
**작성일**: 2025-01-02
|
||
|
|
**버전**: 1.0
|
||
|
|
**상태**: 🔄 설계 중
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 목차
|
||
|
|
|
||
|
|
1. [개요](#개요)
|
||
|
|
2. [노드 분류](#노드-분류)
|
||
|
|
3. [연결 규칙 매트릭스](#연결-규칙-매트릭스)
|
||
|
|
4. [상세 연결 규칙](#상세-연결-규칙)
|
||
|
|
5. [구현 계획](#구현-계획)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 개요
|
||
|
|
|
||
|
|
### 목적
|
||
|
|
|
||
|
|
노드 간 연결 가능 여부를 명확히 정의하여:
|
||
|
|
|
||
|
|
- 사용자의 실수 방지
|
||
|
|
- 논리적으로 올바른 플로우만 생성 가능
|
||
|
|
- 명확한 오류 메시지 제공
|
||
|
|
|
||
|
|
### 기본 원칙
|
||
|
|
|
||
|
|
1. **데이터 흐름 방향**: 소스 → 변환 → 액션
|
||
|
|
2. **타입 안전성**: 출력과 입력 타입이 호환되어야 함
|
||
|
|
3. **논리적 정합성**: 의미 없는 연결 방지
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 노드 분류
|
||
|
|
|
||
|
|
### 1. 데이터 소스 노드 (Source)
|
||
|
|
|
||
|
|
**역할**: 데이터를 생성하는 시작점
|
||
|
|
|
||
|
|
- `tableSource` - 내부 테이블
|
||
|
|
- `externalDBSource` - 외부 DB
|
||
|
|
- `restAPISource` - REST API
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
|
||
|
|
- ✅ 출력만 가능 (소스 핸들)
|
||
|
|
- ❌ 입력 불가능
|
||
|
|
- 플로우의 시작점
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. 변환/조건 노드 (Transform)
|
||
|
|
|
||
|
|
**역할**: 데이터를 가공하거나 흐름을 제어
|
||
|
|
|
||
|
|
#### 2.1 데이터 변환
|
||
|
|
|
||
|
|
- `fieldMapping` - 필드 매핑
|
||
|
|
- `dataTransform` - 데이터 변환
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
|
||
|
|
- ✅ 입력 가능 (타겟 핸들)
|
||
|
|
- ✅ 출력 가능 (소스 핸들)
|
||
|
|
- 중간 파이프라인 역할
|
||
|
|
|
||
|
|
#### 2.2 조건 분기
|
||
|
|
|
||
|
|
- `condition` - 조건 분기
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
|
||
|
|
- ✅ 입력 가능 (타겟 핸들)
|
||
|
|
- ✅ 출력 가능 (TRUE/FALSE 2개의 소스 핸들)
|
||
|
|
- 흐름을 분기
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. 액션 노드 (Action)
|
||
|
|
|
||
|
|
**역할**: 실제 데이터베이스 작업 수행
|
||
|
|
|
||
|
|
- `insertAction` - INSERT
|
||
|
|
- `updateAction` - UPDATE
|
||
|
|
- `deleteAction` - DELETE
|
||
|
|
- `upsertAction` - UPSERT
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
|
||
|
|
- ✅ 입력 가능 (타겟 핸들)
|
||
|
|
- ⚠️ 출력 제한적 (성공/실패 결과만)
|
||
|
|
- 플로우의 종착점 또는 중간 액션
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. 유틸리티 노드 (Utility)
|
||
|
|
|
||
|
|
**역할**: 보조적인 기능 제공
|
||
|
|
|
||
|
|
- `log` - 로그 출력
|
||
|
|
- `comment` - 주석
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
|
||
|
|
- `log`: 입력/출력 모두 가능 (패스스루)
|
||
|
|
- `comment`: 연결 불가능 (독립 노드)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 연결 규칙 매트릭스
|
||
|
|
|
||
|
|
### 출력(From) → 입력(To) 연결 가능 여부
|
||
|
|
|
||
|
|
| From ↓ / To → | tableSource | externalDB | restAPI | condition | fieldMapping | dataTransform | insert | update | delete | upsert | log | comment |
|
||
|
|
| ----------------- | ----------- | ---------- | ------- | --------- | ------------ | ------------- | ------ | ------ | ------ | ------ | --- | ------- |
|
||
|
|
| **tableSource** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **externalDB** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **restAPI** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **condition** | ❌ | ❌ | ❌ | ⚠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **fieldMapping** | ❌ | ❌ | ❌ | ✅ | ⚠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **dataTransform** | ❌ | ❌ | ❌ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **insert** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ❌ |
|
||
|
|
| **update** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ❌ |
|
||
|
|
| **delete** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ❌ |
|
||
|
|
| **upsert** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ❌ |
|
||
|
|
| **log** | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||
|
|
| **comment** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||
|
|
|
||
|
|
**범례**:
|
||
|
|
|
||
|
|
- ✅ 허용
|
||
|
|
- ❌ 금지
|
||
|
|
- ⚠️ 조건부 허용 (경고 메시지와 함께)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 상세 연결 규칙
|
||
|
|
|
||
|
|
### 규칙 1: 소스 노드는 입력을 받을 수 없음
|
||
|
|
|
||
|
|
**금지되는 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
❌ 어떤 노드 → tableSource
|
||
|
|
❌ 어떤 노드 → externalDBSource
|
||
|
|
❌ 어떤 노드 → restAPISource
|
||
|
|
```
|
||
|
|
|
||
|
|
**이유**: 소스 노드는 데이터의 시작점이므로 외부 입력이 의미 없음
|
||
|
|
|
||
|
|
**오류 메시지**:
|
||
|
|
|
||
|
|
```
|
||
|
|
"소스 노드는 입력을 받을 수 없습니다. 소스 노드는 플로우의 시작점입니다."
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 규칙 2: 소스 노드끼리 연결 불가
|
||
|
|
|
||
|
|
**금지되는 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
❌ tableSource → externalDBSource
|
||
|
|
❌ restAPISource → tableSource
|
||
|
|
```
|
||
|
|
|
||
|
|
**이유**: 소스 노드는 독립적으로 데이터를 생성하므로 서로 연결 불필요
|
||
|
|
|
||
|
|
**오류 메시지**:
|
||
|
|
|
||
|
|
```
|
||
|
|
"소스 노드끼리는 연결할 수 없습니다. 각 소스는 독립적으로 동작합니다."
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 규칙 3: Comment 노드는 연결 불가
|
||
|
|
|
||
|
|
**금지되는 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
❌ 어떤 노드 → comment
|
||
|
|
❌ comment → 어떤 노드
|
||
|
|
```
|
||
|
|
|
||
|
|
**이유**: Comment는 설명 전용 노드로 데이터 흐름에 영향을 주지 않음
|
||
|
|
|
||
|
|
**오류 메시지**:
|
||
|
|
|
||
|
|
```
|
||
|
|
"주석 노드는 연결할 수 없습니다. 주석은 플로우 설명 용도로만 사용됩니다."
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 규칙 4: 동일한 타입의 변환 노드 연속 연결 경고
|
||
|
|
|
||
|
|
**경고가 필요한 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
⚠️ fieldMapping → fieldMapping
|
||
|
|
⚠️ dataTransform → dataTransform
|
||
|
|
⚠️ condition → condition
|
||
|
|
```
|
||
|
|
|
||
|
|
**이유**: 논리적으로 가능하지만 비효율적일 수 있음
|
||
|
|
|
||
|
|
**경고 메시지**:
|
||
|
|
|
||
|
|
```
|
||
|
|
"동일한 타입의 변환 노드가 연속으로 연결되었습니다. 하나의 노드로 통합하는 것이 효율적입니다."
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 규칙 5: 액션 노드 연속 연결 경고
|
||
|
|
|
||
|
|
**경고가 필요한 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
⚠️ insertAction → updateAction
|
||
|
|
⚠️ updateAction → deleteAction
|
||
|
|
⚠️ deleteAction → insertAction
|
||
|
|
```
|
||
|
|
|
||
|
|
**이유**: 트랜잭션 관리나 성능에 영향을 줄 수 있음
|
||
|
|
|
||
|
|
**경고 메시지**:
|
||
|
|
|
||
|
|
```
|
||
|
|
"액션 노드가 연속으로 연결되었습니다. 트랜잭션과 성능을 고려해주세요."
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 규칙 6: 자기 자신에게 연결 금지
|
||
|
|
|
||
|
|
**금지되는 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
❌ 모든 노드 → 자기 자신
|
||
|
|
```
|
||
|
|
|
||
|
|
**이유**: 무한 루프 방지
|
||
|
|
|
||
|
|
**오류 메시지**:
|
||
|
|
|
||
|
|
```
|
||
|
|
"노드는 자기 자신에게 연결할 수 없습니다."
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 규칙 7: Log 노드는 패스스루
|
||
|
|
|
||
|
|
**허용되는 연결**:
|
||
|
|
|
||
|
|
```
|
||
|
|
✅ 모든 노드 → log → 모든 노드 (소스 제외)
|
||
|
|
```
|
||
|
|
|
||
|
|
**특징**:
|
||
|
|
|
||
|
|
- Log 노드는 데이터를 그대로 전달
|
||
|
|
- 디버깅 및 모니터링 용도
|
||
|
|
- 데이터 흐름에 영향 없음
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 구현 계획
|
||
|
|
|
||
|
|
### Phase 1: 기본 금지 규칙 (우선순위: 높음)
|
||
|
|
|
||
|
|
**구현 위치**: `frontend/lib/stores/flowEditorStore.ts` - `validateConnection` 함수
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
function validateConnection(
|
||
|
|
connection: Connection,
|
||
|
|
nodes: FlowNode[]
|
||
|
|
): { valid: boolean; error?: string } {
|
||
|
|
const sourceNode = nodes.find((n) => n.id === connection.source);
|
||
|
|
const targetNode = nodes.find((n) => n.id === connection.target);
|
||
|
|
|
||
|
|
if (!sourceNode || !targetNode) {
|
||
|
|
return { valid: false, error: "노드를 찾을 수 없습니다" };
|
||
|
|
}
|
||
|
|
|
||
|
|
// 규칙 1: 소스 노드는 입력을 받을 수 없음
|
||
|
|
if (isSourceNode(targetNode.type)) {
|
||
|
|
return {
|
||
|
|
valid: false,
|
||
|
|
error:
|
||
|
|
"소스 노드는 입력을 받을 수 없습니다. 소스 노드는 플로우의 시작점입니다.",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 규칙 2: 소스 노드끼리 연결 불가
|
||
|
|
if (isSourceNode(sourceNode.type) && isSourceNode(targetNode.type)) {
|
||
|
|
return {
|
||
|
|
valid: false,
|
||
|
|
error: "소스 노드끼리는 연결할 수 없습니다.",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 규칙 3: Comment 노드는 연결 불가
|
||
|
|
if (sourceNode.type === "comment" || targetNode.type === "comment") {
|
||
|
|
return {
|
||
|
|
valid: false,
|
||
|
|
error: "주석 노드는 연결할 수 없습니다.",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 규칙 6: 자기 자신에게 연결 금지
|
||
|
|
if (connection.source === connection.target) {
|
||
|
|
return {
|
||
|
|
valid: false,
|
||
|
|
error: "노드는 자기 자신에게 연결할 수 없습니다.",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return { valid: true };
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**예상 작업 시간**: 30분
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Phase 2: 경고 규칙 (우선순위: 중간)
|
||
|
|
|
||
|
|
**구현 방법**: 연결은 허용하되 경고 표시
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
function getConnectionWarning(
|
||
|
|
connection: Connection,
|
||
|
|
nodes: FlowNode[]
|
||
|
|
): string | null {
|
||
|
|
const sourceNode = nodes.find((n) => n.id === connection.source);
|
||
|
|
const targetNode = nodes.find((n) => n.id === connection.target);
|
||
|
|
|
||
|
|
if (!sourceNode || !targetNode) return null;
|
||
|
|
|
||
|
|
// 규칙 4: 동일한 타입의 변환 노드 연속 연결
|
||
|
|
if (sourceNode.type === targetNode.type && isTransformNode(sourceNode.type)) {
|
||
|
|
return "동일한 타입의 변환 노드가 연속으로 연결되었습니다. 하나로 통합하는 것이 효율적입니다.";
|
||
|
|
}
|
||
|
|
|
||
|
|
// 규칙 5: 액션 노드 연속 연결
|
||
|
|
if (isActionNode(sourceNode.type) && isActionNode(targetNode.type)) {
|
||
|
|
return "액션 노드가 연속으로 연결되었습니다. 트랜잭션과 성능을 고려해주세요.";
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**UI 구현**:
|
||
|
|
|
||
|
|
- 경고 아이콘을 연결선 위에 표시
|
||
|
|
- 호버 시 경고 메시지 툴팁 표시
|
||
|
|
|
||
|
|
**예상 작업 시간**: 1시간
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Phase 3: 시각적 피드백 (우선순위: 낮음)
|
||
|
|
|
||
|
|
**기능**:
|
||
|
|
|
||
|
|
1. 드래그 중 호환 가능한 노드 하이라이트
|
||
|
|
2. 불가능한 연결 시도 시 빨간색 표시
|
||
|
|
3. 경고가 있는 연결은 노란색 표시
|
||
|
|
|
||
|
|
**예상 작업 시간**: 2시간
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 테스트 케이스
|
||
|
|
|
||
|
|
### 금지 테스트
|
||
|
|
|
||
|
|
- [ ] tableSource → tableSource (금지)
|
||
|
|
- [ ] fieldMapping → comment (금지)
|
||
|
|
- [ ] 자기 자신 → 자기 자신 (금지)
|
||
|
|
|
||
|
|
### 경고 테스트
|
||
|
|
|
||
|
|
- [ ] fieldMapping → fieldMapping (경고)
|
||
|
|
- [ ] insertAction → updateAction (경고)
|
||
|
|
|
||
|
|
### 정상 테스트
|
||
|
|
|
||
|
|
- [ ] tableSource → fieldMapping → insertAction
|
||
|
|
- [ ] externalDBSource → condition → (TRUE) → updateAction
|
||
|
|
- [ ] restAPISource → log → dataTransform → upsertAction
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 향후 확장
|
||
|
|
|
||
|
|
### 추가 고려사항
|
||
|
|
|
||
|
|
1. **핸들별 제약**:
|
||
|
|
|
||
|
|
- Condition 노드의 TRUE/FALSE 출력 구분
|
||
|
|
- 특정 핸들만 특정 노드 타입과 연결 가능
|
||
|
|
|
||
|
|
2. **데이터 타입 검증**:
|
||
|
|
|
||
|
|
- 숫자 필드만 계산 노드로 연결 가능
|
||
|
|
- 문자열 필드만 텍스트 변환 노드로 연결 가능
|
||
|
|
|
||
|
|
3. **순서 제약**:
|
||
|
|
- UPDATE/DELETE 전에 반드시 SELECT 필요
|
||
|
|
- 특정 변환 순서 강제
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 변경 이력
|
||
|
|
|
||
|
|
| 버전 | 날짜 | 변경 내용 | 작성자 |
|
||
|
|
| ---- | ---------- | --------- | ------ |
|
||
|
|
| 1.0 | 2025-01-02 | 초안 작성 | AI |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**다음 단계**: Phase 1 구현 시작
|