fix: 테스트 위젯 최종 수정 및 충돌 해결
This commit is contained in:
@@ -39,12 +39,12 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
const parseTextData = (text: string): any[] => {
|
||||
// XML 형식 감지
|
||||
if (text.trim().startsWith("<?xml") || text.trim().startsWith("<result>")) {
|
||||
console.log("📄 XML 형식 데이터 감지");
|
||||
// console.log("📄 XML 형식 데이터 감지");
|
||||
return parseXmlData(text);
|
||||
}
|
||||
|
||||
// CSV 형식 (기상청 특보)
|
||||
console.log("📄 CSV 형식 데이터 감지");
|
||||
// console.log("📄 CSV 형식 데이터 감지");
|
||||
const lines = text.split("\n").filter((line) => {
|
||||
const trimmed = line.trim();
|
||||
return trimmed && !trimmed.startsWith("#") && trimmed !== "=";
|
||||
@@ -98,7 +98,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
results.push(obj);
|
||||
}
|
||||
|
||||
console.log(`✅ XML 파싱 완료: ${results.length}개 레코드`);
|
||||
// console.log(`✅ XML 파싱 완료: ${results.length}개 레코드`);
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error("❌ XML 파싱 실패:", error);
|
||||
@@ -107,14 +107,78 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
};
|
||||
|
||||
const loadRestApiData = useCallback(async (source: ChartDataSource) => {
|
||||
if (!source.endpoint) {
|
||||
// 🆕 외부 연결 ID가 있으면 먼저 외부 연결 정보를 가져옴
|
||||
let actualEndpoint = source.endpoint;
|
||||
let actualQueryParams = source.queryParams;
|
||||
let actualHeaders = source.headers;
|
||||
|
||||
if (source.externalConnectionId) {
|
||||
// console.log("🔗 외부 연결 ID 감지:", source.externalConnectionId);
|
||||
try {
|
||||
const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection");
|
||||
const connection = await ExternalDbConnectionAPI.getApiConnectionById(Number(source.externalConnectionId));
|
||||
|
||||
if (connection) {
|
||||
// console.log("✅ 외부 연결 정보 가져옴:", connection);
|
||||
|
||||
// 전체 엔드포인트 URL 생성
|
||||
actualEndpoint = connection.endpoint_path
|
||||
? `${connection.base_url}${connection.endpoint_path}`
|
||||
: connection.base_url;
|
||||
|
||||
// console.log("📍 실제 엔드포인트:", actualEndpoint);
|
||||
|
||||
// 기본 헤더 적용
|
||||
const headers: any[] = [];
|
||||
if (connection.default_headers && Object.keys(connection.default_headers).length > 0) {
|
||||
Object.entries(connection.default_headers).forEach(([key, value]) => {
|
||||
headers.push({ key, value });
|
||||
});
|
||||
}
|
||||
|
||||
// 인증 정보 적용
|
||||
const queryParams: any[] = [];
|
||||
if (connection.auth_type && connection.auth_type !== "none" && connection.auth_config) {
|
||||
const authConfig = connection.auth_config;
|
||||
|
||||
if (connection.auth_type === "api-key") {
|
||||
if (authConfig.keyLocation === "header" && authConfig.keyName && authConfig.keyValue) {
|
||||
headers.push({ key: authConfig.keyName, value: authConfig.keyValue });
|
||||
// console.log("🔑 API Key 헤더 추가:", authConfig.keyName);
|
||||
} else if (authConfig.keyLocation === "query" && authConfig.keyName && authConfig.keyValue) {
|
||||
const actualKeyName = authConfig.keyName === "apiKey" ? "key" : authConfig.keyName;
|
||||
queryParams.push({ key: actualKeyName, value: authConfig.keyValue });
|
||||
// console.log("🔑 API Key 쿼리 파라미터 추가:", actualKeyName);
|
||||
}
|
||||
} else if (connection.auth_type === "bearer" && authConfig.token) {
|
||||
headers.push({ key: "Authorization", value: `Bearer ${authConfig.token}` });
|
||||
// console.log("🔑 Bearer Token 헤더 추가");
|
||||
} else if (connection.auth_type === "basic" && authConfig.username && authConfig.password) {
|
||||
const credentials = btoa(`${authConfig.username}:${authConfig.password}`);
|
||||
headers.push({ key: "Authorization", value: `Basic ${credentials}` });
|
||||
// console.log("🔑 Basic Auth 헤더 추가");
|
||||
}
|
||||
}
|
||||
|
||||
actualHeaders = headers;
|
||||
actualQueryParams = queryParams;
|
||||
|
||||
// console.log("✅ 최종 헤더:", actualHeaders);
|
||||
// console.log("✅ 최종 쿼리 파라미터:", actualQueryParams);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("❌ 외부 연결 정보 가져오기 실패:", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!actualEndpoint) {
|
||||
throw new Error("API endpoint가 없습니다.");
|
||||
}
|
||||
|
||||
// 쿼리 파라미터 처리
|
||||
const queryParamsObj: Record<string, string> = {};
|
||||
if (source.queryParams && Array.isArray(source.queryParams)) {
|
||||
source.queryParams.forEach((param) => {
|
||||
if (actualQueryParams && Array.isArray(actualQueryParams)) {
|
||||
actualQueryParams.forEach((param) => {
|
||||
if (param.key && param.value) {
|
||||
queryParamsObj[param.key] = param.value;
|
||||
}
|
||||
@@ -123,34 +187,33 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
|
||||
// 헤더 처리
|
||||
const headersObj: Record<string, string> = {};
|
||||
if (source.headers && Array.isArray(source.headers)) {
|
||||
source.headers.forEach((header) => {
|
||||
if (actualHeaders && Array.isArray(actualHeaders)) {
|
||||
actualHeaders.forEach((header) => {
|
||||
if (header.key && header.value) {
|
||||
headersObj[header.key] = header.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log("🌐 API 호출 준비:", {
|
||||
endpoint: source.endpoint,
|
||||
queryParams: queryParamsObj,
|
||||
headers: headersObj,
|
||||
});
|
||||
console.log("🔍 원본 source.queryParams:", source.queryParams);
|
||||
console.log("🔍 원본 source.headers:", source.headers);
|
||||
// console.log("🌐 API 호출 준비:", {
|
||||
// endpoint: actualEndpoint,
|
||||
// queryParams: queryParamsObj,
|
||||
// headers: headersObj,
|
||||
// });
|
||||
|
||||
const response = await fetch(getApiUrl("/api/dashboards/fetch-external-api"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
url: source.endpoint,
|
||||
url: actualEndpoint,
|
||||
method: "GET",
|
||||
headers: headersObj,
|
||||
queryParams: queryParamsObj,
|
||||
}),
|
||||
});
|
||||
|
||||
console.log("🌐 API 응답 상태:", response.status);
|
||||
// console.log("🌐 API 응답 상태:", response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
@@ -163,22 +226,22 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
|
||||
let apiData = result.data;
|
||||
|
||||
console.log("🔍 API 응답 데이터 타입:", typeof apiData);
|
||||
console.log("🔍 API 응답 데이터 (처음 500자):", typeof apiData === "string" ? apiData.substring(0, 500) : JSON.stringify(apiData).substring(0, 500));
|
||||
// console.log("🔍 API 응답 데이터 타입:", typeof apiData);
|
||||
// console.log("🔍 API 응답 데이터 (처음 500자):", typeof apiData === "string" ? apiData.substring(0, 500) : JSON.stringify(apiData).substring(0, 500));
|
||||
|
||||
// 백엔드가 {text: "XML..."} 형태로 감싼 경우 처리
|
||||
if (apiData && typeof apiData === "object" && apiData.text && typeof apiData.text === "string") {
|
||||
console.log("📦 백엔드가 text 필드로 감싼 데이터 감지");
|
||||
// console.log("📦 백엔드가 text 필드로 감싼 데이터 감지");
|
||||
apiData = parseTextData(apiData.text);
|
||||
console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
// console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
} else if (typeof apiData === "string") {
|
||||
console.log("📄 텍스트 형식 데이터 감지, 파싱 시도");
|
||||
// console.log("📄 텍스트 형식 데이터 감지, 파싱 시도");
|
||||
apiData = parseTextData(apiData);
|
||||
console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
// console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
} else if (Array.isArray(apiData)) {
|
||||
console.log("✅ 이미 배열 형태의 데이터입니다.");
|
||||
// console.log("✅ 이미 배열 형태의 데이터입니다.");
|
||||
} else {
|
||||
console.log("⚠️ 예상치 못한 데이터 형식입니다. 배열로 변환 시도.");
|
||||
// console.log("⚠️ 예상치 못한 데이터 형식입니다. 배열로 변환 시도.");
|
||||
apiData = [apiData];
|
||||
}
|
||||
|
||||
@@ -220,7 +283,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
}, []);
|
||||
|
||||
const convertToAlerts = useCallback((rows: any[], sourceName: string): Alert[] => {
|
||||
console.log("🔄 convertToAlerts 호출:", rows.length, "개 행");
|
||||
// console.log("🔄 convertToAlerts 호출:", rows.length, "개 행");
|
||||
|
||||
return rows.map((row: any, index: number) => {
|
||||
// 타입 결정 (UTIC XML 기준)
|
||||
@@ -325,7 +388,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
source: sourceName,
|
||||
};
|
||||
|
||||
console.log(` ✅ Alert ${index}:`, alert);
|
||||
// console.log(` ✅ Alert ${index}:`, alert);
|
||||
return alert;
|
||||
});
|
||||
}, []);
|
||||
@@ -338,19 +401,19 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
console.log("🔄 RiskAlertTestWidget 데이터 로딩 시작:", dataSources.length, "개 소스");
|
||||
// console.log("🔄 RiskAlertTestWidget 데이터 로딩 시작:", dataSources.length, "개 소스");
|
||||
|
||||
try {
|
||||
const results = await Promise.allSettled(
|
||||
dataSources.map(async (source, index) => {
|
||||
console.log(`📡 데이터 소스 ${index + 1} 로딩 중:`, source.name, source.type);
|
||||
// console.log(`📡 데이터 소스 ${index + 1} 로딩 중:`, source.name, source.type);
|
||||
if (source.type === "api") {
|
||||
const alerts = await loadRestApiData(source);
|
||||
console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
// console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
return alerts;
|
||||
} else {
|
||||
const alerts = await loadDatabaseData(source);
|
||||
console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
// console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
return alerts;
|
||||
}
|
||||
})
|
||||
@@ -359,14 +422,14 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
const allAlerts: Alert[] = [];
|
||||
results.forEach((result, index) => {
|
||||
if (result.status === "fulfilled") {
|
||||
console.log(`✅ 결과 ${index + 1} 병합:`, result.value.length, "개 알림");
|
||||
// console.log(`✅ 결과 ${index + 1} 병합:`, result.value.length, "개 알림");
|
||||
allAlerts.push(...result.value);
|
||||
} else {
|
||||
console.error(`❌ 결과 ${index + 1} 실패:`, result.reason);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ 총", allAlerts.length, "개 알림 로딩 완료");
|
||||
// console.log("✅ 총", allAlerts.length, "개 알림 로딩 완료");
|
||||
allAlerts.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
||||
setAlerts(allAlerts);
|
||||
setLastRefreshTime(new Date());
|
||||
@@ -380,7 +443,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
|
||||
// 수동 새로고침 핸들러
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
// console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
loadMultipleDataSources();
|
||||
}, [loadMultipleDataSources]);
|
||||
|
||||
@@ -403,15 +466,15 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
if (intervals.length === 0) return;
|
||||
|
||||
const minInterval = Math.min(...intervals);
|
||||
console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
// console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
console.log("🔄 자동 새로고침 실행");
|
||||
// console.log("🔄 자동 새로고침 실행");
|
||||
loadMultipleDataSources();
|
||||
}, minInterval * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("⏹️ 자동 새로고침 정리");
|
||||
// console.log("⏹️ 자동 새로고침 정리");
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [dataSources, loadMultipleDataSources]);
|
||||
@@ -516,7 +579,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
</div>
|
||||
|
||||
{/* 컨텐츠 */}
|
||||
<div className="flex-1 overflow-hidden p-2">
|
||||
<div className="flex flex-1 flex-col overflow-hidden p-2">
|
||||
<div className="mb-2 flex gap-1 overflow-x-auto">
|
||||
<Button
|
||||
variant={filter === "all" ? "default" : "outline"}
|
||||
@@ -545,7 +608,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-1.5 overflow-y-auto">
|
||||
<div className="flex-1 space-y-1.5 overflow-y-auto pr-1">
|
||||
{filteredAlerts.length === 0 ? (
|
||||
<div className="flex h-full items-center justify-center text-gray-500">
|
||||
<p className="text-sm">알림이 없습니다</p>
|
||||
|
||||
Reference in New Issue
Block a user