"use client"; import * as React from "react"; import { cn } from "@/lib/utils"; import { formatPersonalNumber, formatPersonalNumberMasked, parsePersonalNumber } from "@/lib/formatters"; export interface PersonalNumberInputProps extends Omit, "onChange" | "value" | "type"> { value: string; onChange: (value: string) => void; error?: boolean; maskBack?: boolean; } /** * PersonalNumberInput - 주민번호 자동 포맷팅 입력 컴포넌트 * * 특징: * - 입력 시 자동 하이픈 삽입 (000000-0000000) * - 숫자만 입력 허용 (최대 13자리) * - onChange에는 숫자만 전달 (DB 저장용) * - 뒷자리 마스킹 옵션 (000000-*******) * * @example * */ const PersonalNumberInput = React.forwardRef( ({ className, value, onChange, error, maskBack = false, ...props }, ref) => { // 표시용 포맷된 값 const displayValue = React.useMemo(() => { if (!value) return ""; return maskBack ? formatPersonalNumberMasked(value) : formatPersonalNumber(value); }, [value, maskBack]); const handleChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; // 숫자만 추출하여 전달 const numbersOnly = parsePersonalNumber(inputValue); onChange(numbersOnly); }; const handleKeyDown = (e: React.KeyboardEvent) => { // 숫자, 백스페이스, 탭, 화살표, 복사/붙여넣기 허용 const allowedKeys = [ "Backspace", "Delete", "Tab", "Escape", "Enter", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End" ]; if (allowedKeys.includes(e.key)) return; // Ctrl/Cmd + A, C, V, X 허용 if ((e.ctrlKey || e.metaKey) && ["a", "c", "v", "x"].includes(e.key.toLowerCase())) { return; } // 숫자가 아니면 차단 if (!/^\d$/.test(e.key)) { e.preventDefault(); } }; const handlePaste = (e: React.ClipboardEvent) => { e.preventDefault(); const pastedText = e.clipboardData.getData("text"); const numbersOnly = parsePersonalNumber(pastedText); onChange(numbersOnly); }; return ( ); } ); PersonalNumberInput.displayName = "PersonalNumberInput"; export { PersonalNumberInput };