refactor: 프론트엔드 TypeScript 타입 안전성 및 커스텀 훅 분리

- types/trading.ts: 중앙 타입 정의 (CandleData, Position, Trade 등)
- useMarketData.ts: 실시간 시세 처리 + 500ms 쓰로틀링
- usePortfolioData.ts: React Query 기반 포트폴리오 데이터 훅
- useTradeActions.ts: 매매 실행 액션 훅
- useWebSocket.ts: pong timeout 10초 감지 (좀비 연결 방지)
- TradingChart.tsx: @ts-nocheck 제거, CandleData 타입 적용
- PortfolioPanel/RecommendationPanel: any → 구체적 타입 교체
- Dashboard.tsx: 상태 관리 로직을 커스텀 훅으로 이전

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 17:26:27 +09:00
parent a477072bdc
commit dc9bc114da
10 changed files with 736 additions and 244 deletions

View File

@@ -0,0 +1,65 @@
import { useEffect, useState, useRef, useCallback } from 'react'
export const useMarketData = (
selectedStock: string,
portfolio: any[] | undefined
) => {
// Live prices with throttling to prevent excessive re-renders
const [livePrices, setLivePrices] = useState<Record<string, number>>({})
const [liveQuote, setLiveQuote] = useState<any>(null)
// Throttle ref: track last update time per stock
const lastPriceUpdateRef = useRef<Record<string, number>>({})
const throttleDelayMs = 500 // Max 2-3 price updates per second (500ms = 2/sec)
// Handle market updates (tick data) with throttling
const handleMarketUpdate = useCallback((data: unknown) => {
const d = data as any
if (d?._type === 'tick' && d.stock_code && d.price != null) {
const now = Date.now()
const lastUpdate =
lastPriceUpdateRef.current[d.stock_code] ?? 0
// Only update if throttle delay has passed
if (now - lastUpdate >= throttleDelayMs) {
setLivePrices((prev) => ({
...prev,
[d.stock_code]: d.price,
}))
lastPriceUpdateRef.current[d.stock_code] = now
}
} else if (
d?._type === 'quote' &&
d.stock_code === selectedStock
) {
setLiveQuote(d)
}
}, [selectedStock])
// Initialize live prices from portfolio
useEffect(() => {
if (!portfolio) return
setLivePrices((prev) => {
const next = { ...prev }
portfolio.forEach((s: any) => {
if (s.current_price && !next[s.stock_code]) {
next[s.stock_code] = Number(s.current_price)
}
})
return next
})
}, [portfolio])
// Clear live quote when selected stock changes
useEffect(() => {
setLiveQuote(null)
}, [selectedStock])
return {
livePrices,
liveQuote,
handleMarketUpdate,
}
}