import type { DashboardChartTimeframe, DashboardStockChartResponse, } from "@/features/trade/types/trade.types"; import { hasKisConfig } from "@/lib/kis/config"; import { getDomesticChart } from "@/lib/kis/domestic"; import { NextRequest, NextResponse } from "next/server"; import { hasKisApiSession } from "@/app/api/kis/_session"; import { createKisApiErrorResponse, KIS_API_ERROR_CODE, toKisApiErrorMessage, } from "@/app/api/kis/_response"; import { readKisCredentialsFromHeaders } from "@/app/api/kis/domestic/_shared"; const VALID_TIMEFRAMES: DashboardChartTimeframe[] = [ "1m", "5m", "10m", "15m", "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 createKisApiErrorResponse({ status: 401, code: KIS_API_ERROR_CODE.AUTH_REQUIRED, message: "로그인이 필요합니다.", }); } 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 createKisApiErrorResponse({ status: 400, code: KIS_API_ERROR_CODE.INVALID_REQUEST, message: "symbol은 6자리 숫자여야 합니다.", }); } if (!VALID_TIMEFRAMES.includes(timeframe)) { return createKisApiErrorResponse({ status: 400, code: KIS_API_ERROR_CODE.INVALID_REQUEST, message: "지원하지 않는 timeframe입니다.", }); } const credentials = readKisCredentialsFromHeaders(request.headers); if (!hasKisConfig(credentials)) { return createKisApiErrorResponse({ status: 400, code: KIS_API_ERROR_CODE.CREDENTIAL_REQUIRED, message: "대시보드 상단에서 KIS API 키를 입력하고 검증해 주세요. 키 정보가 없어서 차트를 조회할 수 없습니다.", }); } 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) { return createKisApiErrorResponse({ status: 500, code: KIS_API_ERROR_CODE.UPSTREAM_FAILURE, message: toKisApiErrorMessage(error, "KIS 차트 조회 중 오류가 발생했습니다."), }); } }