"use client"; import React, { useState, useCallback, useEffect } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Globe, Settings, TestTube, History, Info } from "lucide-react"; // 타입 import import { ExternalCallConfig, ExternalCallPanelProps, RestApiSettings as RestApiSettingsType, ApiTestResult, } from "@/types/external-call/ExternalCallTypes"; import { DataMappingConfig, TableInfo } from "@/types/external-call/DataMappingTypes"; // 하위 컴포넌트 import import RestApiSettings from "./RestApiSettings"; import ExternalCallTestPanel from "./ExternalCallTestPanel"; import { DataMappingSettings } from "./DataMappingSettings"; /** * 🌐 외부호출 메인 패널 컴포넌트 * * 데이터 저장 기능과 완전히 분리된 독립적인 외부호출 전용 패널 * REST API 설정, 테스트, 실행 이력 등을 통합 관리 */ const ExternalCallPanel: React.FC = ({ relationshipId, onSettingsChange, initialSettings, readonly = false, }) => { console.log("🌐 [ExternalCallPanel] Component mounted with props:", { relationshipId, initialSettings, readonly, }); // 상태 관리 const [config, setConfig] = useState( () => initialSettings || { callType: "rest-api", restApiSettings: { apiUrl: "", httpMethod: "POST", headers: { "Content-Type": "application/json", Accept: "application/json", }, bodyTemplate: `{ "message": "데이터가 업데이트되었습니다", "data": {{sourceData}}, "timestamp": "{{timestamp}}", "relationshipId": "{{relationshipId}}" }`, authentication: { type: "none", }, timeout: 30000, // 30초 retryCount: 3, }, }, ); const [activeTab, setActiveTab] = useState("settings"); const [lastTestResult, setLastTestResult] = useState(null); const [isConfigValid, setIsConfigValid] = useState(false); // 데이터 매핑 상태 const [dataMappingConfig, setDataMappingConfig] = useState(() => ({ direction: "none", })); // 사용 가능한 테이블 목록 (임시 데이터) const [availableTables] = useState([ { name: "customers", displayName: "고객", fields: [ { name: "id", dataType: "number", nullable: false, isPrimaryKey: true }, { name: "name", dataType: "string", nullable: false }, { name: "email", dataType: "string", nullable: true }, { name: "phone", dataType: "string", nullable: true }, { name: "created_at", dataType: "date", nullable: false }, ], }, { name: "orders", displayName: "주문", fields: [ { name: "id", dataType: "number", nullable: false, isPrimaryKey: true }, { name: "customer_id", dataType: "number", nullable: false }, { name: "product_name", dataType: "string", nullable: false }, { name: "quantity", dataType: "number", nullable: false }, { name: "price", dataType: "number", nullable: false }, { name: "status", dataType: "string", nullable: false }, { name: "order_date", dataType: "date", nullable: false }, ], }, { name: "products", displayName: "제품", fields: [ { name: "id", dataType: "number", nullable: false, isPrimaryKey: true }, { name: "name", dataType: "string", nullable: false }, { name: "price", dataType: "number", nullable: false }, { name: "stock", dataType: "number", nullable: false }, { name: "category", dataType: "string", nullable: true }, ], }, ]); // 설정 변경 핸들러 const handleRestApiSettingsChange = useCallback( (newSettings: RestApiSettingsType) => { const updatedConfig: ExternalCallConfig = { ...config, restApiSettings: newSettings, metadata: { ...config.metadata, updatedAt: new Date().toISOString(), version: "1.0", }, }; setConfig(updatedConfig); onSettingsChange({ ...updatedConfig, dataMappingConfig, }); }, [config, onSettingsChange, dataMappingConfig], ); // 테스트 결과 핸들러 const handleTestResult = useCallback((result: ApiTestResult) => { setLastTestResult(result); // 테스트 탭에 머물러서 응답 정보를 바로 확인할 수 있도록 함 // (이전에는 성공 시 자동으로 history 탭으로 이동했음) }, []); // 설정 유효성 검사 const validateConfig = useCallback(() => { const { restApiSettings } = config; // HTTP 메서드에 따라 바디 필요 여부 결정 const methodNeedsBody = !["GET", "HEAD", "DELETE"].includes(restApiSettings.httpMethod?.toUpperCase()); const isValid = !!( restApiSettings.apiUrl && restApiSettings.apiUrl.startsWith("http") && restApiSettings.httpMethod && (methodNeedsBody ? restApiSettings.bodyTemplate : true) // GET/HEAD/DELETE는 바디 불필요 ); setIsConfigValid(isValid); return isValid; }, [config]); // 설정 변경 시 유효성 검사 실행 useEffect(() => { validateConfig(); }, [validateConfig]); return (
{/* 헤더 */}
외부 호출 설정 {isConfigValid ? "설정 완료" : "설정 필요"}
관계 실행 시 외부 API를 호출하여 데이터를 전송하거나 알림을 보낼 수 있습니다.
{/* 메인 탭 컨텐츠 */} API 설정 🔄 데이터 매핑 테스트 이력 정보 {/* API 설정 탭 */} {/* 데이터 매핑 탭 */} {/* 테스트 탭 */} {isConfigValid ? ( ) : ( API 테스트를 실행하려면 먼저 설정 탭에서 필수 정보를 입력해주세요. )} {/* 이력 탭 */} 실행 이력 {lastTestResult ? (
최근 테스트 결과 {lastTestResult.success ? "성공" : "실패"}
상태 코드: {lastTestResult.statusCode || "N/A"}
응답 시간: {lastTestResult.responseTime}ms
{lastTestResult.error && ( {lastTestResult.error} )} {lastTestResult.responseData && (
응답 데이터:
                        {JSON.stringify(lastTestResult.responseData, null, 2)}
                      
)}
) : (

아직 실행 이력이 없습니다.

테스트 탭에서 API 호출을 테스트해보세요.

)}
{/* 정보 탭 */} 템플릿 변수 가이드
요청 바디에서 사용할 수 있는 템플릿 변수들입니다:
{"{{sourceData}}"} 소스 노드의 전체 데이터
{"{{timestamp}}"} 현재 타임스탬프
{"{{relationshipId}}"} 관계 ID
{"{{userId}}"} 현재 사용자 ID
{"{{executionId}}"} 실행 ID
템플릿 변수는 실제 실행 시 해당 값으로 자동 치환됩니다. JSON 형식의 데이터는 따옴표 없이 사용하세요. (예: {"{{sourceData}}"})
설정 정보
관계 ID: {relationshipId}
호출 타입: {config.callType.toUpperCase()}
설정 상태: {isConfigValid ? "완료" : "미완료"}
{config.metadata?.updatedAt && (
마지막 수정: {new Date(config.metadata.updatedAt).toLocaleString()}
)}
); }; export default ExternalCallPanel;