스킬 정리 및 리팩토링

This commit is contained in:
2026-02-26 09:05:17 +09:00
parent 4c52d6d82f
commit 406af7408a
71 changed files with 3776 additions and 3934 deletions

View File

@@ -1,4 +1,5 @@
import { create } from "zustand";
import { buildKisErrorDetail } from "@/lib/kis/error-codes";
import { useKisRuntimeStore } from "@/features/settings/store/use-kis-runtime-store";
import { buildKisRealtimeMessage } from "@/features/kis-realtime/utils/websocketUtils";
@@ -63,6 +64,21 @@ const RECONNECT_BASE_DELAY_MS = 1_000;
const RECONNECT_MAX_DELAY_MS = 30_000;
const RECONNECT_JITTER_MS = 300;
function isKisWsDebugEnabled() {
if (typeof window === "undefined") return false;
return window.localStorage.getItem("KIS_WS_DEBUG") === "1";
}
function wsDebugLog(...args: unknown[]) {
if (!isKisWsDebugEnabled()) return;
console.log(...args);
}
function wsDebugWarn(...args: unknown[]) {
if (!isKisWsDebugEnabled()) return;
console.warn(...args);
}
export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
isConnected: false,
error: null,
@@ -105,7 +121,7 @@ export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
// 소켓 생성
// socket 변수에 할당하기 전에 로컬 변수로 제어하여 이벤트 핸들러 클로저 문제 방지
const ws = new WebSocket(wsConnection.wsUrl);
console.log("[KisWebSocket] Connecting to:", wsConnection.wsUrl);
wsDebugLog("[KisWebSocket] Connecting to:", wsConnection.wsUrl);
socket = ws;
ws.onopen = () => {
@@ -116,7 +132,7 @@ export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
set({ isConnected: true, error: null });
reconnectAttempt = 0;
console.log("[KisWebSocket] Connected");
wsDebugLog("[KisWebSocket] Connected");
// 재연결 시 기존 구독 복구
const approvalKey = wsConnection.approvalKey;
@@ -147,7 +163,7 @@ export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
if (canAutoReconnect) {
reconnectAttempt += 1;
const delayMs = getReconnectDelayMs(reconnectAttempt);
console.warn(
wsDebugWarn(
`[KisWebSocket] Disconnected (code=${event.code}, reason=${event.reason || "none"}, wasClean=${event.wasClean}) -> reconnect in ${delayMs}ms (attempt ${reconnectAttempt}/${MAX_AUTO_RECONNECT_ATTEMPTS})`,
);
@@ -170,7 +186,7 @@ export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
}
reconnectAttempt = 0;
console.log(
wsDebugLog(
`[KisWebSocket] Disconnected (code=${event.code}, reason=${event.reason || "none"})`,
);
}
@@ -221,15 +237,15 @@ export const useKisWebSocketStore = create<KisWebSocketState>((set, get) => ({
// KIS 서버가 세션을 정리하는 데 최소 10~30초가 필요하므로
// 충분한 대기 후 재연결합니다.
if (control.msgCd === "OPSP8996") {
const now = Date.now();
if (now - lastAppKeyConflictAt > 5_000) {
lastAppKeyConflictAt = now;
console.warn(
"[KisWebSocket] ALREADY IN USE appkey - 현재 소켓을 닫고 30초 후 재연결합니다.",
);
// 현재 소켓을 즉시 닫아 서버 측 세션 정리 유도
if (socket === ws && ws.readyState === WebSocket.OPEN) {
ws.close(1000, "ALREADY IN USE - graceful close");
const now = Date.now();
if (now - lastAppKeyConflictAt > 5_000) {
lastAppKeyConflictAt = now;
wsDebugWarn(
"[KisWebSocket] ALREADY IN USE appkey - 현재 소켓을 닫고 30초 후 재연결합니다.",
);
// 현재 소켓을 즉시 닫아 서버 측 세션 정리 유도
if (socket === ws && ws.readyState === WebSocket.OPEN) {
ws.close(1000, "ALREADY IN USE - graceful close");
}
window.clearTimeout(reconnectRetryTimer);
reconnectRetryTimer = window.setTimeout(() => {
@@ -374,11 +390,11 @@ function sendSubscription(
try {
const msg = buildKisRealtimeMessage(appKey, symbol, trId, trType);
ws.send(JSON.stringify(msg));
console.debug(
wsDebugLog(
`[KisWebSocket] ${trType === "1" ? "Sub" : "Unsub"} ${trId} ${symbol}`,
);
} catch (e) {
console.warn("[KisWebSocket] Send error", e);
wsDebugWarn("[KisWebSocket] Send error", e);
}
}
@@ -440,7 +456,10 @@ function buildControlErrorMessage(message: KisWsControlMessage) {
if (message.msgCd === "OPSP8996") {
return "실시간 연결이 다른 세션과 충돌해 재연결을 시도합니다.";
}
const detail = [message.msg1, message.msgCd].filter(Boolean).join(" / ");
const detail = buildKisErrorDetail({
message: message.msg1,
msgCode: message.msgCd,
});
return detail
? `실시간 제어 메시지 오류: ${detail}`
: "실시간 제어 메시지 오류";