useTrades.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. 'use client';
  2. import { useEffect, useState, useCallback, useRef } from 'react';
  3. import { useSignalRContext } from '@/contexts/signalrProvider';
  4. import { fetchApi } from '@/lib/utils/client';
  5. import type { TradeData, TradeRestData } from '@/types/crypto';
  6. const MAX_TRADES = 100;
  7. export default function useTrades(market: string) {
  8. const { cryptoConnection, cryptoConnected } = useSignalRContext();
  9. const [trades, setTrades] = useState<TradeData[]>([]);
  10. const tradesRef = useRef<TradeData[]>([]);
  11. const updateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  12. // REST 초기 로드
  13. useEffect(() => {
  14. if (!market) {
  15. return;
  16. }
  17. setTrades([]);
  18. tradesRef.current = [];
  19. const load = async () => {
  20. try {
  21. const res = await fetchApi<TradeRestData[]>(`/api/crypto/${market}/trades?count=50`);
  22. if (res.success && res.data) {
  23. const mapped: TradeData[] = res.data.map((t) => ({
  24. market,
  25. symbol: market.split('-')[1] || '',
  26. tradePrice: t.tradePrice,
  27. tradeVolume: t.tradeVolume,
  28. askBid: t.askBid,
  29. prevClosingPrice: t.prevClosingPrice,
  30. change: '',
  31. changePrice: t.changePrice,
  32. tradeDate: '',
  33. tradeTime: '',
  34. tradeTimestamp: t.timestamp,
  35. sequentialId: t.sequentialId,
  36. timestamp: t.timestamp,
  37. streamType: 'SNAPSHOT',
  38. bestAskPrice: 0,
  39. bestAskSize: 0,
  40. bestBidPrice: 0,
  41. bestBidSize: 0,
  42. }));
  43. tradesRef.current = mapped;
  44. setTrades(mapped);
  45. }
  46. } catch (error) {
  47. console.error('Failed to load trades:', error);
  48. }
  49. };
  50. load();
  51. }, [market]);
  52. // 배치 업데이트
  53. const scheduleUpdate = useCallback(() => {
  54. if (updateTimerRef.current) {
  55. return;
  56. }
  57. updateTimerRef.current = setTimeout(() => {
  58. setTrades([...tradesRef.current]);
  59. updateTimerRef.current = null;
  60. }, 200);
  61. }, []);
  62. // SignalR 실시간 업데이트
  63. const handleTrade = useCallback((data: TradeData) => {
  64. if (data.market === market) {
  65. tradesRef.current = [data, ...tradesRef.current].slice(0, MAX_TRADES);
  66. scheduleUpdate();
  67. }
  68. }, [market, scheduleUpdate]);
  69. useEffect(() => {
  70. if (!cryptoConnection || !cryptoConnected) {
  71. return;
  72. }
  73. cryptoConnection.on('ReceiveTrade', handleTrade);
  74. return () => {
  75. cryptoConnection.off('ReceiveTrade', handleTrade);
  76. if (updateTimerRef.current) {
  77. clearTimeout(updateTimerRef.current);
  78. updateTimerRef.current = null;
  79. }
  80. };
  81. }, [cryptoConnection, cryptoConnected, handleTrade]);
  82. return trades;
  83. }