스킬 정리 및 리팩토링
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import type { DashboardKisProfileValidateResponse } from "@/features/trade/types/trade.types";
|
||||
import { hasKisApiSession } from "@/app/api/kis/_session";
|
||||
import { parseKisAccountParts } from "@/lib/kis/account";
|
||||
@@ -6,13 +7,18 @@ import { kisGet } from "@/lib/kis/client";
|
||||
import { normalizeTradingEnv, type KisCredentialInput } from "@/lib/kis/config";
|
||||
import { validateKisCredentialInput } from "@/lib/kis/request";
|
||||
import { getKisAccessToken } from "@/lib/kis/token";
|
||||
import {
|
||||
createKisApiErrorResponse,
|
||||
KIS_API_ERROR_CODE,
|
||||
toKisApiErrorMessage,
|
||||
} from "@/app/api/kis/_response";
|
||||
|
||||
interface KisProfileValidateRequestBody {
|
||||
appKey?: string;
|
||||
appSecret?: string;
|
||||
tradingEnv?: string;
|
||||
accountNo?: string;
|
||||
}
|
||||
const kisProfileValidateBodySchema = z.object({
|
||||
appKey: z.string().trim().min(1, "앱 키를 입력해 주세요."),
|
||||
appSecret: z.string().trim().min(1, "앱 시크릿을 입력해 주세요."),
|
||||
tradingEnv: z.string().optional(),
|
||||
accountNo: z.string().trim().min(1, "계좌번호를 입력해 주세요."),
|
||||
});
|
||||
|
||||
interface BalanceValidationPreset {
|
||||
inqrDvsn: "01" | "02";
|
||||
@@ -50,34 +56,44 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const hasSession = await hasKisApiSession();
|
||||
if (!hasSession) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
tradingEnv: fallbackTradingEnv,
|
||||
message: "로그인이 필요합니다.",
|
||||
} satisfies Pick<DashboardKisProfileValidateResponse, "ok" | "tradingEnv" | "message">,
|
||||
{ status: 401 },
|
||||
);
|
||||
return createKisApiErrorResponse({
|
||||
status: 401,
|
||||
code: KIS_API_ERROR_CODE.AUTH_REQUIRED,
|
||||
message: "로그인이 필요합니다.",
|
||||
tradingEnv: fallbackTradingEnv,
|
||||
});
|
||||
}
|
||||
|
||||
let body: KisProfileValidateRequestBody = {};
|
||||
let rawBody: unknown = {};
|
||||
|
||||
try {
|
||||
body = (await request.json()) as KisProfileValidateRequestBody;
|
||||
rawBody = (await request.json()) as unknown;
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
tradingEnv: fallbackTradingEnv,
|
||||
message: "요청 본문(JSON)을 읽을 수 없습니다.",
|
||||
} satisfies Pick<DashboardKisProfileValidateResponse, "ok" | "tradingEnv" | "message">,
|
||||
{ status: 400 },
|
||||
);
|
||||
return createKisApiErrorResponse({
|
||||
status: 400,
|
||||
code: KIS_API_ERROR_CODE.INVALID_REQUEST,
|
||||
message: "요청 본문(JSON)을 읽을 수 없습니다.",
|
||||
tradingEnv: fallbackTradingEnv,
|
||||
});
|
||||
}
|
||||
|
||||
const parsedBody = kisProfileValidateBodySchema.safeParse(rawBody);
|
||||
if (!parsedBody.success) {
|
||||
return createKisApiErrorResponse({
|
||||
status: 400,
|
||||
code: KIS_API_ERROR_CODE.INVALID_REQUEST,
|
||||
message:
|
||||
parsedBody.error.issues[0]?.message ??
|
||||
"요청 본문 값이 올바르지 않습니다.",
|
||||
tradingEnv: fallbackTradingEnv,
|
||||
});
|
||||
}
|
||||
|
||||
const body = parsedBody.data;
|
||||
|
||||
const credentials: KisCredentialInput = {
|
||||
appKey: body.appKey?.trim(),
|
||||
appSecret: body.appSecret?.trim(),
|
||||
appKey: body.appKey.trim(),
|
||||
appSecret: body.appSecret.trim(),
|
||||
tradingEnv: normalizeTradingEnv(body.tradingEnv),
|
||||
};
|
||||
|
||||
@@ -85,39 +101,25 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const invalidCredentialMessage = validateKisCredentialInput(credentials);
|
||||
if (invalidCredentialMessage) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
tradingEnv,
|
||||
message: invalidCredentialMessage,
|
||||
} satisfies Pick<DashboardKisProfileValidateResponse, "ok" | "tradingEnv" | "message">,
|
||||
{ status: 400 },
|
||||
);
|
||||
return createKisApiErrorResponse({
|
||||
status: 400,
|
||||
code: KIS_API_ERROR_CODE.INVALID_REQUEST,
|
||||
message: invalidCredentialMessage,
|
||||
tradingEnv,
|
||||
});
|
||||
}
|
||||
|
||||
const accountNoInput = (body.accountNo ?? "").trim();
|
||||
|
||||
if (!accountNoInput) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
tradingEnv,
|
||||
message: "계좌번호를 입력해 주세요.",
|
||||
} satisfies Pick<DashboardKisProfileValidateResponse, "ok" | "tradingEnv" | "message">,
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
const accountNoInput = body.accountNo.trim();
|
||||
|
||||
const accountParts = parseKisAccountParts(accountNoInput);
|
||||
if (!accountParts) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
tradingEnv,
|
||||
message: "계좌번호 형식이 올바르지 않습니다. 8-2 형식(예: 12345678-01)으로 입력해 주세요.",
|
||||
} satisfies Pick<DashboardKisProfileValidateResponse, "ok" | "tradingEnv" | "message">,
|
||||
{ status: 400 },
|
||||
);
|
||||
return createKisApiErrorResponse({
|
||||
status: 400,
|
||||
code: KIS_API_ERROR_CODE.ACCOUNT_REQUIRED,
|
||||
message:
|
||||
"계좌번호 형식이 올바르지 않습니다. 8-2 형식(예: 12345678-01)으로 입력해 주세요.",
|
||||
tradingEnv,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -150,19 +152,12 @@ export async function POST(request: NextRequest) {
|
||||
},
|
||||
} satisfies DashboardKisProfileValidateResponse);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "계좌 검증 중 오류가 발생했습니다.";
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
ok: false,
|
||||
tradingEnv,
|
||||
message,
|
||||
} satisfies Pick<DashboardKisProfileValidateResponse, "ok" | "tradingEnv" | "message">,
|
||||
{ status: 400 },
|
||||
);
|
||||
return createKisApiErrorResponse({
|
||||
status: 400,
|
||||
code: KIS_API_ERROR_CODE.UNAUTHORIZED,
|
||||
message: toKisApiErrorMessage(error, "계좌 검증 중 오류가 발생했습니다."),
|
||||
tradingEnv,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user