/** * formatters.ts - 입력값 포맷팅 유틸리티 함수 * * 전화번호, 사업자번호, 주민번호, 숫자 포맷팅 함수 모음 */ /** * 전화번호 포맷팅 * - 휴대폰 (11자리): 010-1234-5678 * - 서울 (9~10자리): 02-123-4567 / 02-1234-5678 * - 지역 (10~11자리): 031-123-4567 / 031-1234-5678 */ export function formatPhoneNumber(value: string): string { const numbers = value.replace(/\D/g, ''); if (numbers.length === 0) return ''; // 서울 (02로 시작) if (numbers.startsWith('02')) { if (numbers.length <= 2) return numbers; if (numbers.length <= 5) return `${numbers.slice(0, 2)}-${numbers.slice(2)}`; if (numbers.length <= 9) return `${numbers.slice(0, 2)}-${numbers.slice(2, 5)}-${numbers.slice(5)}`; return `${numbers.slice(0, 2)}-${numbers.slice(2, 6)}-${numbers.slice(6, 10)}`; } // 휴대폰 및 지역번호 (3자리 지역번호) if (numbers.length <= 3) return numbers; if (numbers.length <= 7) return `${numbers.slice(0, 3)}-${numbers.slice(3)}`; if (numbers.length <= 10) return `${numbers.slice(0, 3)}-${numbers.slice(3, 6)}-${numbers.slice(6)}`; return `${numbers.slice(0, 3)}-${numbers.slice(3, 7)}-${numbers.slice(7, 11)}`; } /** * 전화번호에서 숫자만 추출 */ export function parsePhoneNumber(formatted: string): string { return formatted.replace(/\D/g, ''); } /** * 사업자번호 포맷팅 (000-00-00000) */ export function formatBusinessNumber(value: string): string { const numbers = value.replace(/\D/g, '').slice(0, 10); if (numbers.length === 0) return ''; if (numbers.length <= 3) return numbers; if (numbers.length <= 5) return `${numbers.slice(0, 3)}-${numbers.slice(3)}`; return `${numbers.slice(0, 3)}-${numbers.slice(3, 5)}-${numbers.slice(5)}`; } /** * 사업자번호에서 숫자만 추출 */ export function parseBusinessNumber(formatted: string): string { return formatted.replace(/\D/g, '').slice(0, 10); } /** * 사업자번호 유효성 검사 (체크섬 검증) * @see https://www.law.go.kr/법령/법인세법시행령/(20230101,33262,20221231)/제43조 */ export function validateBusinessNumber(value: string): boolean { const numbers = value.replace(/\D/g, ''); if (numbers.length !== 10) return false; const weights = [1, 3, 7, 1, 3, 7, 1, 3, 5]; let sum = 0; for (let i = 0; i < 9; i++) { sum += parseInt(numbers[i]) * weights[i]; } sum += Math.floor((parseInt(numbers[8]) * 5) / 10); const checkDigit = (10 - (sum % 10)) % 10; return checkDigit === parseInt(numbers[9]); } /** * 주민번호 포맷팅 (000000-0000000) */ export function formatPersonalNumber(value: string): string { const numbers = value.replace(/\D/g, '').slice(0, 13); if (numbers.length === 0) return ''; if (numbers.length <= 6) return numbers; return `${numbers.slice(0, 6)}-${numbers.slice(6)}`; } /** * 주민번호 마스킹 (000000-*******) */ export function formatPersonalNumberMasked(value: string): string { const numbers = value.replace(/\D/g, '').slice(0, 13); if (numbers.length === 0) return ''; if (numbers.length <= 6) return numbers; return `${numbers.slice(0, 6)}-${'*'.repeat(Math.min(7, numbers.length - 6))}`; } /** * 주민번호에서 숫자만 추출 */ export function parsePersonalNumber(formatted: string): string { return formatted.replace(/\D/g, '').slice(0, 13); } /** * 카드번호 포맷팅 (0000-0000-0000-0000) */ export function formatCardNumber(value: string): string { const numbers = value.replace(/\D/g, '').slice(0, 16); if (numbers.length === 0) return ''; if (numbers.length <= 4) return numbers; if (numbers.length <= 8) return `${numbers.slice(0, 4)}-${numbers.slice(4)}`; if (numbers.length <= 12) return `${numbers.slice(0, 4)}-${numbers.slice(4, 8)}-${numbers.slice(8)}`; return `${numbers.slice(0, 4)}-${numbers.slice(4, 8)}-${numbers.slice(8, 12)}-${numbers.slice(12)}`; } /** * 카드번호에서 숫자만 추출 */ export function parseCardNumber(formatted: string): string { return formatted.replace(/\D/g, '').slice(0, 16); } /** * 계좌번호 포맷팅 (4자리마다 하이픈) * 예: 1234-5678-9012-34 */ export function formatAccountNumber(value: string): string { const numbers = value.replace(/\D/g, '').slice(0, 16); if (numbers.length === 0) return ''; // 4자리씩 나눠서 하이픈으로 연결 const groups = []; for (let i = 0; i < numbers.length; i += 4) { groups.push(numbers.slice(i, i + 4)); } return groups.join('-'); } /** * 계좌번호에서 숫자만 추출 */ export function parseAccountNumber(formatted: string): string { return formatted.replace(/\D/g, '').slice(0, 16); } /** * 숫자 천단위 콤마 포맷팅 */ export function formatNumber(value: number | string, options?: { useComma?: boolean; decimalPlaces?: number; suffix?: string; }): string { const { useComma = true, decimalPlaces, suffix = '' } = options || {}; const num = typeof value === 'string' ? parseFloat(value) : value; if (isNaN(num)) return ''; let formatted: string; if (decimalPlaces !== undefined) { formatted = num.toFixed(decimalPlaces); } else { formatted = String(num); } if (useComma) { const parts = formatted.split('.'); parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); formatted = parts.join('.'); } return suffix ? `${formatted}${suffix}` : formatted; } /** * 포맷된 숫자 문자열에서 숫자 추출 */ export function parseNumber(formatted: string): number { const cleaned = formatted.replace(/[^\d.-]/g, ''); const num = parseFloat(cleaned); return isNaN(num) ? 0 : num; } /** * Leading zero 제거 (01 → 1) */ export function removeLeadingZeros(value: string): string { // 빈 값 또는 '0' 하나만 있는 경우 if (!value || value === '0') return value; // 소수점이 있는 경우 (0.5 등) if (value.includes('.')) { const parts = value.split('.'); parts[0] = parts[0].replace(/^0+/, '') || '0'; return parts.join('.'); } // 음수인 경우 if (value.startsWith('-')) { return '-' + (value.slice(1).replace(/^0+/, '') || '0'); } // 일반 정수 return value.replace(/^0+/, '') || '0'; } /** * 숫자만 추출 (음수, 소수점 허용 옵션) */ export function extractNumbers(value: string, options?: { allowNegative?: boolean; allowDecimal?: boolean; }): string { const { allowNegative = false, allowDecimal = false } = options || {}; let pattern = '\\d'; if (allowNegative) pattern = '-?' + pattern; if (allowDecimal) pattern = pattern + '|\\.'; const regex = new RegExp(`[^${allowNegative ? '-' : ''}${allowDecimal ? '.' : ''}\\d]`, 'g'); let result = value.replace(regex, ''); // 중복 마이너스 제거 (첫 번째만 유지) if (allowNegative && result.includes('-')) { const isNegative = result.startsWith('-'); result = result.replace(/-/g, ''); if (isNegative) result = '-' + result; } // 중복 소수점 제거 (첫 번째만 유지) if (allowDecimal && result.includes('.')) { const parts = result.split('.'); result = parts[0] + (parts.length > 1 ? '.' + parts.slice(1).join('') : ''); } return result; }