/** * Layout.tsx — 전체 페이지 레이아웃 컴포넌트 * * 역할: * - 상단 네비게이션 바 (브랜드 로고, 메뉴, 연결 상태 표시기) * - 백엔드 및 MCP 서버 연결 상태 실시간 모니터링 (5초 폴링) * - children으로 전달된 페이지 컨텐츠 렌더링 * * 구조: * [Header] — sticky 상단 바 (z-50) * - 브랜드 로고 (AutoStock) * - 네비게이션 링크 (대시보드, 성과분석, 전략설정, 백테스트, 매매로그, MCP상태) * - 연결 상태 표시 (Backend LIVE/OFFLINE, MCP connected/unknown) * [Main] — 페이지 컨텐츠 영역 */ import { Link, useLocation } from 'react-router-dom' import { LayoutDashboard, History, Cpu, Activity, SlidersHorizontal, BarChart3, TrendingUp } from 'lucide-react' import { useQuery } from '@tanstack/react-query' import api from '../lib/api' /** Layout Props 정의 */ interface LayoutProps { children: React.ReactNode } /** * Layout — 전체 페이지 레이아웃 (헤더 + 컨텐츠) * * 쿼리: * - backend-health: 백엔드 연결 상태 확인 (portfolio API 호출, 3초 타임아웃) * - mcp-status: MCP 서버 연결 상태 확인 */ const Layout = ({ children }: LayoutProps) => { const location = useLocation() // 백엔드 연결 상태 확인 (30초 폴링, 3초 타임아웃 — /health 경량 엔드포인트 사용) const { data: healthData } = useQuery({ queryKey: ['backend-health'], queryFn: async () => { try { const r = await fetch('/health', { signal: AbortSignal.timeout(3000), }) return r.ok } catch { return false } }, refetchInterval: 30000, }) const backendConnected = healthData === true const { data: mcpStatus } = useQuery({ queryKey: ['mcp-status'], queryFn: async () => { try { return (await api.get('/mcp/status')).data } catch { return null } }, refetchInterval: 5000, }) /** 네비게이션 메뉴 항목 (경로, 아이콘, 레이블) */ const navItems = [ { path: '/', icon: LayoutDashboard, label: '대시보드' }, { path: '/analysis', icon: TrendingUp, label: '성과 분석' }, { path: '/strategy', icon: SlidersHorizontal, label: '전략 설정' }, { path: '/backtest', icon: BarChart3, label: '백테스트' }, { path: '/trades', icon: History, label: '매매 로그' }, { path: '/mcp', icon: Cpu, label: 'MCP 상태' }, ] return (