feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect } from "react";
|
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
|
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
|
|
|
|
import { useFlowEditorStore } from "@/lib/stores/flowEditorStore";
|
|
|
|
|
|
import { RestAPISourceNodeData } from "@/types/node-editor";
|
|
|
|
|
|
import { Globe, Plus, Trash2 } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
|
|
interface RestAPISourcePropertiesProps {
|
|
|
|
|
|
nodeId: string;
|
|
|
|
|
|
data: RestAPISourceNodeData;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
|
|
|
|
|
|
const AUTH_TYPES = [
|
|
|
|
|
|
{ value: "none", label: "인증 없음" },
|
|
|
|
|
|
{ value: "bearer", label: "Bearer Token" },
|
|
|
|
|
|
{ value: "basic", label: "Basic Auth" },
|
|
|
|
|
|
{ value: "apikey", label: "API Key" },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
export function RestAPISourceProperties({ nodeId, data }: RestAPISourcePropertiesProps) {
|
|
|
|
|
|
const { updateNode } = useFlowEditorStore();
|
|
|
|
|
|
|
|
|
|
|
|
const [displayName, setDisplayName] = useState(data.displayName || "");
|
|
|
|
|
|
const [url, setUrl] = useState(data.url || "");
|
|
|
|
|
|
const [method, setMethod] = useState(data.method || "GET");
|
|
|
|
|
|
const [headers, setHeaders] = useState(data.headers || {});
|
|
|
|
|
|
const [newHeaderKey, setNewHeaderKey] = useState("");
|
|
|
|
|
|
const [newHeaderValue, setNewHeaderValue] = useState("");
|
|
|
|
|
|
const [body, setBody] = useState(JSON.stringify(data.body || {}, null, 2));
|
|
|
|
|
|
const [authType, setAuthType] = useState(data.authentication?.type || "none");
|
|
|
|
|
|
const [authToken, setAuthToken] = useState(data.authentication?.token || "");
|
|
|
|
|
|
const [timeout, setTimeout] = useState(data.timeout?.toString() || "30000");
|
|
|
|
|
|
const [responseMapping, setResponseMapping] = useState(data.responseMapping || "");
|
2025-10-13 12:00:41 +09:00
|
|
|
|
const [responseFields, setResponseFields] = useState(data.responseFields || []);
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
setDisplayName(data.displayName || "");
|
|
|
|
|
|
setUrl(data.url || "");
|
|
|
|
|
|
setMethod(data.method || "GET");
|
|
|
|
|
|
setHeaders(data.headers || {});
|
|
|
|
|
|
setBody(JSON.stringify(data.body || {}, null, 2));
|
|
|
|
|
|
setAuthType(data.authentication?.type || "none");
|
|
|
|
|
|
setAuthToken(data.authentication?.token || "");
|
|
|
|
|
|
setTimeout(data.timeout?.toString() || "30000");
|
|
|
|
|
|
setResponseMapping(data.responseMapping || "");
|
2025-10-13 12:00:41 +09:00
|
|
|
|
setResponseFields(data.responseFields || []);
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
}, [data]);
|
|
|
|
|
|
|
|
|
|
|
|
const handleApply = () => {
|
|
|
|
|
|
let parsedBody = {};
|
|
|
|
|
|
try {
|
|
|
|
|
|
parsedBody = body.trim() ? JSON.parse(body) : {};
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
alert("Body JSON 형식이 올바르지 않습니다.");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-13 12:00:41 +09:00
|
|
|
|
console.log("🔧 REST API 노드 업데이트 중...");
|
|
|
|
|
|
console.log("📦 responseFields:", responseFields);
|
|
|
|
|
|
console.log("📊 responseFields 개수:", responseFields.length);
|
|
|
|
|
|
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
updateNode(nodeId, {
|
|
|
|
|
|
displayName,
|
|
|
|
|
|
url,
|
|
|
|
|
|
method: method as any,
|
|
|
|
|
|
headers,
|
|
|
|
|
|
body: parsedBody,
|
|
|
|
|
|
authentication: {
|
|
|
|
|
|
type: authType as any,
|
|
|
|
|
|
token: authToken || undefined,
|
|
|
|
|
|
},
|
|
|
|
|
|
timeout: parseInt(timeout) || 30000,
|
|
|
|
|
|
responseMapping,
|
2025-10-13 12:00:41 +09:00
|
|
|
|
responseFields,
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
});
|
2025-10-13 12:00:41 +09:00
|
|
|
|
|
|
|
|
|
|
console.log("✅ REST API 노드 업데이트 완료");
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const addHeader = () => {
|
|
|
|
|
|
if (newHeaderKey.trim() && newHeaderValue.trim()) {
|
|
|
|
|
|
setHeaders({ ...headers, [newHeaderKey.trim()]: newHeaderValue.trim() });
|
|
|
|
|
|
setNewHeaderKey("");
|
|
|
|
|
|
setNewHeaderValue("");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const removeHeader = (key: string) => {
|
|
|
|
|
|
const newHeaders = { ...headers };
|
|
|
|
|
|
delete newHeaders[key];
|
|
|
|
|
|
setHeaders(newHeaders);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-10-13 12:00:41 +09:00
|
|
|
|
const addResponseField = () => {
|
|
|
|
|
|
const newFields = [
|
|
|
|
|
|
...responseFields,
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "",
|
|
|
|
|
|
label: "",
|
|
|
|
|
|
dataType: "TEXT",
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
console.log("➕ 응답 필드 추가:", newFields);
|
|
|
|
|
|
setResponseFields(newFields);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const updateResponseField = (index: number, field: string, value: string) => {
|
|
|
|
|
|
const updated = [...responseFields];
|
|
|
|
|
|
updated[index] = { ...updated[index], [field]: value };
|
|
|
|
|
|
console.log(`✏️ 응답 필드 ${index} 업데이트 (${field}=${value}):`, updated);
|
|
|
|
|
|
setResponseFields(updated);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const removeResponseField = (index: number) => {
|
|
|
|
|
|
const newFields = responseFields.filter((_, i) => i !== index);
|
|
|
|
|
|
console.log("🗑️ 응답 필드 삭제:", newFields);
|
|
|
|
|
|
setResponseFields(newFields);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
return (
|
2025-10-24 14:11:12 +09:00
|
|
|
|
<div className="space-y-4 p-4 pb-8">
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
<div className="flex items-center gap-2 rounded-md bg-teal-50 p-2">
|
|
|
|
|
|
<Globe className="h-4 w-4 text-teal-600" />
|
|
|
|
|
|
<span className="font-semibold text-teal-600">REST API 소스</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label htmlFor="displayName" className="text-xs">
|
|
|
|
|
|
표시 이름
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="displayName"
|
|
|
|
|
|
value={displayName}
|
|
|
|
|
|
onChange={(e) => setDisplayName(e.target.value)}
|
|
|
|
|
|
placeholder="노드에 표시될 이름"
|
|
|
|
|
|
className="mt-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label htmlFor="url" className="text-xs">
|
|
|
|
|
|
API URL
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="url"
|
|
|
|
|
|
value={url}
|
|
|
|
|
|
onChange={(e) => setUrl(e.target.value)}
|
|
|
|
|
|
placeholder="https://api.example.com/data"
|
|
|
|
|
|
className="mt-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label className="text-xs">HTTP 메서드</Label>
|
|
|
|
|
|
<Select value={method} onValueChange={setMethod}>
|
|
|
|
|
|
<SelectTrigger className="mt-1">
|
|
|
|
|
|
<SelectValue />
|
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent>
|
|
|
|
|
|
{HTTP_METHODS.map((m) => (
|
|
|
|
|
|
<SelectItem key={m} value={m}>
|
|
|
|
|
|
{m}
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label className="text-xs">헤더</Label>
|
|
|
|
|
|
<div className="mt-1 space-y-2">
|
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={newHeaderKey}
|
|
|
|
|
|
onChange={(e) => setNewHeaderKey(e.target.value)}
|
|
|
|
|
|
placeholder="Key"
|
|
|
|
|
|
className="text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={newHeaderValue}
|
|
|
|
|
|
onChange={(e) => setNewHeaderValue(e.target.value)}
|
|
|
|
|
|
onKeyDown={(e) => e.key === "Enter" && addHeader()}
|
|
|
|
|
|
placeholder="Value"
|
|
|
|
|
|
className="text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Button size="sm" onClick={addHeader}>
|
|
|
|
|
|
<Plus className="h-4 w-4" />
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="max-h-[100px] space-y-1 overflow-y-auto">
|
|
|
|
|
|
{Object.entries(headers).map(([key, value]) => (
|
|
|
|
|
|
<div key={key} className="flex items-center justify-between rounded bg-teal-50 px-2 py-1">
|
|
|
|
|
|
<span className="text-xs">
|
|
|
|
|
|
<span className="font-medium">{key}:</span> {value}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<Button variant="ghost" size="sm" onClick={() => removeHeader(key)}>
|
|
|
|
|
|
<Trash2 className="h-3 w-3 text-red-500" />
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{(method === "POST" || method === "PUT" || method === "PATCH") && (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label htmlFor="body" className="text-xs">
|
|
|
|
|
|
Body (JSON)
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Textarea
|
|
|
|
|
|
id="body"
|
|
|
|
|
|
value={body}
|
|
|
|
|
|
onChange={(e) => setBody(e.target.value)}
|
|
|
|
|
|
placeholder='{"key": "value"}'
|
|
|
|
|
|
className="mt-1 font-mono text-sm"
|
|
|
|
|
|
rows={5}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label className="text-xs">인증</Label>
|
|
|
|
|
|
<Select value={authType} onValueChange={setAuthType}>
|
|
|
|
|
|
<SelectTrigger className="mt-1">
|
|
|
|
|
|
<SelectValue />
|
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent>
|
|
|
|
|
|
{AUTH_TYPES.map((type) => (
|
|
|
|
|
|
<SelectItem key={type.value} value={type.value}>
|
|
|
|
|
|
{type.label}
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
{authType !== "none" && (
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={authToken}
|
|
|
|
|
|
onChange={(e) => setAuthToken(e.target.value)}
|
|
|
|
|
|
placeholder="토큰/키 입력"
|
|
|
|
|
|
className="mt-2 text-sm"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label htmlFor="timeout" className="text-xs">
|
|
|
|
|
|
타임아웃 (ms)
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="timeout"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
value={timeout}
|
|
|
|
|
|
onChange={(e) => setTimeout(e.target.value)}
|
|
|
|
|
|
className="mt-1 text-sm"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Label htmlFor="responseMapping" className="text-xs">
|
|
|
|
|
|
응답 매핑 (JSON 경로)
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="responseMapping"
|
|
|
|
|
|
value={responseMapping}
|
|
|
|
|
|
onChange={(e) => setResponseMapping(e.target.value)}
|
|
|
|
|
|
placeholder="예: data.items"
|
|
|
|
|
|
className="mt-1 text-sm"
|
|
|
|
|
|
/>
|
2025-10-13 12:00:41 +09:00
|
|
|
|
<p className="mt-1 text-xs text-gray-500">배열 데이터의 경로를 지정하세요 (예: data.items, result.users)</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div className="mb-2 flex items-center justify-between">
|
|
|
|
|
|
<Label className="text-xs">응답 필드 정의</Label>
|
|
|
|
|
|
<Button size="sm" variant="outline" onClick={addResponseField}>
|
|
|
|
|
|
<Plus className="mr-1 h-3 w-3" />
|
|
|
|
|
|
필드 추가
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="max-h-[300px] space-y-2 overflow-y-auto">
|
|
|
|
|
|
{responseFields.length === 0 ? (
|
|
|
|
|
|
<div className="rounded border border-dashed p-3 text-center text-xs text-gray-400">
|
|
|
|
|
|
응답 필드를 추가하여 데이터 구조를 정의하세요
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
responseFields.map((field, index) => (
|
|
|
|
|
|
<div key={index} className="rounded border bg-gray-50 p-2">
|
|
|
|
|
|
<div className="mb-2 flex items-center justify-between">
|
|
|
|
|
|
<span className="text-xs font-medium text-gray-700">필드 {index + 1}</span>
|
|
|
|
|
|
<Button variant="ghost" size="sm" onClick={() => removeResponseField(index)}>
|
|
|
|
|
|
<Trash2 className="h-3 w-3 text-red-500" />
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={field.name}
|
|
|
|
|
|
onChange={(e) => updateResponseField(index, "name", e.target.value)}
|
|
|
|
|
|
placeholder="필드명 (예: userId)"
|
|
|
|
|
|
className="text-xs"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={field.label || ""}
|
|
|
|
|
|
onChange={(e) => updateResponseField(index, "label", e.target.value)}
|
|
|
|
|
|
placeholder="표시명 (예: 사용자 ID)"
|
|
|
|
|
|
className="text-xs"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<Select
|
|
|
|
|
|
value={field.dataType || "TEXT"}
|
|
|
|
|
|
onValueChange={(value) => updateResponseField(index, "dataType", value)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<SelectTrigger className="text-xs">
|
|
|
|
|
|
<SelectValue placeholder="데이터 타입" />
|
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent>
|
|
|
|
|
|
<SelectItem value="TEXT">텍스트</SelectItem>
|
|
|
|
|
|
<SelectItem value="NUMBER">숫자</SelectItem>
|
|
|
|
|
|
<SelectItem value="DATE">날짜</SelectItem>
|
|
|
|
|
|
<SelectItem value="BOOLEAN">참/거짓</SelectItem>
|
|
|
|
|
|
<SelectItem value="JSON">JSON</SelectItem>
|
|
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
))
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
feat: 노드 기반 데이터 플로우 시스템 구현
- 노드 에디터 UI 구현 (React Flow 기반)
- TableSource, DataTransform, INSERT, UPDATE, DELETE, UPSERT 노드
- 드래그앤드롭 노드 추가 및 연결
- 속성 패널을 통한 노드 설정
- 실시간 필드 라벨 표시 (column_labels 테이블 연동)
- 데이터 변환 노드 (DataTransform) 기능
- EXPLODE: 구분자로 1개 행 → 여러 행 확장
- UPPERCASE, LOWERCASE, TRIM, CONCAT, SPLIT, REPLACE 등 12가지 변환 타입
- In-place 변환 지원 (타겟 필드 생략 시 소스 필드 덮어쓰기)
- 변환된 필드가 하위 액션 노드에 자동 전달
- 노드 플로우 실행 엔진
- 위상 정렬을 통한 노드 실행 순서 결정
- 레벨별 병렬 실행 (Promise.allSettled)
- 부분 실패 허용 (한 노드 실패 시 연결된 하위 노드만 스킵)
- 트랜잭션 기반 안전한 데이터 처리
- UPSERT 액션 로직 구현
- DB 제약 조건 없이 SELECT → UPDATE or INSERT 방식
- 복합 충돌 키 지원 (예: sales_no + product_name)
- 파라미터 인덱스 정확한 매핑
- 데이터 소스 자동 감지
- 테이블 선택 데이터 (selectedRowsData) 자동 주입
- 폼 입력 데이터 (formData) 자동 주입
- TableSource 노드가 외부 데이터 우선 사용
- 버튼 컴포넌트 통합
- 기존 관계 실행 + 새 노드 플로우 실행 하이브리드 지원
- 노드 플로우 선택 UI 추가
- API 클라이언트 통합 (Axios)
- 개발 문서 작성
- 노드 기반 제어 시스템 개선 계획
- 노드 연결 규칙 설계
- 노드 실행 엔진 설계
- 노드 구조 개선안
- 버튼 통합 분석
2025-10-02 16:22:29 +09:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<Button onClick={handleApply} className="w-full">
|
|
|
|
|
|
적용
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|