2025-09-17 11:47:57 +09:00
|
|
|
/**
|
|
|
|
|
* 외부 호출 API 클라이언트
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// 백엔드 타입과 동일한 인터페이스 정의
|
|
|
|
|
export interface ExternalCallResult {
|
|
|
|
|
success: boolean;
|
|
|
|
|
statusCode?: number;
|
|
|
|
|
response?: string;
|
|
|
|
|
error?: string;
|
|
|
|
|
executionTime: number;
|
|
|
|
|
timestamp: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ExternalCallTestRequest {
|
|
|
|
|
settings: Record<string, unknown>;
|
|
|
|
|
templateData?: Record<string, unknown>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ExternalCallExecuteRequest {
|
|
|
|
|
diagramId: number;
|
|
|
|
|
relationshipId: string;
|
|
|
|
|
settings: Record<string, unknown>;
|
|
|
|
|
templateData?: Record<string, unknown>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ValidationResult {
|
|
|
|
|
valid: boolean;
|
|
|
|
|
errors: string[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 외부 호출 API 클래스
|
|
|
|
|
*/
|
|
|
|
|
// API URL 동적 설정 - 기존 client.ts와 동일한 로직
|
|
|
|
|
const getApiBaseUrl = (): string => {
|
2025-10-02 17:28:52 +09:00
|
|
|
// 1. 환경변수가 있으면 우선 사용
|
|
|
|
|
if (process.env.NEXT_PUBLIC_API_URL) {
|
|
|
|
|
return process.env.NEXT_PUBLIC_API_URL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 클라이언트 사이드에서 동적 설정
|
2025-09-17 11:47:57 +09:00
|
|
|
if (typeof window !== "undefined") {
|
|
|
|
|
const currentHost = window.location.hostname;
|
|
|
|
|
const currentPort = window.location.port;
|
|
|
|
|
|
|
|
|
|
// 로컬 개발환경: localhost:9771 또는 localhost:3000 → localhost:8080
|
|
|
|
|
if (
|
|
|
|
|
(currentHost === "localhost" || currentHost === "127.0.0.1") &&
|
|
|
|
|
(currentPort === "9771" || currentPort === "3000")
|
|
|
|
|
) {
|
|
|
|
|
return "http://localhost:8080/api";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 17:28:52 +09:00
|
|
|
// 3. 기본값
|
|
|
|
|
return "http://localhost:8080/api";
|
2025-09-17 11:47:57 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export class ExternalCallAPI {
|
|
|
|
|
private static readonly BASE_URL = `${getApiBaseUrl()}/external-calls`;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 외부 호출 테스트 실행
|
|
|
|
|
*/
|
|
|
|
|
static async testExternalCall(request: ExternalCallTestRequest): Promise<{
|
|
|
|
|
success: boolean;
|
|
|
|
|
result?: ExternalCallResult;
|
|
|
|
|
error?: string;
|
|
|
|
|
}> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${this.BASE_URL}/test`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(request),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 응답이 JSON인지 확인
|
|
|
|
|
const contentType = response.headers.get("content-type");
|
|
|
|
|
if (!contentType || !contentType.includes("application/json")) {
|
|
|
|
|
const text = await response.text();
|
|
|
|
|
throw new Error(`서버에서 JSON이 아닌 응답을 반환했습니다: ${text.substring(0, 100)}...`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(data.error || `HTTP ${response.status}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("외부 호출 테스트 실패:", error);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 외부 호출 실행
|
|
|
|
|
*/
|
|
|
|
|
static async executeExternalCall(request: ExternalCallExecuteRequest): Promise<{
|
|
|
|
|
success: boolean;
|
|
|
|
|
result?: ExternalCallResult;
|
|
|
|
|
error?: string;
|
|
|
|
|
}> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${this.BASE_URL}/execute`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(request),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(data.error || `HTTP ${response.status}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("외부 호출 실행 실패:", error);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 지원되는 외부 호출 타입 목록 조회
|
|
|
|
|
*/
|
|
|
|
|
static async getSupportedTypes(): Promise<{
|
|
|
|
|
success: boolean;
|
|
|
|
|
supportedTypes?: Record<string, any>;
|
|
|
|
|
error?: string;
|
|
|
|
|
}> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${this.BASE_URL}/types`);
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(data.error || `HTTP ${response.status}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("지원 타입 조회 실패:", error);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 외부 호출 설정 검증
|
|
|
|
|
*/
|
|
|
|
|
static async validateSettings(settings: Record<string, unknown>): Promise<{
|
|
|
|
|
success: boolean;
|
|
|
|
|
validation?: ValidationResult;
|
|
|
|
|
error?: string;
|
|
|
|
|
}> {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${this.BASE_URL}/validate`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({ settings }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(data.error || `HTTP ${response.status}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("설정 검증 실패:", error);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|