"use client"; import { useEffect, useMemo, useState } from "react"; import type { DashboardHoldingItem } from "@/features/dashboard/types/dashboard.types"; import { type KisRealtimeStockTick, parseKisRealtimeStockTick, } from "@/features/dashboard/utils/kis-stock-realtime.utils"; import { useKisWebSocketStore } from "@/features/kis-realtime/stores/kisWebSocketStore"; const STOCK_REALTIME_TR_ID = "H0STCNT0"; /** * @description 보유 종목 목록에 대한 실시간 체결 데이터를 구독합니다. * @param holdings 보유 종목 목록 * @returns 종목별 실시간 체결 데이터/연결 상태 * @remarks UI 흐름: DashboardContainer -> useHoldingsRealtime -> HoldingsList/summary 실시간 반영 * @see features/dashboard/components/DashboardContainer.tsx 보유종목 실시간 병합 */ export function useHoldingsRealtime(holdings: DashboardHoldingItem[]) { const [realtimeData, setRealtimeData] = useState< Record >({}); const { subscribe, connect, isConnected } = useKisWebSocketStore(); const uniqueSymbols = useMemo( () => Array.from(new Set((holdings ?? []).map((item) => item.symbol))).sort(), [holdings], ); const symbolKey = useMemo(() => uniqueSymbols.join(","), [uniqueSymbols]); useEffect(() => { if (uniqueSymbols.length === 0) { const resetTimerId = window.setTimeout(() => { setRealtimeData({}); }, 0); return () => window.clearTimeout(resetTimerId); } if (!isConnected) { connect(); } const unsubs: (() => void)[] = []; uniqueSymbols.forEach((symbol) => { const unsub = subscribe(STOCK_REALTIME_TR_ID, symbol, (data) => { const tick = parseKisRealtimeStockTick(data); if (tick) { setRealtimeData((prev) => { const prevTick = prev[tick.symbol]; if ( prevTick?.currentPrice === tick.currentPrice && prevTick?.change === tick.change && prevTick?.changeRate === tick.changeRate ) { return prev; } return { ...prev, [tick.symbol]: tick, }; }); } }); unsubs.push(unsub); }); return () => { unsubs.forEach((unsub) => unsub()); }; }, [symbolKey, uniqueSymbols, connect, subscribe, isConnected]); return { realtimeData, isConnected }; }