대시보드
This commit is contained in:
89
lib/kis/client.ts
Normal file
89
lib/kis/client.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import type { KisCredentialInput } from "@/lib/kis/config";
|
||||
import { getKisConfig } from "@/lib/kis/config";
|
||||
import { getKisAccessToken } from "@/lib/kis/token";
|
||||
|
||||
/**
|
||||
* @file lib/kis/client.ts
|
||||
* @description KIS REST 공통 클라이언트(실전/모의 공통)
|
||||
*/
|
||||
|
||||
export interface KisApiEnvelope<TOutput> {
|
||||
rt_cd?: string;
|
||||
msg_cd?: string;
|
||||
msg1?: string;
|
||||
output?: TOutput;
|
||||
output1?: unknown;
|
||||
output2?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* KIS GET 호출
|
||||
* @param apiPath REST 경로
|
||||
* @param trId KIS TR ID
|
||||
* @param params 쿼리 파라미터
|
||||
* @param credentials 사용자 입력 키(선택)
|
||||
* @returns KIS 원본 응답
|
||||
* @see lib/kis/domestic.ts getDomesticQuote/getDomesticDailyPrice - 대시보드 시세 데이터 소스
|
||||
*/
|
||||
export async function kisGet<TOutput>(
|
||||
apiPath: string,
|
||||
trId: string,
|
||||
params: Record<string, string>,
|
||||
credentials?: KisCredentialInput,
|
||||
): Promise<KisApiEnvelope<TOutput>> {
|
||||
const config = getKisConfig(credentials);
|
||||
const token = await getKisAccessToken(credentials);
|
||||
|
||||
const url = new URL(apiPath, config.baseUrl);
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== "") url.searchParams.set(key, value);
|
||||
});
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
authorization: `Bearer ${token}`,
|
||||
appkey: config.appKey,
|
||||
appsecret: config.appSecret,
|
||||
tr_id: trId,
|
||||
tr_cont: "",
|
||||
custtype: "P",
|
||||
},
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
const rawText = await response.text();
|
||||
const payload = tryParseKisEnvelope<TOutput>(rawText);
|
||||
|
||||
if (!response.ok) {
|
||||
const detail = payload.msg1 || rawText.slice(0, 200);
|
||||
throw new Error(detail ? `KIS API 요청 실패 (${response.status}): ${detail}` : `KIS API 요청 실패 (${response.status})`);
|
||||
}
|
||||
|
||||
if (payload.rt_cd && payload.rt_cd !== "0") {
|
||||
const detail = [payload.msg1, payload.msg_cd].filter(Boolean).join(" / ");
|
||||
throw new Error(detail || "KIS API 비즈니스 오류가 발생했습니다.");
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* KIS 응답을 안전하게 JSON으로 파싱합니다.
|
||||
* @param rawText fetch 응답 원문
|
||||
* @returns KisApiEnvelope
|
||||
* @see lib/kis/token.ts tryParseTokenResponse - 토큰 발급 응답 파싱 방식과 동일 패턴
|
||||
*/
|
||||
function tryParseKisEnvelope<TOutput>(rawText: string): KisApiEnvelope<TOutput> {
|
||||
try {
|
||||
return JSON.parse(rawText) as KisApiEnvelope<TOutput>;
|
||||
} catch {
|
||||
return {
|
||||
msg1: rawText.slice(0, 200),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 하위 호환(alias)
|
||||
export const kisMockGet = kisGet;
|
||||
Reference in New Issue
Block a user