전체적인 리팩토링

This commit is contained in:
2026-03-12 09:26:27 +09:00
parent 406af7408a
commit e51d767878
97 changed files with 13651 additions and 363 deletions

View File

@@ -0,0 +1,98 @@
/**
* @file features/layout/hooks/use-market-indices.ts
* @description 시장 지수 데이터를 가져오고 상태를 관리하는 커스텀 훅
*
* @description [주요 책임]
* - `useMarketIndicesStore`와 연동하여 상태(지수, 로딩, 에러)를 제공
* - KIS 검증 세션이 있을 때 `/api/kis/domestic/indices` API를 인증 헤더와 함께 호출
* - API 호출 로직을 `useCallback`으로 메모이제이션하여 성능을 최적화
*/
import { useCallback } from "react";
import {
buildKisRequestHeaders,
resolveKisApiErrorMessage,
type KisApiErrorPayload,
} from "@/features/settings/apis/kis-api-utils";
import { useKisRuntimeStore } from "@/features/settings/store/use-kis-runtime-store";
import { useMarketIndicesStore } from "@/features/layout/stores/market-indices-store";
import type { DomesticMarketIndexResult } from "@/lib/kis/dashboard";
import type { DashboardIndicesResponse } from "@/features/dashboard/types/dashboard.types";
interface LegacyMarketIndicesResponse {
indices: DomesticMarketIndexResult[];
fetchedAt: string;
}
export function useMarketIndices() {
const verifiedCredentials = useKisRuntimeStore((state) => state.verifiedCredentials);
const isKisVerified = useKisRuntimeStore((state) => state.isKisVerified);
const {
indices,
isLoading,
error,
fetchedAt,
setIndices,
setLoading,
setError,
} = useMarketIndicesStore();
const fetchIndices = useCallback(async () => {
// [Step 1] KIS 검증이 안 된 상태에서는 지수 API를 호출하지 않습니다.
if (!isKisVerified || !verifiedCredentials) {
setLoading(false);
setError(null);
return;
}
setLoading(true);
try {
// [Step 2] 인증 헤더를 포함한 신규 지수 API를 호출합니다.
const response = await fetch("/api/kis/domestic/indices", {
method: "GET",
headers: buildKisRequestHeaders(verifiedCredentials, {
includeSessionOverride: true,
}),
cache: "no-store",
});
const payload = (await response.json()) as
| DashboardIndicesResponse
| LegacyMarketIndicesResponse
| KisApiErrorPayload;
if (!response.ok) {
throw new Error(resolveKisApiErrorMessage(payload, "지수 조회 중 오류가 발생했습니다."));
}
// [Step 3] 신규/레거시 응답 형식을 모두 수용해 스토어에 반영합니다.
if ("items" in payload) {
setIndices({
indices: payload.items,
fetchedAt: payload.fetchedAt,
});
return;
}
if ("indices" in payload && "fetchedAt" in payload) {
setIndices({
indices: payload.indices,
fetchedAt: payload.fetchedAt,
});
return;
}
throw new Error("지수 응답 형식이 올바르지 않습니다.");
} catch (e) {
setError(e instanceof Error ? e : new Error("An unknown error occurred"));
}
}, [isKisVerified, setError, setIndices, setLoading, verifiedCredentials]);
return {
indices,
isLoading,
error,
fetchedAt,
fetchIndices,
};
}