- 여러 관리 페이지(영업, 회계, 인사, 결재, 게시판, 설정)의 리스트 UI 통일 - IntegratedListTemplateV2 기반 레이아웃 정리 - PricingHistoryDialog 개선 - 공통 컴포넌트 추출 계획 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
11 KiB
공통 컴포넌트 추출 계획서
MVP 완료 후 리팩토링 계획 (2025-12-23)
개요
| 항목 | 수치 |
|---|---|
| 예상 코드 절감 | ~1,900줄 |
| 영향 파일 | 50+ 개 |
| 유지보수 비용 감소 | 30-40% |
| 예상 작업 기간 | 3-4일 |
현재 공통화 현황
✅ 잘 되어있는 것
SearchFilter- 검색 입력 + 필터TabFilter- 탭 형태 필터DateRangeSelector- 날짜 범위 선택TableActions- 테이블 행 액션 버튼FormActions- 폼 저장/취소 버튼FormField- 개별 폼 필드StatCards- 통계 카드StandardDialog- 기본 다이얼로그 (but 사용률 저조)
❌ 공통화 필요한 것
- 삭제 확인 다이얼로그 (40+ 파일 중복)
- 금액 포맷 유틸 (30+ 파일 중복)
- 상태 배지 + 색상 상수 (10+ 파일 중복)
- 상세정보 카드 (15+ 파일 각자 구현)
- 폼 레이아웃 템플릿 (20+ 파일 중복)
Phase 1: 핵심 컴포넌트 (1일)
1.1 DeleteDialog 컴포넌트
위치: src/components/molecules/DeleteDialog.tsx
Props 설계:
interface DeleteDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
itemName?: string; // "거래처", "품목" 등
itemLabel?: string; // 삭제 대상 이름 (예: "삼성전자")
title?: string; // 커스텀 타이틀
description?: string; // 커스텀 설명
onConfirm: () => void;
isLoading?: boolean;
confirmText?: string; // 기본값: "삭제"
cancelText?: string; // 기본값: "취소"
}
사용 예시:
<DeleteDialog
open={showDeleteDialog}
onOpenChange={setShowDeleteDialog}
itemName="거래처"
itemLabel={selectedVendor?.name}
onConfirm={handleDelete}
isLoading={isDeleting}
/>
체크리스트:
- DeleteDialog 컴포넌트 생성
- 기본 스타일 (빨간색 삭제 버튼)
- isLoading 상태 처리
- 접근성 (포커스 트랩, ESC 닫기)
- molecules/index.ts export 추가
적용 대상 파일 (40+ 파일):
accounting/VendorManagement/index.tsxaccounting/BillManagement/index.tsxaccounting/SalesManagement/index.tsxaccounting/PurchaseManagement/index.tsxaccounting/DepositManagement/index.tsxaccounting/WithdrawalManagement/index.tsxhr/EmployeeManagement/index.tsxhr/DepartmentManagement/index.tsxhr/VacationManagement/index.tsxsettings/RankManagement/index.tsxsettings/TitleManagement/index.tsxsettings/PermissionManagement/index.tsxsettings/AccountManagement/index.tsxboard/BoardManagement/index.tsxitems/ItemListClient.tsx- (나머지 25+ 파일은 grep으로 검색)
1.2 포맷 유틸 함수
위치: src/lib/formatters.ts
함수 설계:
// 금액 포맷
export function formatCurrency(amount: number): string;
export function formatCurrencyWithSign(amount: number): string; // +/- 표시
// 금액 셀 (조건부 표시)
export function formatCurrencyOrDash(amount: number, dash?: string): string;
// 날짜 포맷
export function formatDate(date: string | Date, format?: string): string;
export function formatDateTime(date: string | Date): string;
// 숫자 포맷
export function formatNumber(num: number): string;
export function formatPercent(num: number, decimals?: number): string;
// 전화번호 포맷
export function formatPhone(phone: string): string;
// 사업자번호 포맷
export function formatBizNo(bizNo: string): string;
사용 예시:
formatCurrency(10000) // "10,000원"
formatCurrencyOrDash(0) // "-"
formatCurrencyWithSign(-5000) // "-5,000원"
formatDate('2024-01-15') // "2024-01-15"
formatPhone('01012345678') // "010-1234-5678"
formatBizNo('1234567890') // "123-45-67890"
체크리스트:
- formatters.ts 파일 생성
- formatCurrency 함수
- formatCurrencyOrDash 함수
- formatCurrencyWithSign 함수
- formatDate 함수
- formatDateTime 함수
- formatNumber 함수
- formatPercent 함수
- formatPhone 함수
- formatBizNo 함수
- 단위 테스트 (선택)
적용 대상 파일 (30+ 파일):
- 모든 accounting/* 컴포넌트
- 모든 테이블에서 금액 표시하는 곳
Phase 2: 상태 표시 컴포넌트 (1일)
2.1 상태 색상 중앙화
위치: src/lib/status-colors.ts
설계:
// 공통 상태 색상
export const STATUS_COLORS = {
// 일반 상태
active: 'bg-green-100 text-green-800',
inactive: 'bg-gray-100 text-gray-800',
pending: 'bg-yellow-100 text-yellow-800',
completed: 'bg-blue-100 text-blue-800',
cancelled: 'bg-red-100 text-red-800',
// 결제/금융 상태
paid: 'bg-green-100 text-green-800',
unpaid: 'bg-red-100 text-red-800',
partial: 'bg-orange-100 text-orange-800',
overdue: 'bg-red-100 text-red-800',
// 기본값
default: 'bg-gray-100 text-gray-800',
} as const;
// 도메인별 상태 색상
export const BILL_STATUS_COLORS = { ... };
export const VENDOR_CATEGORY_COLORS = { ... };
export const ORDER_STATUS_COLORS = { ... };
export const INSPECTION_STATUS_COLORS = { ... };
체크리스트:
- status-colors.ts 파일 생성
- 공통 STATUS_COLORS 정의
- BILL_STATUS_COLORS 이동
- VENDOR_CATEGORY_COLORS 이동
- ORDER_STATUS_COLORS 정의
- INSPECTION_STATUS_COLORS 정의
- PRODUCTION_STATUS_COLORS 정의
2.2 StatusBadge 컴포넌트
위치: src/components/ui/status-badge.tsx
Props 설계:
interface StatusBadgeProps {
status: string;
label?: string;
colorMap?: Record<string, string>;
size?: 'sm' | 'md' | 'lg';
className?: string;
}
사용 예시:
// 색상맵 지정
<StatusBadge
status="paymentComplete"
label="결제완료"
colorMap={BILL_STATUS_COLORS}
/>
// 공통 색상 사용
<StatusBadge status="active" label="활성" />
체크리스트:
- StatusBadge 컴포넌트 생성
- 기본 색상 (STATUS_COLORS) 적용
- colorMap prop으로 커스텀 색상 지원
- size 변형 (sm, md, lg)
- ui/index.ts export 추가
적용 대상 파일 (10+ 파일):
accounting/BillManagement/index.tsxaccounting/SalesManagement/index.tsxaccounting/VendorManagement/index.tsxproduction/WorkOrders/WorkOrderList.tsxquality/InspectionManagement/InspectionList.tsx- (나머지 파일)
Phase 3: 카드/레이아웃 컴포넌트 (1일)
3.1 DetailInfoCard 컴포넌트
위치: src/components/molecules/DetailInfoCard.tsx
Props 설계:
interface InfoItem {
label: string;
value: React.ReactNode;
className?: string;
span?: number; // grid span
}
interface DetailInfoCardProps {
title?: string;
description?: string;
items: InfoItem[];
columns?: 1 | 2 | 3 | 4;
className?: string;
headerAction?: React.ReactNode;
}
사용 예시:
<DetailInfoCard
title="거래처 정보"
columns={2}
headerAction={<Button size="sm">수정</Button>}
items={[
{ label: '상호명', value: data.name },
{ label: '사업자번호', value: formatBizNo(data.bizNo) },
{ label: '대표자', value: data.ceo },
{ label: '연락처', value: formatPhone(data.phone) },
{ label: '주소', value: data.address, span: 2 },
]}
/>
체크리스트:
- DetailInfoCard 컴포넌트 생성
- columns 1/2/3/4 지원
- span으로 컬럼 병합 지원
- headerAction 슬롯
- 반응형 (모바일에서 1컬럼)
- molecules/index.ts export 추가
적용 대상 파일 (15+ 파일):
accounting/VendorManagement/VendorDetail.tsxaccounting/BillManagement/BillDetail.tsxaccounting/SalesManagement/SalesDetail.tsxaccounting/PurchaseManagement/PurchaseDetail.tsxhr/EmployeeManagement/EmployeeDetail.tsx- (나머지 Detail 페이지들)
3.2 FormGridLayout 컴포넌트
위치: src/components/molecules/FormGridLayout.tsx
Props 설계:
interface FormGridLayoutProps {
children: React.ReactNode;
columns?: 1 | 2 | 3 | 4;
gap?: 'sm' | 'md' | 'lg';
className?: string;
}
interface FormSectionProps {
title?: string;
description?: string;
children: React.ReactNode;
columns?: 1 | 2 | 3 | 4;
}
사용 예시:
<FormSection title="기본 정보" columns={2}>
<FormField label="이름" required value={name} onChange={setName} />
<FormField label="이메일" type="email" value={email} onChange={setEmail} />
<FormField label="주소" className="col-span-2" value={address} onChange={setAddress} />
</FormSection>
체크리스트:
- FormGridLayout 컴포넌트 생성
- FormSection 컴포넌트 생성
- columns 1/2/3/4 지원
- gap 크기 (sm/md/lg)
- col-span 클래스 지원
- 반응형
Phase 4: 마이그레이션 및 검증 (1일)
4.1 기존 코드 마이그레이션
체크리스트:
- DeleteDialog 마이그레이션 (40+ 파일)
- formatCurrency 마이그레이션 (30+ 파일)
- StatusBadge 마이그레이션 (10+ 파일)
- DetailInfoCard 마이그레이션 (15+ 파일)
- 불필요한 import 제거
- 미사용 코드 삭제
4.2 검증
체크리스트:
- 빌드 에러 없음 확인 (
npm run build) - 타입 에러 없음 확인 (
npm run type-check) - 주요 페이지 동작 테스트
- 거래처 삭제
- 품목 삭제
- 금액 표시 확인
- 상태 배지 표시 확인
- 반응형 테스트 (모바일)
4.3 문서화
체크리스트:
- 컴포넌트 JSDoc 주석
- 사용 예시 코드
- claudedocs 업데이트
파일 구조 (최종)
src/
├── components/
│ ├── ui/
│ │ ├── status-badge.tsx # 🆕 Phase 2
│ │ └── ...
│ ├── molecules/
│ │ ├── StandardDialog.tsx # 기존
│ │ ├── DeleteDialog.tsx # 🆕 Phase 1
│ │ ├── DetailInfoCard.tsx # 🆕 Phase 3
│ │ ├── FormGridLayout.tsx # 🆕 Phase 3
│ │ └── index.ts
│ └── ...
├── lib/
│ ├── formatters.ts # 🆕 Phase 1
│ ├── status-colors.ts # 🆕 Phase 2
│ └── ...
└── ...
예상 효과
| Phase | 컴포넌트 | 절감 라인 | 영향 파일 |
|---|---|---|---|
| 1 | DeleteDialog | ~800줄 | 40+ |
| 1 | formatters | ~150줄 | 30+ |
| 2 | status-colors | ~200줄 | 10+ |
| 2 | StatusBadge | ~100줄 | 10+ |
| 3 | DetailInfoCard | ~400줄 | 15+ |
| 3 | FormGridLayout | ~250줄 | 20+ |
| 합계 | ~1,900줄 | 50+ |
우선순위 정리
🔴 필수 (Phase 1)
- DeleteDialog - 가장 많은 중복, 즉시 효과
- formatters - 유틸 함수, 간단히 적용
🟡 권장 (Phase 2)
- status-colors - 색상 상수 중앙화
- StatusBadge - 일관된 상태 표시
🟢 선택 (Phase 3)
- DetailInfoCard - 상세 페이지 통일
- FormGridLayout - 폼 레이아웃 통일
변경 이력
| 날짜 | 변경 내용 |
|---|---|
| 2025-12-23 | 최초 작성 - 공통 컴포넌트 추출 계획 |