# 노드 연결 규칙 설계 **작성일**: 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 구현 시작