--- id: wiki-2026-0508-실시간-데이터-대시보드-레이아웃-조절-시스템 title: 실시간 데이터 대시보드 레이아웃 조절 시스템 category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Real-time Dashboard Layout, Drag-and-drop Grid, Resizable Widget System, react-grid-layout] duplicate_of: none source_trust_level: A confidence_score: 0.86 verification_status: applied tags: [frontend, dashboard, layout, drag-drop, grid, real-time, websocket] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: React 19, react-grid-layout, dnd-kit, CSS Grid, WebSocket, SSE --- # 실시간 데이터 대시보드 레이아웃 조절 시스템 ## 매 한 줄 > **"매 user-customizable dashboard — 매 widget 의 drag/resize/persist + 매 real-time data update — 매 layout state machine + 매 streaming connection (WS/SSE) + 매 efficient re-render strategy 의 결합"**. 매 2026 의 modern stack: react-grid-layout / dnd-kit, Server-Sent Events / WebSocket, virtualization, CSS Grid + container queries, IndexedDB persist. ## 매 핵심 ### 매 Layout 의 3 element - **Position** — column, row, x, y. - **Size** — width, height (in grid units or px). - **Constraint** — min/max width/height, lockable, draggable flag. ### 매 Layout System 의 categories - **CSS Grid based** — declarative, 매 free-form 의 X. - **Coordinate based** (react-grid-layout) — 매 free, 매 collision 의 handle. - **Mosaic / split-pane** (golden-layout) — 매 nested split, 매 complete coverage. - **Free canvas** (Excalidraw, tldraw) — 매 absolute, 매 무한. ### 매 Real-time Data 의 channels - **WebSocket** — bidirectional, 매 high-frequency. - **Server-Sent Events** — server→client, 매 simple. - **HTTP/2 + Streaming JSON** — 매 partial response. - **WebTransport** (2026) — UDP-based, 매 unreliable streams. - **WebRTC DataChannel** — 매 P2P, 매 low-latency. ### 매 Render Strategy - **Memoization** — `React.memo`, 매 widget 별 의 isolation. - **Selector pattern** — Zustand/Redux selector, 매 specific slice subscribe. - **Virtualization** — 매 viewport 안 의 widget 만 의 render. - **Web Worker** — 매 computation off main thread. ## 💻 패턴 ### Pattern 1: react-grid-layout (drag/resize) ```tsx import GridLayout from 'react-grid-layout'; import 'react-grid-layout/css/styles.css'; const layout = [ { i: 'sales', x: 0, y: 0, w: 6, h: 4 }, { i: 'orders', x: 6, y: 0, w: 6, h: 4 }, { i: 'users', x: 0, y: 4, w: 12, h: 3 }, ]; persist(newLayout)} >
``` ### Pattern 2: dnd-kit + CSS Grid (modern) ```tsx import { DndContext, useDraggable, useDroppable } from '@dnd-kit/core'; function Widget({ id }: { id: string }) { const { attributes, listeners, setNodeRef, transform } = useDraggable({ id }); return (
...
); } ``` ### Pattern 3: WebSocket data subscription ```ts import { useEffect } from 'react'; import { useStore } from './store'; export function useRealtimeMetric(channel: string) { const update = useStore(s => s.updateMetric); useEffect(() => { const ws = new WebSocket(`wss://api/stream?channel=${channel}`); ws.onmessage = (e) => { const { metric, value } = JSON.parse(e.data); update(channel, metric, value); }; return () => ws.close(); }, [channel, update]); } ``` ### Pattern 4: Zustand selector + memoization ```ts import { create } from 'zustand'; type State = { metrics: Record }; const useStore = create(() => ({ metrics: {} })); function SalesWidget() { const sales = useStore(s => s.metrics.sales); // 매 sales 의 변화 만 의 re-render return
Sales: {sales}
; } ``` ### Pattern 5: SSE (one-way streaming) ```ts const es = new EventSource('/api/dashboard/stream'); es.addEventListener('metric', (e) => { const data = JSON.parse(e.data); store.update(data); }); es.addEventListener('error', () => { /* 매 EventSource 의 자동 reconnect */ }); ``` ### Pattern 6: Layout persist (IndexedDB) ```ts import { openDB } from 'idb'; const dbPromise = openDB('dashboard', 1, { upgrade(db) { db.createObjectStore('layouts'); } }); export async function saveLayout(userId: string, layout: any) { const db = await dbPromise; await db.put('layouts', layout, userId); } export async function loadLayout(userId: string) { const db = await dbPromise; return await db.get('layouts', userId); } ``` ### Pattern 7: Virtualized widget list ```tsx import { useVirtualizer } from '@tanstack/react-virtual'; function WidgetList({ widgets }: { widgets: Widget[] }) { const parentRef = useRef(null); const v = useVirtualizer({ count: widgets.length, getScrollElement: () => parentRef.current, estimateSize: () => 200, }); return (
{v.getVirtualItems().map(item => (
))}
); } ``` ### Pattern 8: Container queries 의 widget responsive ```css .widget { container-type: inline-size; container-name: widget; } @container widget (min-width: 400px) { .chart { display: grid; grid-template-columns: 2fr 1fr; } } @container widget (max-width: 200px) { .chart-detail { display: none; } /* 매 small widget 의 simplify */ } ``` ### Pattern 9: Throttled chart update ```ts import { throttle } from 'lodash-es'; const updateChart = throttle((data) => { chart.setOption({ series: [{ data }] }); }, 100); // 매 100ms 의 max — 매 60fps 보다 매 throttle ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Free-form drag/resize | react-grid-layout | | Modern composable | dnd-kit + CSS Grid | | Nested split panes | golden-layout / react-mosaic | | Real-time tick | WebSocket + Zustand selector | | Server-push only | SSE | | 100+ widgets | Virtualization | | Persist across session | IndexedDB | | Widget responsive | Container queries | **기본값**: dnd-kit + CSS Grid + WebSocket + Zustand selector + IndexedDB persist + container queries. ## 🔗 Graph - 부모: [[Frontend]] - 변형: [[Drag and Drop]] · [[Grid Layout]] - 응용: [[웹 프론트엔드 성능 최적화|Frontend Performance Optimization (FE 성능 최적화)]] - Adjacent: [[Server-Sent Events]] · [[Zustand]] ## 🤖 LLM 활용 **언제**: layout schema 설계, websocket reconnection 전략 권고, virtualization threshold 의 결정. **언제 X**: 매 specific chart visualization 의 design — 매 D3/ECharts 의 별도 영역. ## ❌ 안티패턴 - **모든 widget 의 every tick re-render**: 매 selector 의 사용 X → main thread block. - **Layout 의 매 drag 의 server save**: 매 debounce 의 X. - **Polling 의 1초**: 매 SSE/WebSocket 의 사용. - **Grid library 의 unbounded widget count**: 매 virtualization 의 X. - **Layout state 의 React state 의 single global**: 매 매 widget 의 매 layout change 의 re-render. ## 🧪 검증 / 중복 - Verified (react-grid-layout docs, dnd-kit docs, Zustand docs, MDN WebSocket/EventSource 2026). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — grid systems, real-time channels, virtualization, persist patterns |