'use client'; import React, { useState, useCallback } from 'react'; import { ChartDataSource, QueryResult } from './types'; interface QueryEditorProps { dataSource?: ChartDataSource; onDataSourceChange: (dataSource: ChartDataSource) => void; onQueryTest?: (result: QueryResult) => void; } /** * SQL 쿼리 에디터 컴포넌트 * - SQL 쿼리 작성 및 편집 * - 쿼리 실행 및 결과 미리보기 * - 데이터 소스 설정 */ export function QueryEditor({ dataSource, onDataSourceChange, onQueryTest }: QueryEditorProps) { const [query, setQuery] = useState(dataSource?.query || ''); const [isExecuting, setIsExecuting] = useState(false); const [queryResult, setQueryResult] = useState(null); const [error, setError] = useState(null); // 쿼리 실행 const executeQuery = useCallback(async () => { if (!query.trim()) { setError('쿼리를 입력해주세요.'); 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() }) }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || '쿼리 실행에 실패했습니다.'); } const apiResult = await response.json(); if (!apiResult.success) { throw new Error(apiResult.message || '쿼리 실행에 실패했습니다.'); } // API 결과를 QueryResult 형식으로 변환 const result: QueryResult = { columns: apiResult.data.columns, rows: apiResult.data.rows, totalRows: apiResult.data.rowCount, executionTime: 0 // API에서 실행 시간을 제공하지 않으므로 0으로 설정 }; setQueryResult(result); onQueryTest?.(result); // 데이터 소스 업데이트 onDataSourceChange({ type: 'database', query: query.trim(), refreshInterval: dataSource?.refreshInterval || 30000, lastExecuted: new Date().toISOString() }); } 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]); // 샘플 쿼리 삽입 const insertSampleQuery = useCallback((sampleType: string) => { const samples = { comparison: `-- 제품별 월별 매출 비교 (다중 시리즈) -- 갤럭시(Galaxy) vs 아이폰(iPhone) 매출 비교 SELECT DATE_TRUNC('month', order_date) as month, SUM(CASE WHEN product_category = '갤럭시' THEN amount ELSE 0 END) as galaxy_sales, SUM(CASE WHEN product_category = '아이폰' THEN amount ELSE 0 END) as iphone_sales, SUM(CASE WHEN product_category = '기타' THEN amount ELSE 0 END) as other_sales FROM orders WHERE order_date >= CURRENT_DATE - INTERVAL '12 months' GROUP BY DATE_TRUNC('month', order_date) ORDER BY month;`, sales: `-- 월별 매출 데이터 SELECT DATE_TRUNC('month', order_date) as month, SUM(total_amount) as sales, COUNT(*) as order_count FROM orders WHERE order_date >= CURRENT_DATE - INTERVAL '12 months' GROUP BY DATE_TRUNC('month', order_date) ORDER BY month;`, users: `-- 사용자 가입 추이 SELECT DATE_TRUNC('week', created_at) as week, COUNT(*) as new_users FROM users WHERE created_at >= CURRENT_DATE - INTERVAL '3 months' GROUP BY DATE_TRUNC('week', created_at) ORDER BY week;`, products: `-- 상품별 판매량 SELECT product_name, SUM(quantity) as total_sold, SUM(quantity * price) as revenue FROM order_items oi JOIN products p ON oi.product_id = p.id WHERE oi.created_at >= CURRENT_DATE - INTERVAL '1 month' GROUP BY product_name ORDER BY total_sold DESC LIMIT 10;`, regional: `-- 지역별 매출 비교 SELECT region as 지역, SUM(CASE WHEN quarter = 'Q1' THEN sales ELSE 0 END) as Q1, SUM(CASE WHEN quarter = 'Q2' THEN sales ELSE 0 END) as Q2, SUM(CASE WHEN quarter = 'Q3' THEN sales ELSE 0 END) as Q3, SUM(CASE WHEN quarter = 'Q4' THEN sales ELSE 0 END) as Q4 FROM regional_sales WHERE year = EXTRACT(YEAR FROM CURRENT_DATE) GROUP BY region ORDER BY Q4 DESC;` }; setQuery(samples[sampleType as keyof typeof samples] || ''); }, []); return (
{/* 쿼리 에디터 헤더 */}

📝 SQL 쿼리 에디터

{/* 샘플 쿼리 버튼들 */}
샘플 쿼리:
{/* SQL 쿼리 입력 영역 */}