"use client"; import { useState, useTransition } from "react"; import { useShallow } from "zustand/react/shallow"; import { CreditCard, CheckCircle2, SearchCheck, ShieldOff, XCircle, FileLock2, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { InlineSpinner } from "@/components/ui/loading-spinner"; import { validateKisProfile } from "@/features/settings/apis/kis-auth.api"; import { useKisRuntimeStore } from "@/features/settings/store/use-kis-runtime-store"; import { SettingsCard } from "./SettingsCard"; /** * @description 한국투자증권 계좌번호 검증 폼입니다. * @remarks UI 흐름: /settings -> 계좌번호 입력 -> 계좌 확인 버튼 -> validate-profile API -> store 반영 -> 대시보드 반영 * @see app/api/kis/validate-profile/route.ts 계좌번호 검증 서버 라우트 * @see features/settings/store/use-kis-runtime-store.ts 검증 성공값을 전역 상태에 저장합니다. */ export function KisProfileForm() { const { kisAccountNoInput, verifiedCredentials, isKisVerified, isKisProfileVerified, verifiedAccountNo, setKisAccountNoInput, setVerifiedKisProfile, invalidateKisProfileVerification, } = useKisRuntimeStore( useShallow((state) => ({ kisAccountNoInput: state.kisAccountNoInput, verifiedCredentials: state.verifiedCredentials, isKisVerified: state.isKisVerified, isKisProfileVerified: state.isKisProfileVerified, verifiedAccountNo: state.verifiedAccountNo, setKisAccountNoInput: state.setKisAccountNoInput, setVerifiedKisProfile: state.setVerifiedKisProfile, invalidateKisProfileVerification: state.invalidateKisProfileVerification, })), ); const [statusMessage, setStatusMessage] = useState(null); const [errorMessage, setErrorMessage] = useState(null); const [isValidating, startValidateTransition] = useTransition(); /** * @description 계좌번호 인증을 해제하고 입력값을 비웁니다. * @see features/settings/store/use-kis-runtime-store.ts setKisAccountNoInput * @see features/settings/store/use-kis-runtime-store.ts invalidateKisProfileVerification */ function handleDisconnectAccount() { setStatusMessage("계좌 인증을 해제했습니다."); setErrorMessage(null); setKisAccountNoInput(""); invalidateKisProfileVerification(); } function handleValidateProfile() { startValidateTransition(async () => { try { setStatusMessage(null); setErrorMessage(null); if (!verifiedCredentials || !isKisVerified) { throw new Error("먼저 앱키 연결을 완료해 주세요."); } const accountNo = kisAccountNoInput.trim(); if (!accountNo) { throw new Error("계좌번호를 입력해 주세요."); } if (!isValidAccountNo(accountNo)) { throw new Error( "계좌번호 형식이 올바르지 않습니다. 8-2 형식(예: 12345678-01)으로 입력해 주세요.", ); } const result = await validateKisProfile({ ...verifiedCredentials, accountNo, }); setVerifiedKisProfile({ accountNo: result.account.normalizedAccountNo, }); setStatusMessage(result.message); } catch (error) { invalidateKisProfileVerification(); setErrorMessage( error instanceof Error ? error.message : "계좌 확인 중 오류가 발생했습니다.", ); } }); } return ( 인증 완료 ) : undefined } className={ !isKisVerified ? "opacity-60 grayscale pointer-events-none" : undefined } footer={{ actions: (
), status: (
{errorMessage && (

{errorMessage}

)} {statusMessage && (

{statusMessage}

)} {!statusMessage && !errorMessage && !isKisVerified && (

먼저 앱키 연결을 완료해 주세요

)} {!statusMessage && !errorMessage && isKisProfileVerified && (

확인된 계좌: {maskAccountNo(verifiedAccountNo)}

)}
), }} >
{/* ========== ACCOUNT GUIDE ========== */}

계좌번호 형식 안내

8-2 형식으로 입력하세요. 예: 12345678-01

{/* ========== ACCOUNT NO INPUT ========== */}
setKisAccountNoInput(e.target.value)} placeholder="계좌번호 (예: 12345678-01)" className="h-10 border-none bg-transparent px-3 text-sm shadow-none focus-visible:ring-0" autoComplete="off" />
); } /** * @description KIS 계좌번호(8-2) 입력 포맷을 검증합니다. * @param value 사용자 입력 계좌번호 * @returns 형식 유효 여부 * @see features/settings/components/KisProfileForm.tsx handleValidateProfile */ function isValidAccountNo(value: string) { const digits = value.replace(/\D/g, ""); return digits.length === 10; } /** * @description 표시용 계좌번호를 마스킹 처리합니다. * @param value 계좌번호(8-2) * @returns 마스킹 계좌번호 * @see features/settings/components/KisProfileForm.tsx 확인된 값 표시 */ function maskAccountNo(value: string | null) { if (!value) return "-"; const digits = value.replace(/\D/g, ""); if (digits.length !== 10) return "********"; return "********-**"; }