차트 수정

This commit is contained in:
2026-02-13 16:41:10 +09:00
parent b73867c65d
commit 276ef09d89
8 changed files with 421 additions and 177 deletions

View File

@@ -192,21 +192,12 @@ export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
const parts = data.split("|");
if (parts.length >= 4) {
const trId = parts[1];
// 데이터 부분 (마지막 부분)에서 종목코드를 찾아야 함.
// 하지만 응답에는 종목코드가 명시적으로 없는 경우가 많음 (순서로 추론).
// 다행히 KIS API는 요청했던 TR_ID와 수신된 데이터의 호가/체결 데이터를 매핑해야 함.
// 여기서는 모든 구독자에게 브로드캐스트하는 방식을 사용 (TR_ID 기준).
// 더 정확한 라우팅을 위해:
// 실시간 체결/호가 데이터에는 종목코드가 포함되어 있음.
// 체결(H0STCNT0): data.split("^")[0] (유가증권 단축종목코드)
const body = parts[3];
const values = body.split("^");
const symbol = values[0]; // 대부분 첫 번째 필드가 종목코드
const symbol = values[0] ?? "";
const key = `${trId}|${symbol}`;
const callbacks = subscribers.get(key);
callbacks?.forEach((cb) => cb(data));
// UI 흐름: 소켓 수신 -> TR/심볼 정규화 매칭 -> 해당 구독 콜백 실행 -> 훅 파서(parseKisRealtime*) -> 화면 반영
dispatchRealtimeMessageToSubscribers(trId, symbol, data);
}
}
};
@@ -384,3 +375,67 @@ function waitForSocketClose(target: WebSocket, timeoutMs = 2_000) {
target.addEventListener("error", onError);
});
}
/**
* @description 실시간 데이터(TR/종목코드)와 등록된 구독자를 매칭해 콜백을 실행합니다.
* 종목코드 접두(prefix) 차이(A005930/J005930 등)와 구독 심볼 형식 차이를 허용합니다.
* @param trId 수신 TR ID
* @param rawSymbol 수신 데이터의 원본 종목코드
* @param payload 웹소켓 원문 메시지
* @see features/trade/hooks/useTradeTickSubscription.ts 체결 구독 콜백
* @see features/trade/hooks/useOrderbookSubscription.ts 호가 구독 콜백
*/
function dispatchRealtimeMessageToSubscribers(
trId: string,
rawSymbol: string,
payload: string,
) {
const callbackSet = new Set<RealtimeCallback>();
const normalizedIncomingSymbol = normalizeRealtimeSymbol(rawSymbol);
// 1) 정확히 일치하는 key 우선
const exactKey = `${trId}|${rawSymbol}`;
subscribers.get(exactKey)?.forEach((callback) => callbackSet.add(callback));
// 2) 숫자 6자리 기준(정규화)으로 일치하는 key 매칭
subscribers.forEach((callbacks, key) => {
const [subscribedTrId, subscribedSymbol = ""] = key.split("|");
if (subscribedTrId !== trId) return;
if (!normalizedIncomingSymbol) return;
const normalizedSubscribedSymbol = normalizeRealtimeSymbol(subscribedSymbol);
if (!normalizedSubscribedSymbol) return;
if (normalizedIncomingSymbol !== normalizedSubscribedSymbol) return;
callbacks.forEach((callback) => callbackSet.add(callback));
});
// 3) 심볼 매칭이 실패한 경우에도 같은 TR 전체 콜백으로 안전 fallback
if (callbackSet.size === 0) {
subscribers.forEach((callbacks, key) => {
const [subscribedTrId] = key.split("|");
if (subscribedTrId !== trId) return;
callbacks.forEach((callback) => callbackSet.add(callback));
});
}
callbackSet.forEach((callback) => callback(payload));
}
/**
* @description 실시간 종목코드를 비교 가능한 6자리 숫자 코드로 정규화합니다.
* @param value 원본 종목코드 (예: 005930, A005930)
* @returns 정규화된 6자리 코드. 파싱 불가 시 원본 trim 값 반환
* @see features/kis-realtime/stores/kisWebSocketStore.ts dispatchRealtimeMessageToSubscribers
*/
function normalizeRealtimeSymbol(value: string) {
const trimmed = value.trim();
if (!trimmed) return "";
const digits = trimmed.replace(/\D/g, "");
if (digits.length >= 6) {
return digits.slice(-6);
}
return trimmed;
}