실시간 웹소켓 리팩토링
This commit is contained in:
@@ -3,18 +3,23 @@ import type { KisRuntimeCredentials } from "@/features/settings/store/use-kis-ru
|
||||
import type { DashboardStockOrderBookResponse } from "@/features/trade/types/trade.types";
|
||||
import { useKisWebSocketStore } from "@/features/kis-realtime/stores/kisWebSocketStore";
|
||||
import {
|
||||
extractKisRealtimeTrId,
|
||||
hasMeaningfulOrderBookPayload,
|
||||
parseKisRealtimeOrderbook,
|
||||
resolveOrderBookTrIds,
|
||||
shouldAcceptRealtimeMessageByPriority,
|
||||
} from "@/features/trade/utils/kisRealtimeUtils";
|
||||
import type { DomesticKisSession } from "@/lib/kis/domestic-market-session";
|
||||
|
||||
interface UseOrderbookSubscriptionParams {
|
||||
symbol: string | undefined; // orderBookSymbol
|
||||
market: "KOSPI" | "KOSDAQ" | undefined;
|
||||
isVerified: boolean;
|
||||
credentials: KisRuntimeCredentials | null;
|
||||
marketSession: DomesticKisSession;
|
||||
onOrderBookMessage?: (data: DashboardStockOrderBookResponse) => void;
|
||||
}
|
||||
const STABLE_SOURCE_STALE_MS = Number.POSITIVE_INFINITY;
|
||||
|
||||
/**
|
||||
* @description 실시간 호가(Orderbook) 구독 로직을 담당하는 훅입니다.
|
||||
@@ -24,6 +29,7 @@ interface UseOrderbookSubscriptionParams {
|
||||
*/
|
||||
export function useOrderbookSubscription({
|
||||
symbol,
|
||||
market,
|
||||
isVerified,
|
||||
credentials,
|
||||
marketSession,
|
||||
@@ -31,6 +37,8 @@ export function useOrderbookSubscription({
|
||||
}: UseOrderbookSubscriptionParams) {
|
||||
const { subscribe, connect } = useKisWebSocketStore();
|
||||
const onOrderBookMessageRef = useRef(onOrderBookMessage);
|
||||
const activeOrderBookTrIdRef = useRef<string | null>(null);
|
||||
const activeOrderBookTrUpdatedAtRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
onOrderBookMessageRef.current = onOrderBookMessage;
|
||||
@@ -41,12 +49,34 @@ export function useOrderbookSubscription({
|
||||
|
||||
connect();
|
||||
|
||||
const trIds = resolveOrderBookTrIds(credentials.tradingEnv, marketSession);
|
||||
const trIds = resolveOrderBookTrIds(
|
||||
credentials.tradingEnv,
|
||||
marketSession,
|
||||
market,
|
||||
);
|
||||
const unsubscribers: Array<() => void> = [];
|
||||
|
||||
const handleOrderBookMessage = (data: string) => {
|
||||
const incomingTrId = extractKisRealtimeTrId(data);
|
||||
if (!incomingTrId) return;
|
||||
|
||||
// UI 흐름: 소켓 수신 -> TR 우선순위 고정(시장별 상위 소스 우선) -> 파싱 -> 호가 상태 반영
|
||||
const shouldAccept = shouldAcceptRealtimeMessageByPriority({
|
||||
incomingTrId,
|
||||
preferredTrIds: trIds,
|
||||
activeTrId: activeOrderBookTrIdRef.current,
|
||||
activeTrUpdatedAtMs: activeOrderBookTrUpdatedAtRef.current,
|
||||
// 정규장/동시호가에서는 한 번 잡은 상위 소스를 유지해 소스 간 왕복 반영을 막습니다.
|
||||
staleAfterMs: STABLE_SOURCE_STALE_MS,
|
||||
});
|
||||
if (!shouldAccept) return;
|
||||
|
||||
const ob = parseKisRealtimeOrderbook(data, symbol);
|
||||
if (ob) {
|
||||
if (hasMeaningfulOrderBookPayload(ob)) {
|
||||
activeOrderBookTrIdRef.current = incomingTrId;
|
||||
activeOrderBookTrUpdatedAtRef.current = Date.now();
|
||||
}
|
||||
ob.tradingEnv = credentials.tradingEnv;
|
||||
onOrderBookMessageRef.current?.(ob);
|
||||
}
|
||||
@@ -58,6 +88,8 @@ export function useOrderbookSubscription({
|
||||
|
||||
return () => {
|
||||
unsubscribers.forEach((unsub) => unsub());
|
||||
activeOrderBookTrIdRef.current = null;
|
||||
activeOrderBookTrUpdatedAtRef.current = 0;
|
||||
};
|
||||
}, [symbol, isVerified, credentials, marketSession, connect, subscribe]);
|
||||
}, [symbol, market, isVerified, credentials, marketSession, connect, subscribe]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user