Files
sam-react-prod/claudedocs/refactoring/[REF-2026-02-19] code-dedup-commonization-checklist.md
유병철 07374c826c refactor(WEB): claudedocs 재정리 및 AuthContext/Zustand/유틸 코드 개선
- claudedocs 폴더 구조 재정리: archive/sessions, guides/migration·mobile·universal-list, refactoring 분류
- 오래된 세션 컨텍스트/체크리스트 문서 정리 (아카이브 이동 또는 삭제)
- AuthContext → authStore(Zustand) 전환 시작, RootProvider 간소화
- GenericCRUDDialog 공통 다이얼로그 컴포넌트 추가
- PermissionDialog 삭제 → GenericCRUDDialog로 대체
- RankDialog/TitleDialog GenericCRUDDialog 기반으로 리팩토링
- toast-utils.ts 삭제 (미사용)
- fileDownload.ts 개선, excel-download.ts 정리
- menuStore/themeStore Zustand 셀렉터 최적화
- useColumnSettings/useTableColumnStore 기능 보강
- 세금계산서/견적/작업자화면/결재 등 소규모 개선

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

481 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 코드 중복 제거 및 공통화 체크리스트
> 작성일: 2026-02-19
> 기반: 4개 에이전트 병렬 분석 결과 (회계 page.tsx / 회계 컴포넌트 / 유틸 중복 / 비회계 페이지)
> 구조: 6개 작업 패키지 (WP) — 의존성 기반 병렬화 설계
---
## 작업 패키지 의존성 맵
```
Phase 1 (독립 — 전부 병렬 가능):
[x] WP-1: 날짜 하드코딩 긴급 수정 (10파일) ✅
[x] WP-2: formatAmount/formatDate import 통일 (35+파일) ✅
[x] WP-3: 차량관리 edit 페이지 리다이렉트 전환 (6파일) ✅
Phase 2 (Phase 1 완료 후 — 서로 간 병렬 가능):
[x] WP-4: 공통 훅 추출 (useDateRange, useAccountingListPage 등) ✅
[x] WP-5: 회계 컴포넌트 공통 패턴 추출 (팩토리 함수, 공통 컴포넌트) ✅
Phase 3 (Phase 2 완료 후):
[x] WP-6: .toISOString().split('T')[0] → getTodayString() 전환 (15파일 26건) ✅
```
### 병렬 실행 판단 기준
| 판단 | 조건 |
|------|------|
| **병렬 가능** | 수정 파일이 겹치지 않고, 새 유틸/훅에 의존하지 않음 |
| **순차 필요** | 새로 만드는 훅/유틸을 다른 WP에서 import해야 하는 경우 |
---
## WP-1: 날짜 필터 하드코딩 긴급 수정 ✅ 완료 (2026-02-19)
**심각도**: 🔴 CRITICAL (데이터 누락 버그)
**난이도**: 낮음 | **파일 수**: 10 | **예상 변경량**: 각 2줄
**병렬**: Phase 1 — 독립 실행 가능
### 현황
매출관리와 동일한 버그. `2025-xx-xx` 하드코딩으로 잘못된 기간 데이터 표시.
### 수정 완료
**WP-4에서 useDateRange('currentYear') 적용으로 이미 수정된 파일:**
- [x] `accounting/DepositManagement/index.tsx`
- [x] `accounting/WithdrawalManagement/index.tsx`
- [x] `accounting/BillManagement/index.tsx`
**이번 세션에서 추가 발견 및 수정한 파일 (동일 버그):**
- [x] `accounting/BillManagement/BillManagementClient.tsx` — 실제 page에서 사용되는 컴포넌트 (`'2025-09-01'`~`'2025-09-03'``useDateRange('currentYear')`)
- [x] `accounting/PurchaseManagement/index.tsx` — (`'2025-01-01'`~`'2025-12-31'``useDateRange('currentYear')`)
- [x] `approval/ApprovalBox/index.tsx` — (`'2025-09-01'`~`'2025-09-03'``useDateRange('currentYear')`)
- [x] `approval/ReferenceBox/index.tsx` — (`'2025-09-01'`~`'2025-09-03'``useDateRange('currentYear')`)
- [x] `approval/DraftBox/index.tsx` — (`'2025-01-01'`~`'2025-12-31'``useDateRange('currentYear')`)
- [x] `process-management/ProcessListClient.tsx` — (`'2025-01-01'`~`'2025-12-31'``useDateRange('currentYear')`)
- [x] `hr/VacationManagement/index.tsx` — (`'2025-12-01'`~`'2025-12-31'``useDateRange('currentMonth')`)
- [x] `hr/SalaryManagement/index.tsx` — (`'2025-12-01'`~`'2025-12-31'``useDateRange('currentMonth')`)
- [x] `pricing-table-management/PricingTableListClient.tsx` — (`'2025-01-01'`~`'2025-12-31'``useDateRange('currentYear')`)
- [x] `checklist-management/ChecklistListClient.tsx` — (`'2025-01-01'`~`'2025-12-31'``useDateRange('currentYear')`)
### 검증
- [x] `npx tsc --noEmit` 통과
- [x] `useState('2025` 잔존 0건 확인
---
## WP-2: formatAmount / formatDate import 통일 ✅ 완료 (2026-02-19)
**심각도**: 🟡 HIGH (코드 품질)
**난이도**: 낮음 | **파일 수**: 24파일 수정 / 10파일 스킵 | **예상 변경량**: 각 파일 2-5줄 (로컬 함수 삭제 + import 추가)
**병렬**: Phase 1 — 독립 실행 가능
### 2-A: formatAmount/formatNumber/formatCurrency 통일 — 21파일 완료
**canonical**: `src/lib/utils/amount.ts``formatNumber()`, `formatAmountWon()`
- [x] VehicleDispatch 3파일 → `import { formatNumber as formatAmount } from '@/lib/utils/amount'`
- [x] 결재 문서 5파일 → `import { formatNumber as formatCurrency } from '@/lib/utils/amount'`
- [x] SalesDetail, PurchaseDetail → `import { formatNumber as formatAmount } from '@/lib/utils/amount'`
- [x] GiftCertificateManagement, DailyReport → `import { formatNumber as formatAmount } from '@/lib/utils/amount'`
- [x] StageCard, ProjectCard → `import { formatNumber as formatAmount } from '@/lib/utils/amount'`
- [x] ProjectListClient → `import { formatAmountWon as formatAmount } from '@/lib/utils/amount'`
- [x] PricingTableForm, PricingTableListClient → `import { formatNumber } from '@/lib/utils/amount'`
- [x] DirectConstructionContent, IndirectConstructionContent → `import { formatNumber } from '@/lib/utils/amount'`
- [x] SubscriptionClient, SubscriptionManagement → `import { formatAmountWon as formatCurrency } from '@/lib/utils/amount'`
**스킵 (다른 동작):**
- ReceivablesStatus (0일 때 '' 반환), VendorLedger (0일 때 '' 반환), VendorLedgerDetail (괄호 옵션)
- CEODashboard/components.tsx (showUnit 옵션)
### 2-B: formatDate 통일 — 3파일 완료
**canonical**: `src/lib/utils/date.ts``formatDate()`
- [x] OrderManagementListClient, OrderManagementUnified, ConstructionManagementListClient → `import { formatDate } from '@/lib/utils/date'`
**스킵 (다른 출력 형식 — canonical formatDate와 비호환):**
- BoardDetail, BoardForm (formatDateTime: `YYYY-MM-DD HH:MM`)
- SubscriptionClient, SubscriptionManagement (한국어: `YYYY년 M월 D일`)
- PermissionManagement (date-fns format 사용)
- ProductionDashboard (`M/D` 형식)
- HandoverReportDocumentModal (한국어 long date)
### 검증
- [x] TypeScript 빌드 에러 없음 확인
- [x] 각 수정 파일에서 포맷 결과가 기존과 동일한지 확인 (동일 로직 import)
---
## WP-3: 차량관리 edit 페이지 리다이렉트 전환 ✅ 이전 세션에서 완료
**심각도**: 🟡 HIGH (코드 중복)
**난이도**: 낮음 | **파일 수**: 6 (3쌍)
**병렬**: Phase 1 — 독립 실행 가능
### 현황
이전 세션에서 이미 완료됨. 3개 edit 페이지 모두 리다이렉트 스텁으로 교체되어 있고,
3개 `[id]/page.tsx` 모두 `useSearchParams`로 mode를 읽고 있음.
### 수정 완료
- [x] `vehicle-management/vehicle/[id]/edit/page.tsx``router.replace(...?mode=edit)` 리다이렉트 스텁
- [x] `vehicle-management/vehicle/[id]/page.tsx``useSearchParams` + mode 처리
- [x] `vehicle-management/forklift/[id]/edit/page.tsx` → 리다이렉트 스텁
- [x] `vehicle-management/forklift/[id]/page.tsx``useSearchParams` + mode 처리
- [x] `vehicle-management/vehicle-log/[id]/edit/page.tsx` → 리다이렉트 스텁
- [x] `vehicle-management/vehicle-log/[id]/page.tsx``useSearchParams` + mode 처리
---
## WP-4: 공통 훅 추출 ✅ 완료 (2026-02-19)
**심각도**: 🟡 HIGH (아키텍처 개선)
**난이도**: 중간 | **신규 파일**: 2개 | **적용 파일**: 18개
**병렬**: Phase 2 — WP-4와 WP-5는 서로 병렬 가능.
### 4-A: `useDateRange` 훅 — 15파일 적용 완료
**위치**: `src/hooks/useDateRange.ts`
**프리셋**: `'currentYear'` | `'currentMonth'` | `'today'` | `'none'`
- [x] `src/hooks/useDateRange.ts` 생성
- [x] 회계 5개 컴포넌트 적용:
- SalesManagement → `useDateRange('currentYear')`
- DepositManagement → `useDateRange('currentYear')`
- WithdrawalManagement → `useDateRange('currentYear')`
- BillManagement → `useDateRange('currentYear')`
- GiftCertificateManagement → `useDateRange('currentMonth')` (date-fns import 제거)
- [x] 건설 10개 ListClient 적용 (모두 `useDateRange('today')` — UTC 버그도 동시 수정):
- ProgressBillingManagementListClient, OrderManagementListClient
- ConstructionManagementListClient, IssueManagementListClient
- WorkerStatusListClient, PricingListClient
- UtilityManagementListClient, SiteManagementListClient
- StructureReviewListClient, HandoverReportListClient
**스킵 (비호환):**
- ExpectedExpenseManagement (현재월 ~ 3개월 후 커스텀 범위)
- TaxInvoiceIssuance (filters 객체 패턴, 별도 날짜 관리)
### 4-B: `useAccountingListPage` 훅 — 3개 page.tsx 적용 완료
**위치**: `src/hooks/useAccountingListPage.ts`
**효과**: 50줄 → 20줄 (DEFAULT_PAGINATION + useSearchParams + useEffect + state 통합)
- [x] `src/hooks/useAccountingListPage.ts` 생성 (DEFAULT_PAGINATION 포함)
- [x] `src/hooks/index.ts`에 양쪽 훅 export 추가
- [x] 회계 3개 page.tsx 적용:
- `accounting/sales/page.tsx` (56줄 → 22줄)
- `accounting/deposits/page.tsx` (53줄 → 20줄)
- `accounting/withdrawals/page.tsx` (53줄 → 20줄)
**미적용 (패턴 비호환):**
- bills: 추가 searchParams (billType, page, vendorId)
- expected-expenses: mode 없음, 커스텀 fetch params
- gift-certificates: 리스트 자체 로딩, edit 모드 별도
- bad-debt-collection: Promise.all + summary
- vendors: total 사용 (pagination 미사용)
- tax-invoice-issuance: 복합 mode (edit+id), Promise.all
### 검증
- [x] TypeScript 빌드 에러 없음 확인
- [x] 15파일 useDateRange 적용 + 3파일 useAccountingListPage 적용
---
## WP-5: 회계 컴포넌트 공통 패턴 추출 ✅ 완료
**심각도**: 🟢 MEDIUM (코드 품질)
**난이도**: 중간 | **신규 파일**: 1개 | **영향 파일**: 5개 컴포넌트
**병렬**: Phase 2 — WP-4와 서로 병렬 가능 (겹치는 파일 없음)
### 5-A: 팩토리 함수 추출 ✅
**위치**: `src/components/accounting/shared/index.ts` (신규 생성)
4개 유틸리티 함수 생성 및 적용:
- [x] `createDeleteItemHandler(deleteFn, setData, msg)` — 4개 컴포넌트 적용 (Sales, Deposit, Withdrawal, Bill)
- 7줄 inline handler → 1줄 팩토리 호출 (컴포넌트당 -6줄)
- [x] `extractUniqueOptions(data, fieldKey, opts?)` — 5개 컴포넌트 적용 (Sales, Deposit, Withdrawal, Bill, ExpectedExpense)
- 4-6줄 useMemo body → 1줄 호출 (컴포넌트당 -3~5줄)
- [x] `createDateAmountSortFn(dateField, amountField, direction)` — 3개 컴포넌트 적용 (Sales, Deposit, Withdrawal)
- 15줄 switch문 → 1줄 팩토리 호출 (컴포넌트당 -14줄)
- Bill은 'maturityDate' 추가 정렬이 있어 제외, ExpectedExpense는 createdAt 정렬이라 제외
- [x] `computeMonthlyTotal(data, dateField, amountField)` — 3개 컴포넌트 적용 (Sales, Deposit, Withdrawal)
- 6줄 (currentMonth/currentYear + filter + reduce) → 1줄 (컴포넌트당 -5줄)
### 5-B: 스코프 조정 (계획 대비)
**적용하지 않은 항목과 사유:**
- `createFilterFn` — 각 컴포넌트의 customFilterFn이 너무 상이 (필드명, 조건문 구조 다름). 추상화 비용 > 이점
- `AccountSubjectSavePanel` — UI+로직이 각 컴포넌트마다 미묘하게 다름 (Withdrawal은 배열, Deposit은 Set). 현재 수준으로 충분
- `useRefreshableData` — Deposit/Withdrawal 2곳만 사용. 훅 생성 오버헤드가 이점을 초과
### 검증
- [x] TypeScript 빌드 에러 없음 (`npx tsc --noEmit` 통과)
- [x] 삭제/정렬/필터 로직이 기존과 동일한 동작 보장 (동일한 switch/filter 구현을 shared로 이동)
### 정량 결과
- **삭제된 중복 코드**: ~120줄 (5파일 × 평균 24줄/파일)
- **신규 공통 코드**: ~130줄 (shared/index.ts)
- **순 효과**: 중복 제거 + 단일 소스 오브 트루스 확보
---
## WP-6: toISOString UTC 버그 전환 ✅ 완료 (2026-02-19)
**심각도**: 🟢 MEDIUM (잠재적 타임존 버그)
**난이도**: 중간 | **파일 수**: 15파일 26건 | **예상 변경량**: 각 1-2줄
**병렬**: Phase 3 — WP-4의 useDateRange가 먼저 필요 (일부 파일 중복)
### 현황
`new Date().toISOString().split('T')[0]`은 UTC 기준이라 KST 00:00~08:59에 전날 날짜 반환.
`src/lib/utils/date.ts``getTodayString()`/`getLocalDateString()` 이미 존재.
### 수정 완료
**건설관리 10파일 — WP-4에서 useDateRange('today')로 이미 수정됨 (Phase 2에서 완료)**
**getTodayString() 적용 (7파일, 11건):**
- [x] `quotes/QuoteTransactionModal.tsx` — const today 초기화 (1건)
- [x] `vehicle-management/VehicleLogList/actions.ts` — writeDate + createdAt 기본값 (2건)
- [x] `pricing-table-management/actions.ts` — create/update changedDate (2건)
- [x] `pricing-table-management/PricingTableForm.tsx` — 변경일 Input value (1건)
- [x] `material/ReceivingManagement/actions.ts` — reportDate ×2 + today 변수 (3건)
- [x] `material/ReceivingManagement/ReceivingDetail.tsx` — adjustmentDate (1건)
- [x] `accounting/TaxInvoiceManagement/ManualEntryModal.tsx` — writeDate 기본값 (1건)
**getLocalDateString() 적용 (7파일, 13건):**
- [x] `pricing-distribution/PriceDistributionList.tsx` — startDate + endDate (2건)
- [x] `pricing-distribution/actions.ts` — createdAt + effectiveDate (2건)
- [x] `material/StockStatus/StockStatusList.tsx` — startDate + endDate (2건)
- [x] `quality/InspectionManagement/InspectionList.tsx` — startDate + endDate (2건)
- [x] `material/ReceivingManagement/ReceivingList.tsx` — startDate + endDate (2건)
- [x] `outbound/ShipmentManagement/ShipmentList.tsx` — startDate + endDate (2건)
- [x] `outbound/VehicleDispatchManagement/VehicleDispatchList.tsx` — startDate + endDate (2건)
**both 적용 (1파일, 2건):**
- [x] `accounting/TaxInvoiceManagement/CardHistoryModal.tsx` — today (getTodayString) + monthAgo (getLocalDateString)
### 검증
- [x] `npx tsc --noEmit` 통과 — TypeScript 에러 0건
- [x] `src/` 내 잔여 `.toISOString().split('T')[0]` = 0건 (date.ts 주석 1건 제외)
---
## 병렬 실행 계획 요약
```
┌─────────────────────────────────────────────────────────────┐
│ Phase 1 (독립 — 동시 3개 병렬) │
│ │
│ WP-1 날짜 하드코딩 WP-2 format 통일 WP-3 edit 리다이렉트 │
│ (3파일, ~10분) (35파일, ~40분) (6파일, ~20분) │
│ ↓ ↓ ↓ │
├─────────────────────────────────────────────────────────────┤
│ Phase 2 (Phase 1 완료 후 — 동시 2개 병렬) │
│ │
│ WP-4 공통 훅 추출 WP-5 컴포넌트 공통화 │
│ (신규 2-3파일 + 70파일 적용) (신규 3-5파일 + 7파일 적용) │
│ ↓ ↓ │
├─────────────────────────────────────────────────────────────┤
│ Phase 3 (Phase 2 완료 후) │
│ │
│ WP-6 toISOString UTC 버그 전환 │
│ (60+파일, 가장 큰 작업) │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Phase별 병렬 가능 근거
| Phase | WP 조합 | 병렬 가능 이유 |
|-------|---------|---------------|
| 1 | WP-1 + WP-2 + WP-3 | 수정 파일이 완전히 겹치지 않음. 기존 유틸만 사용, 신규 생성 없음 |
| 2 | WP-4 + WP-5 | WP-4는 hooks/ + page.tsx, WP-5는 components/accounting/shared/ + 컴포넌트 내부. 파일 겹침 없음 |
| 3 | WP-6 단독 | WP-4의 useDateRange 훅을 일부 파일에서 활용해야 하므로 Phase 2 이후 |
---
## WP-7: .toLocaleString() → formatNumber() 마이그레이션 ✅ 완료 (2026-02-19)
**심각도**: 🟢 MEDIUM (코드 일관성)
**난이도**: 낮음 | **파일 수**: 52파일, 183건 | **방법**: Node.js 자동 마이그레이션 스크립트
### 수정 완료
- [x] 52개 파일에서 183건 `.toLocaleString()``formatNumber()` 자동 치환
- [x] `formatNumber` import 자동 추가
- [x] 5개 파일 수동 import 수정 (double-quote import 패턴)
### 검증
- [x] `npx tsc --noEmit` 통과
- [x] `src/` 내 잔여 `.toLocaleString()` = 0건 (amount.ts 내부 제외)
---
## WP-8: 인라인 formatDate → date.ts import 마이그레이션 ✅ 완료 (2026-02-19)
**심각도**: 🟢 MEDIUM (코드 일관성)
**난이도**: 낮음 | **파일 수**: 21파일, 32건 | **방법**: Node.js 자동 마이그레이션 스크립트
### 수정 완료
- [x] `.split('T')[0]``formatDate()` 변환 (21파일)
- [x] `new Date().toISOString().slice(0, 10)``getTodayString()` 변환
- [x] `dateVar.toISOString().slice(0, 10)``getLocalDateString(dateVar)` 변환
- [x] 6개 파일 수동 import 수정 (JSDoc 삽입 버그)
### 스킵 항목
- `?.split('T')[0] || ''` — formatDate는 null일 때 '-' 반환 (falsy 의미 다름)
- `.toLocaleDateString()` — 42건, 로케일 표시 형식으로 canonical과 비호환
- `.toISOString().slice(0,10).replace(/-/g,'')` — YYYYMMDD 형식 (파일명용)
### 검증
- [x] `npx tsc --noEmit` 통과
---
## WP-9: useDeleteDialog 훅 채택 확대 ✅ 완료 (2026-02-20)
**심각도**: 🟢 MEDIUM (코드 일관성)
**난이도**: 중간 | **전체 후보**: ~56파일 | **기존 채택자**: 7파일
### 완료된 작업 (9파일)
- [x] `src/hooks/index.ts`에 useDeleteDialog export 추가
- [x] `settings/RankManagement/index.tsx` 마이그레이션 (단건 삭제, number ID)
- [x] `settings/TitleManagement/index.tsx` 마이그레이션 (단건 삭제, number ID)
- [x] `settings/AccountManagement/AccountDetail.tsx` 마이그레이션 (상세→삭제→목록, number ID)
- [x] `settings/PermissionManagement/PermissionDetailClient.tsx` 마이그레이션 (상세→삭제→목록, number ID)
- [x] `accounting/DepositManagement/DepositDetail.tsx` 마이그레이션 (상세→삭제→목록)
- [x] `accounting/WithdrawalManagement/WithdrawalDetail.tsx` 마이그레이션 (상세→삭제→목록)
- [x] `hr/CardManagement/CardDetail.tsx` 마이그레이션 (상세→삭제→목록)
- [x] `board/BoardDetail/index.tsx` 마이그레이션 (deletePost 클로저 캡처, 2-arg delete)
- [x] `board/BoardManagement/BoardDetailClientV2.tsx` 마이그레이션 (deleteBoard + forceRefreshMenus)
### 의도적 스킵 (마이그레이션 비적합)
- `settings/AccountManagement/AccountDetailForm.tsx` — prop 기반 삭제 + isSaving 공유
- `settings/PermissionManagement/PermissionDetail.tsx` — prop 기반 + IntegratedDetailTemplate 연동
- `settings/PermissionManagement/index.tsx` — bulk+single 혼합 다이얼로그
- `board/CommentSection/CommentItem.tsx` — void onDelete prop, 행동 변경됨
- `hr/EmployeeManagement/index.tsx` — 780줄, 복잡한 useMemo, 위험도 높음
- `hr/DepartmentManagement/index.tsx` — bulk+single 혼합 다이얼로그
- `settings/PopupManagement/PopupDetailClientV2.tsx` — IntegratedDetailTemplate 내부 처리
### 최종 채택 현황: 7(기존) + 9(신규) = 16파일
### 검증
- [x] `npx tsc --noEmit` 통과 (모든 수정 파일 tsc 에러 0건)
---
### Phase 간 의존성 상세
| 의존 관계 | 이유 |
|-----------|------|
| WP-4 → WP-1 | WP-1에서 수정한 날짜 초기값 패턴을 useDateRange 훅 설계에 반영 |
| WP-5 → WP-2 | WP-2에서 통일된 formatAmount를 공통 컴포넌트에서 import |
| WP-6 → WP-4 | useDateRange가 getTodayString()을 내부 사용하므로 훅 완성 후 적용 |
---
## WP-10: 검색 필터 유틸 search.ts ✅ 완료 (2026-02-20)
**심각도**: 🟢 MEDIUM (코드 일관성)
**난이도**: 중간 | **신규 파일**: 1개 | **적용 파일**: 9개
### 10-A: search.ts 유틸 생성 ✅
**위치**: `src/lib/utils/search.ts` (~70줄)
- [x] `filterByText<T>(data, query, fields, options?)` — 텍스트 검색 (case-insensitive 기본)
- [x] `filterByEnum<T>(data, field, value, allValue?)` — enum 필터 ('all' short-circuit)
- [x] `filterByDateRange<T>(data, field, startDate?, endDate?)` — 날짜 범위 필터
- [x] `applyFilters<T>(data, filters)` — 필터 체이닝 파이프라인
- [x] `textFilter`, `enumFilter`, `dateRangeFilter` — 팩토리 함수 (applyFilters용)
### 10-B: 회계 모듈 적용 (9파일) ✅
| # | 파일 | 변경 내용 | 상태 |
|---|------|----------|------|
| 1 | `VendorManagement/VendorManagementClient.tsx` | useMemo 체인 → `applyFilters` (text 3필드 + enum 4개) | ✅ |
| 2 | `BadDebtCollection/index.tsx` | customFilterFn → `applyFilters` (enum 2개) | ✅ |
| 3 | `ExpectedExpenseManagement/index.tsx` | useMemo 체인 → `applyFilters` (text 3필드 + enum 1개) | ✅ |
| 4 | `SalesManagement/index.tsx` | customFilterFn → `applyFilters` (enum 2개 + issuance 인라인 유지) | ✅ |
| 5 | `DepositManagement/index.tsx` | customFilterFn → `applyFilters` (text 4필드 + enum 2개) | ✅ |
| 6 | `WithdrawalManagement/index.tsx` | customFilterFn → `applyFilters` (text 4필드 + enum 2개) | ✅ |
| 7 | `CardTransactionInquiry/index.tsx` | filteredData useMemo → `filterByEnum` (1개) | ✅ |
| 8 | `ReceivablesStatus/index.tsx` | filteredData useMemo → `filterByText` (1필드) | ✅ |
| 9 | `GiftCertificateManagement/index.tsx` | customFilterFn → `applyFilters` (enum 2개) | ✅ |
**스킵**: BillManagement — 서버 사이드 필터링 (API params), 클라이언트 사이드 필터 없음
### 검증
- [x] `npx tsc --noEmit` 통과 (기존 orders/actions.ts 1건 외 에러 0건)
---
## WP-11: 상태 설정 채택 확대 ✅ 완료 (2026-02-20)
**심각도**: 🟢 MEDIUM (코드 일관성)
**난이도**: 중간 | **수정 파일**: status-config.ts + types.ts 4개
### 11-A: 도메인별 상태 설정 추가 (status-config.ts) ✅
7개 회계 도메인 설정 추가:
- [x] `BAD_DEBT_COLLECTION_STATUS_CONFIG` (collecting, legalAction, recovered, badDebt)
- [x] `TAX_INVOICE_STATUS_CONFIG` (pending, journalized, error)
- [x] `BILL_STATUS_CONFIG` (stored~dishonored 8개)
- [x] `SALES_STATUS_CONFIG` (monthlyClose, lastMonth, agreed, outstanding)
- [x] `DEPOSIT_STATUS_CONFIG` (inputWaiting~confirmed 7개)
- [x] `PAYMENT_STATUS_CONFIG` (pending, partial, paid, overdue)
- [x] `MATCH_STATUS_CONFIG` (matched, unmatched)
### 11-B: types.ts 인라인 상수 마이그레이션 (4파일) ✅
| # | 파일 | 제거한 인라인 상수 | 대체 |
|---|------|-------------------|------|
| 1 | `BadDebtCollection/types.ts` | COLLECTION_STATUS_LABELS, STATUS_BADGE_STYLES, STATUS_FILTER_OPTIONS, STATUS_SELECT_OPTIONS | `BAD_DEBT_COLLECTION_STATUS_CONFIG` re-export |
| 2 | `ExpectedExpenseManagement/types.ts` | PAYMENT_STATUS_LABELS, PAYMENT_STATUS_FILTER_OPTIONS | `PAYMENT_STATUS_CONFIG` re-export |
| 3 | `DailyReport/types.ts` | MATCH_STATUS_LABELS, MATCH_STATUS_COLORS | `MATCH_STATUS_CONFIG` re-export |
| 4 | `DepositManagement/types.ts` | DEPOSIT_STATUS_LABELS, DEPOSIT_STATUS_COLORS | `DEPOSIT_STATUS_CONFIG` re-export |
**스킵 (shape 비호환):**
- SalesManagement — `SALES_STATUS_LABELS`에 'all' 키 포함, STATUS_LABELS와 shape 불일치
- TaxInvoiceManagement — `INVOICE_STATUS_MAP``{label, color}` 결합 형태, 분리하면 더 복잡
- WithdrawalManagement — WITHDRAWAL_TYPE 설정 미추가 (status가 아닌 type 분류)
- BillManagement — 수취/발행 분기 헬퍼 함수 복잡
### 검증
- [x] `npx tsc --noEmit` 통과 (기존 orders/actions.ts 1건 외 에러 0건)
- [x] types.ts re-export로 소비 컴포넌트 import 경로 변경 불필요
---
## WP-12: Loading Skeleton 전환 (GenericPageSkeleton → ListPageSkeleton) ✅ 완료 (2026-02-20)
**심각도**: 🟢 MEDIUM (UX 개선)
**난이도**: 낮음 | **파일 수**: 9 | **예상 변경량**: 각 2줄 (import + JSX)
### 현황
회계 모듈 9개 page.tsx가 `GenericPageSkeleton`(form 레이아웃 고정)을 사용하여 실제 리스트 페이지 구조(필터+통계카드+테이블)와 불일치.
이미 `ListPageSkeleton`이 존재하지만 회계 모듈에서 미사용.
### 수정 완료 (9파일)
| # | 파일 | props | 상태 |
|---|------|-------|------|
| 1 | `accounting/deposits/page.tsx` | `showStats statsCount={4} tableColumns={7}` | ✅ |
| 2 | `accounting/sales/page.tsx` | `showStats statsCount={4} tableColumns={10}` | ✅ |
| 3 | `accounting/withdrawals/page.tsx` | `showStats statsCount={4} tableColumns={7}` | ✅ |
| 4 | `accounting/bad-debt-collection/page.tsx` | `showDateRange={false} showCreateButton={false} showStats statsCount={4} tableColumns={8}` | ✅ |
| 5 | `accounting/bills/page.tsx` | `tableColumns={9}` | ✅ |
| 6 | `accounting/expected-expenses/page.tsx` | `showStats statsCount={2} tableColumns={7}` | ✅ |
| 7 | `accounting/vendors/page.tsx` | `showDateRange={false} showStats statsCount={3} tableColumns={10}` | ✅ |
| 8 | `accounting/tax-invoice-issuance/page.tsx` | `showStats statsCount={4} tableColumns={10}` | ✅ |
| 9 | `accounting/gift-certificates/page.tsx` | `showStats statsCount={4} tableColumns={8}` | ✅ |
### 검증
- [x] `npx tsc --noEmit` 통과 (기존 orders/actions.ts 1건 외 에러 0건)
- [x] `GenericPageSkeleton` 회계 모듈 내 잔존 0건
- [x] 브라우저 화면 검수: Slow 3G + CPU 20x 쓰로틀링으로 스켈레톤 캡처 확인
- [x] vendors 페이지 showCreateButton 수정 (화면 검수에서 발견)