- 루트 문서 30개를 도메인별 하위 폴더로 이동 - accounting/, architecture/, dev/, guides/, security/ 등 카테고리 분류 - archive/ 폴더에 QA 스크린샷 이동 - _index.md 문서 맵 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9.9 KiB
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. 롤백 계획
문제 발생 시:
- 새 컴포넌트 import 제거
- 기존
<Input type="number">컴포넌트로 복원 - 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}
/>