- 루트 문서 30개를 도메인별 하위 폴더로 이동 - accounting/, architecture/, dev/, guides/, security/ 등 카테고리 분류 - archive/ 폴더에 QA 스크린샷 이동 - _index.md 문서 맵 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
349 lines
9.9 KiB
Markdown
349 lines
9.9 KiB
Markdown
# 입력폼 공통 컴포넌트화 구현 계획서
|
|
|
|
**작성일**: 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 (범용 숫자 입력)
|
|
|
|
```typescript
|
|
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)
|
|
}
|
|
```
|
|
|
|
**사용 예시**:
|
|
```tsx
|
|
// 기본 정수 입력
|
|
<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 (금액 입력)
|
|
|
|
```typescript
|
|
interface CurrencyInputProps {
|
|
value: number | undefined;
|
|
onChange: (value: number | undefined) => void;
|
|
|
|
currency?: '₩' | '$' | '¥'; // 통화 기호 (기본: ₩)
|
|
showCurrency?: boolean; // 통화 기호 표시 (기본: true)
|
|
allowNegative?: boolean; // 음수 허용 (기본: false)
|
|
}
|
|
```
|
|
|
|
**특징**:
|
|
- 항상 천단위 콤마 표시
|
|
- 정수만 허용 (원 단위)
|
|
- 포커스 해제 시 통화 기호 표시
|
|
|
|
### 4.3 QuantityInput (수량 입력)
|
|
|
|
```typescript
|
|
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 ✅ 완료
|
|
|
|
```typescript
|
|
interface PhoneInputProps {
|
|
value: string;
|
|
onChange: (value: string) => void; // 숫자만 반환
|
|
error?: boolean;
|
|
}
|
|
```
|
|
|
|
### 4.5 BusinessNumberInput ✅ 완료
|
|
|
|
```typescript
|
|
interface BusinessNumberInputProps {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
showValidation?: boolean; // 유효성 검사 아이콘
|
|
error?: boolean;
|
|
}
|
|
```
|
|
|
|
### 4.6 PersonalNumberInput ✅ 완료
|
|
|
|
```typescript
|
|
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: 유틸리티 및 기본 컴포넌트
|
|
- [x] formatters.ts 유틸리티 함수 생성
|
|
- [x] PhoneInput 컴포넌트 생성
|
|
- [x] BusinessNumberInput 컴포넌트 생성
|
|
- [x] PersonalNumberInput 컴포넌트 생성
|
|
- [x] NumberInput 컴포넌트 생성
|
|
- [x] CurrencyInput 컴포넌트 생성
|
|
- [x] QuantityInput 컴포넌트 생성
|
|
|
|
### Phase 2: 통합
|
|
- [x] ui/index.ts export 추가 (개별 import 방식 사용)
|
|
- [x] FormField 타입 확장
|
|
|
|
### Phase 3: 테스트 및 적용
|
|
- [ ] 개별 컴포넌트 동작 테스트
|
|
- [x] VendorDetail 적용 완료
|
|
- [x] PhoneInput: phone, mobile, fax, managerPhone
|
|
- [x] BusinessNumberInput: businessNumber (유효성 검사 포함)
|
|
- [x] CurrencyInput: outstandingAmount, unpaidAmount
|
|
- [x] 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 방식
|
|
```tsx
|
|
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 통합 방식
|
|
```tsx
|
|
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}
|
|
/>
|
|
``` |