Files
sam-react-prod/claudedocs/architecture/[IMPL-2026-01-21] input-form-componentization.md
유병철 f3b07ac875 chore(WEB): claudedocs 디렉토리 도메인별 재구조화
- 루트 문서 30개를 도메인별 하위 폴더로 이동
- accounting/, architecture/, dev/, guides/, security/ 등 카테고리 분류
- archive/ 폴더에 QA 스크린샷 이동
- _index.md 문서 맵 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:35:22 +09:00

9.9 KiB

입력폼 공통 컴포넌트화 구현 계획서

작성일: 2026-01-21 작성자: Claude Code 상태: Phase 1-3 VendorDetail 적용 완료 최종 수정: 2026-01-21


1. 개요

1.1 목적

  • 숫자 입력필드의 선행 0(leading zero) 문제 해결
  • 금액/수량 입력 시 천단위 콤마 및 포맷팅 일관성 확보
  • 전화번호, 사업자번호, 주민번호 등 포맷팅이 필요한 입력필드 공통화
  • 소수점 입력이 필요한 필드 지원 (비율, 환율 등)

1.2 현재 문제점

문제 현상 영향 범위
숫자 입력 leading zero 01, 001 등 표시 전체 숫자 입력
금액 포맷팅 불일치 콤마 처리 제각각 147개 파일
전화번호 포맷팅 없음 01012341234 그대로 표시 거래처, 직원 관리
사업자번호 포맷팅 없음 1234567890 그대로 표시 거래처 관리
Number 타입 일관성 string/number 혼용 타입 에러 가능성

2. 구현 우선순위

🔴 Phase 1: 핵심 숫자 입력 (최우선)

순서 컴포넌트 용도 영향 범위
1 NumberInput 범용 숫자 입력 (leading zero 해결) 전체
2 CurrencyInput 금액 입력 (₩, 천단위 콤마) 147개 파일
3 QuantityInput 수량 입력 (정수, 최소값 0) 재고/주문

🟠 Phase 2: 포맷팅 입력 (완료)

순서 컴포넌트 용도 상태
4 PhoneInput 전화번호 자동 하이픈 완료
5 BusinessNumberInput 사업자번호 포맷팅 완료
6 PersonalNumberInput 주민번호 포맷팅/마스킹 완료

🟢 Phase 3: 통합 및 확장

순서 작업 설명
7 ui/index.ts export 새 컴포넌트 내보내기
8 FormField 확장 새 타입 지원 추가
9 실사용 적용 테스트 VendorDetail 등

3. 생성/수정 파일 목록

3.1 새로 생성한 파일

src/
├── lib/
│   └── formatters.ts              ✅ 완료
├── components/
│   └── ui/
│       ├── phone-input.tsx        ✅ 완료
│       ├── business-number-input.tsx  ✅ 완료
│       ├── personal-number-input.tsx  ✅ 완료
│       ├── number-input.tsx       ✅ 완료
│       ├── currency-input.tsx     ✅ 완료
│       └── quantity-input.tsx     ✅ 완료

3.2 수정한 파일

파일 수정 내용 상태
src/components/molecules/FormField.tsx 새 타입 지원 추가 (phone, businessNumber, personalNumber, currency, quantity) 완료

4. 컴포넌트 상세 설계

4.1 NumberInput (범용 숫자 입력)

interface NumberInputProps {
  value: number | string | undefined;
  onChange: (value: number | undefined) => void;

  // 포맷 옵션
  allowDecimal?: boolean;      // 소수점 허용 (기본: false)
  decimalPlaces?: number;      // 소수점 자릿수 제한
  allowNegative?: boolean;     // 음수 허용 (기본: false)
  useComma?: boolean;          // 천단위 콤마 (기본: false)

  // 범위 제한
  min?: number;
  max?: number;

  // 표시 옵션
  suffix?: string;             // 접미사 (원, 개, % 등)
  allowEmpty?: boolean;        // 빈 값 허용 (기본: true)
}

사용 예시:

// 기본 정수 입력
<NumberInput value={qty} onChange={setQty} />

// 소수점 2자리 (비율, 환율)
<NumberInput value={rate} onChange={setRate} allowDecimal decimalPlaces={2} />

// 퍼센트 입력 (0-100 제한)
<NumberInput value={percent} onChange={setPercent} min={0} max={100} suffix="%" />

// 음수 허용 (재고 조정)
<NumberInput value={adjust} onChange={setAdjust} allowNegative />

4.2 CurrencyInput (금액 입력)

interface CurrencyInputProps {
  value: number | undefined;
  onChange: (value: number | undefined) => void;

  currency?: '₩' | '$' | '¥';  // 통화 기호 (기본: ₩)
  showCurrency?: boolean;       // 통화 기호 표시 (기본: true)
  allowNegative?: boolean;      // 음수 허용 (기본: false)
}

특징:

  • 항상 천단위 콤마 표시
  • 정수만 허용 (원 단위)
  • 포커스 해제 시 통화 기호 표시

4.3 QuantityInput (수량 입력)

interface QuantityInputProps {
  value: number | undefined;
  onChange: (value: number | undefined) => void;

  min?: number;                 // 최소값 (기본: 0)
  max?: number;                 // 최대값
  step?: number;                // 증감 단위 (기본: 1)
  showButtons?: boolean;        // +/- 버튼 표시
  suffix?: string;              // 단위 (개, EA, 박스 등)
}

특징:

  • 정수만 허용
  • 기본 최소값 0
  • 선택적 +/- 버튼

4.4 PhoneInput 완료

interface PhoneInputProps {
  value: string;
  onChange: (value: string) => void;  // 숫자만 반환
  error?: boolean;
}

4.5 BusinessNumberInput 완료

interface BusinessNumberInputProps {
  value: string;
  onChange: (value: string) => void;
  showValidation?: boolean;  // 유효성 검사 아이콘
  error?: boolean;
}

4.6 PersonalNumberInput 완료

interface PersonalNumberInputProps {
  value: string;
  onChange: (value: string) => void;
  maskBack?: boolean;  // 뒷자리 마스킹
  error?: boolean;
}

5. 검수 계획서

5.1 NumberInput 테스트

테스트 항목 입력 기대 결과
Leading zero 제거 01 표시: 1, 값: 1
Leading zero 제거 007 표시: 7, 값: 7
소수점 (허용시) 3.14 표시: 3.14, 값: 3.14
소수점 자릿수 제한 3.14159 (2자리) 표시: 3.14, 값: 3.14
음수 (허용시) -100 표시: -100, 값: -100
콤마 표시 1000000 표시: 1,000,000, 값: 1000000
범위 제한 (max:100) 150 값: 100 (제한)
빈 값 `` 값: undefined
문자 입력 차단 abc 입력 안됨

5.2 CurrencyInput 테스트

테스트 항목 입력 기대 결과
기본 입력 50000 표시: 50,000, 값: 50000
통화 기호 50000 (blur) 표시: ₩50,000
소수점 차단 100.5 표시: 100, 값: 100
대용량 1000000000 표시: 1,000,000,000

5.3 QuantityInput 테스트

테스트 항목 입력 기대 결과
기본 입력 10 표시: 10, 값: 10
음수 차단 -5 값: 0 (최소값)
소수점 차단 10.5 표시: 10, 값: 10
+/- 버튼 클릭 1씩 증감

5.4 실사용 테스트 페이지

페이지 경로 테스트 항목
거래처 관리 /accounting/vendor-management 전화번호, 사업자번호
직원 관리 /hr/employee-management 전화번호, 주민번호
견적 등록 /quotes 수량, 금액
주문 관리 /sales/order-management-sales 수량, 금액
재고 관리 /material/stock-status 수량

6. 완료 체크리스트

Phase 1: 유틸리티 및 기본 컴포넌트

  • formatters.ts 유틸리티 함수 생성
  • PhoneInput 컴포넌트 생성
  • BusinessNumberInput 컴포넌트 생성
  • PersonalNumberInput 컴포넌트 생성
  • NumberInput 컴포넌트 생성
  • CurrencyInput 컴포넌트 생성
  • QuantityInput 컴포넌트 생성

Phase 2: 통합

  • ui/index.ts export 추가 (개별 import 방식 사용)
  • FormField 타입 확장

Phase 3: 테스트 및 적용

  • 개별 컴포넌트 동작 테스트
  • VendorDetail 적용 완료
    • PhoneInput: phone, mobile, fax, managerPhone
    • BusinessNumberInput: businessNumber (유효성 검사 포함)
    • CurrencyInput: outstandingAmount, unpaidAmount
    • NumberInput: overdueDays
  • 문서 최종 업데이트

7. 롤백 계획

문제 발생 시:

  1. 새 컴포넌트 import 제거
  2. 기존 <Input type="number"> 컴포넌트로 복원
  3. FormField 타입 변경 롤백

8. 참고사항

기존 컴포넌트 위치

  • Input: src/components/ui/input.tsx
  • FormField: src/components/molecules/FormField.tsx

생성된 파일

파일 경로
formatters src/lib/formatters.ts
PhoneInput src/components/ui/phone-input.tsx
BusinessNumberInput src/components/ui/business-number-input.tsx
PersonalNumberInput src/components/ui/personal-number-input.tsx
NumberInput src/components/ui/number-input.tsx
CurrencyInput src/components/ui/currency-input.tsx
QuantityInput src/components/ui/quantity-input.tsx

9. 사용 예시

직접 import 방식

import { PhoneInput } from '@/components/ui/phone-input';
import { CurrencyInput } from '@/components/ui/currency-input';
import { NumberInput } from '@/components/ui/number-input';

// 전화번호
<PhoneInput value={phone} onChange={setPhone} />

// 금액
<CurrencyInput value={price} onChange={setPrice} />

// 소수점 허용 숫자
<NumberInput value={rate} onChange={setRate} allowDecimal decimalPlaces={2} />

FormField 통합 방식

import { FormField } from '@/components/molecules/FormField';

// 전화번호
<FormField
  label="전화번호"
  type="phone"
  value={phone}
  onChange={setPhone}
/>

// 사업자번호 (유효성 검사 표시)
<FormField
  label="사업자번호"
  type="businessNumber"
  value={bizNo}
  onChange={setBizNo}
  showValidation
/>

// 금액
<FormField
  label="금액"
  type="currency"
  value={price}
  onChangeNumber={setPrice}
/>

// 수량 (+/- 버튼)
<FormField
  label="수량"
  type="quantity"
  value={qty}
  onChangeNumber={setQty}
  showButtons
  min={1}
  max={100}
/>