import type { DashboardChartTimeframe, DashboardStockChartResponse, } from "@/features/trade/types/trade.types"; import type { KisCredentialInput } from "@/lib/kis/config"; import { hasKisConfig, normalizeTradingEnv } from "@/lib/kis/config"; import { getDomesticChart } from "@/lib/kis/domestic"; import { NextRequest, NextResponse } from "next/server"; import { hasKisApiSession } from "@/app/api/kis/_session"; const VALID_TIMEFRAMES: DashboardChartTimeframe[] = [ "1m", "30m", "1h", "1d", "1w", ]; /** * @file app/api/kis/domestic/chart/route.ts * @description 국내주식 차트(분봉/일봉/주봉) 조회 API */ export async function GET(request: NextRequest) { const hasSession = await hasKisApiSession(); if (!hasSession) { return NextResponse.json({ error: "로그인이 필요합니다." }, { status: 401 }); } const { searchParams } = new URL(request.url); const symbol = (searchParams.get("symbol") ?? "").trim(); const timeframe = ( searchParams.get("timeframe") ?? "1d" ).trim() as DashboardChartTimeframe; const cursor = (searchParams.get("cursor") ?? "").trim() || undefined; if (!/^\d{6}$/.test(symbol)) { return NextResponse.json( { error: "symbol은 6자리 숫자여야 합니다." }, { status: 400 }, ); } if (!VALID_TIMEFRAMES.includes(timeframe)) { return NextResponse.json( { error: "지원하지 않는 timeframe입니다." }, { status: 400 }, ); } const credentials = readKisCredentialsFromHeaders(request.headers); if (!hasKisConfig(credentials)) { return NextResponse.json( { error: "대시보드 상단에서 KIS API 키를 입력하고 검증해 주세요. 키 정보가 없어서 차트를 조회할 수 없습니다.", }, { status: 400 }, ); } try { const chart = await getDomesticChart( symbol, timeframe, credentials, cursor, ); const response: DashboardStockChartResponse = { symbol, timeframe, candles: chart.candles, nextCursor: chart.nextCursor, hasMore: chart.hasMore, fetchedAt: new Date().toISOString(), }; return NextResponse.json(response, { headers: { "cache-control": "no-store", }, }); } catch (error) { const message = error instanceof Error ? error.message : "KIS 차트 조회 중 오류가 발생했습니다."; return NextResponse.json({ error: message }, { status: 500 }); } } function readKisCredentialsFromHeaders(headers: Headers): KisCredentialInput { const appKey = headers.get("x-kis-app-key")?.trim(); const appSecret = headers.get("x-kis-app-secret")?.trim(); const tradingEnv = normalizeTradingEnv( headers.get("x-kis-trading-env") ?? undefined, ); return { appKey, appSecret, tradingEnv, }; }