import { useState, useCallback } from "react"; import { z } from "zod"; import type { KisRuntimeCredentials } from "@/features/settings/store/use-kis-runtime-store"; import type { DashboardStockCashOrderRequest, DashboardStockCashOrderResponse, } from "@/features/trade/types/trade.types"; import { fetchOrderCash } from "@/features/trade/apis/kis-stock.api"; import { parseKisAccountParts } from "@/lib/kis/account"; const placeOrderRequestSchema = z .object({ symbol: z.string().trim().regex(/^\d{6}$/), side: z.enum(["buy", "sell"]), orderType: z.enum(["limit", "market"]), quantity: z.number().int().positive(), price: z.number(), accountNo: z.string().trim().min(1), accountProductCode: z.string().trim().optional(), }) .superRefine((request, ctx) => { if (request.orderType === "limit" && request.price <= 0) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["price"], message: "지정가 주문은 가격이 0보다 커야 합니다.", }); } if (request.orderType === "market" && request.price < 0) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["price"], message: "시장가 주문은 가격이 0 이상이어야 합니다.", }); } if (!parseKisAccountParts(request.accountNo, request.accountProductCode)) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["accountNo"], message: "계좌번호 형식이 올바르지 않습니다. (8-2)", }); } }); export function useOrder() { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [result, setResult] = useState( null, ); const placeOrder = useCallback( async ( request: DashboardStockCashOrderRequest, credentials: KisRuntimeCredentials | null, ) => { if (!credentials) { setError("KIS API 자격 증명이 없습니다."); return null; } setIsLoading(true); setError(null); setResult(null); try { const validationResult = placeOrderRequestSchema.safeParse(request); if (!validationResult.success) { setError( validationResult.error.issues[0]?.message ?? "주문 요청 값이 올바르지 않습니다.", ); return null; } const data = await fetchOrderCash(request, credentials); setResult(data); return data; } catch (err) { const message = err instanceof Error ? err.message : "주문 처리 중 오류가 발생했습니다."; setError(message); return null; } finally { setIsLoading(false); } }, [], ); const reset = useCallback(() => { setError(null); setResult(null); setIsLoading(false); }, []); return { placeOrder, isLoading, error, result, reset, }; }