차트 구현 phase2 완료

This commit is contained in:
dohyeons
2025-10-14 14:10:49 +09:00
parent e667ee7106
commit 3db7feb36b
3 changed files with 118 additions and 73 deletions

View File

@@ -2,6 +2,8 @@
import React, { useState, useCallback } from "react";
import { ChartDataSource, QueryResult } from "./types";
import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection";
import { dashboardApi } from "@/lib/api/dashboard";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
@@ -22,7 +24,7 @@ interface QueryEditorProps {
* SQL 쿼리 에디터 컴포넌트
* - SQL 쿼리 작성 및 편집
* - 쿼리 실행 및 결과 미리보기
* - 데이터 소스 설정
* - 현재 DB / 외부 DB 분기 처리
*/
export function QueryEditor({ dataSource, onDataSourceChange, onQueryTest }: QueryEditorProps) {
const [query, setQuery] = useState(dataSource?.query || "");
@@ -37,37 +39,47 @@ export function QueryEditor({ dataSource, onDataSourceChange, onQueryTest }: Que
return;
}
// 외부 DB인 경우 커넥션 ID 확인
if (dataSource?.connectionType === "external" && !dataSource?.externalConnectionId) {
setError("외부 DB 커넥션을 선택해주세요.");
return;
}
setIsExecuting(true);
setError(null);
try {
// 실제 API 호출
const response = await fetch("http://localhost:8080/api/dashboards/execute-query", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token") || "test-token"}`, // JWT 토큰 사용
},
body: JSON.stringify({ query: query.trim() }),
});
let apiResult: { columns: string[]; rows: any[]; rowCount: number };
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || "쿼리 실행에 실패했습니다.");
// 현재 DB vs 외부 DB 분기
if (dataSource?.connectionType === "external" && dataSource?.externalConnectionId) {
// 외부 DB 쿼리 실행
const result = await ExternalDbConnectionAPI.executeQuery(
parseInt(dataSource.externalConnectionId),
query.trim(),
);
if (!result.success) {
throw new Error(result.message || "외부 DB 쿼리 실행에 실패했습니다.");
}
// ExternalDbConnectionAPI의 응답을 통일된 형식으로 변환
apiResult = {
columns: result.data?.[0] ? Object.keys(result.data[0]) : [],
rows: result.data || [],
rowCount: result.data?.length || 0,
};
} else {
// 현재 DB 쿼리 실행
apiResult = await dashboardApi.executeQuery(query.trim());
}
const apiResult = await response.json();
if (!apiResult.success) {
throw new Error(apiResult.message || "쿼리 실행에 실패했습니다.");
}
// API 결과를 QueryResult 형식으로 변환
// 결과를 QueryResult 형식으로 변환
const result: QueryResult = {
columns: apiResult.data.columns,
rows: apiResult.data.rows,
totalRows: apiResult.data.rowCount,
executionTime: 0, // API에서 실행 시간을 제공하지 않으므로 0으로 설정
columns: apiResult.columns,
rows: apiResult.rows,
totalRows: apiResult.rowCount,
executionTime: 0,
};
setQueryResult(result);
@@ -75,6 +87,7 @@ export function QueryEditor({ dataSource, onDataSourceChange, onQueryTest }: Que
// 데이터 소스 업데이트
onDataSourceChange({
...dataSource,
type: "database",
query: query.trim(),
refreshInterval: dataSource?.refreshInterval || 30000,
@@ -83,11 +96,10 @@ export function QueryEditor({ dataSource, onDataSourceChange, onQueryTest }: Que
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "쿼리 실행 중 오류가 발생했습니다.";
setError(errorMessage);
// console.error('Query execution error:', err);
} finally {
setIsExecuting(false);
}
}, [query, dataSource?.refreshInterval, onDataSourceChange, onQueryTest]);
}, [query, dataSource, onDataSourceChange, onQueryTest]);
// 샘플 쿼리 삽입
const insertSampleQuery = useCallback((sampleType: string) => {