- useAccountingListPage, useDateRange 공통 훅 추출 - accounting/shared/ 공통 컴포넌트 분리 - 회계 모듈(입금/출금/매출/매입/청구 등) 중복 로직 통합 - 차량관리 page.tsx 패턴 간소화 - 건설/결재/자재/출하/단가 등 날짜 관련 코드 공통화 - 코드 중복 제거 체크리스트 문서 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
16 KiB
코드 중복 제거 및 공통화 체크리스트
작성일: 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') 적용으로 이미 수정된 파일:
accounting/DepositManagement/index.tsxaccounting/WithdrawalManagement/index.tsxaccounting/BillManagement/index.tsx
이번 세션에서 추가 발견 및 수정한 파일 (동일 버그):
accounting/BillManagement/BillManagementClient.tsx— 실제 page에서 사용되는 컴포넌트 ('2025-09-01'~'2025-09-03'→useDateRange('currentYear'))accounting/PurchaseManagement/index.tsx— ('2025-01-01'~'2025-12-31'→useDateRange('currentYear'))approval/ApprovalBox/index.tsx— ('2025-09-01'~'2025-09-03'→useDateRange('currentYear'))approval/ReferenceBox/index.tsx— ('2025-09-01'~'2025-09-03'→useDateRange('currentYear'))approval/DraftBox/index.tsx— ('2025-01-01'~'2025-12-31'→useDateRange('currentYear'))process-management/ProcessListClient.tsx— ('2025-01-01'~'2025-12-31'→useDateRange('currentYear'))hr/VacationManagement/index.tsx— ('2025-12-01'~'2025-12-31'→useDateRange('currentMonth'))hr/SalaryManagement/index.tsx— ('2025-12-01'~'2025-12-31'→useDateRange('currentMonth'))pricing-table-management/PricingTableListClient.tsx— ('2025-01-01'~'2025-12-31'→useDateRange('currentYear'))checklist-management/ChecklistListClient.tsx— ('2025-01-01'~'2025-12-31'→useDateRange('currentYear'))
검증
npx tsc --noEmit통과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()
- VehicleDispatch 3파일 →
import { formatNumber as formatAmount } from '@/lib/utils/amount' - 결재 문서 5파일 →
import { formatNumber as formatCurrency } from '@/lib/utils/amount' - SalesDetail, PurchaseDetail →
import { formatNumber as formatAmount } from '@/lib/utils/amount' - GiftCertificateManagement, DailyReport →
import { formatNumber as formatAmount } from '@/lib/utils/amount' - StageCard, ProjectCard →
import { formatNumber as formatAmount } from '@/lib/utils/amount' - ProjectListClient →
import { formatAmountWon as formatAmount } from '@/lib/utils/amount' - PricingTableForm, PricingTableListClient →
import { formatNumber } from '@/lib/utils/amount' - DirectConstructionContent, IndirectConstructionContent →
import { formatNumber } from '@/lib/utils/amount' - 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()
- 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)
검증
- TypeScript 빌드 에러 없음 확인
- 각 수정 파일에서 포맷 결과가 기존과 동일한지 확인 (동일 로직 import)
WP-3: 차량관리 edit 페이지 리다이렉트 전환 ✅ 이전 세션에서 완료
심각도: 🟡 HIGH (코드 중복) 난이도: 낮음 | 파일 수: 6 (3쌍) 병렬: Phase 1 — 독립 실행 가능
현황
이전 세션에서 이미 완료됨. 3개 edit 페이지 모두 리다이렉트 스텁으로 교체되어 있고,
3개 [id]/page.tsx 모두 useSearchParams로 mode를 읽고 있음.
수정 완료
vehicle-management/vehicle/[id]/edit/page.tsx→router.replace(...?mode=edit)리다이렉트 스텁vehicle-management/vehicle/[id]/page.tsx→useSearchParams+ mode 처리vehicle-management/forklift/[id]/edit/page.tsx→ 리다이렉트 스텁vehicle-management/forklift/[id]/page.tsx→useSearchParams+ mode 처리vehicle-management/vehicle-log/[id]/edit/page.tsx→ 리다이렉트 스텁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'
src/hooks/useDateRange.ts생성- 회계 5개 컴포넌트 적용:
- SalesManagement →
useDateRange('currentYear') - DepositManagement →
useDateRange('currentYear') - WithdrawalManagement →
useDateRange('currentYear') - BillManagement →
useDateRange('currentYear') - GiftCertificateManagement →
useDateRange('currentMonth')(date-fns import 제거)
- SalesManagement →
- 건설 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 통합)
src/hooks/useAccountingListPage.ts생성 (DEFAULT_PAGINATION 포함)src/hooks/index.ts에 양쪽 훅 export 추가- 회계 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
검증
- TypeScript 빌드 에러 없음 확인
- 15파일 useDateRange 적용 + 3파일 useAccountingListPage 적용
WP-5: 회계 컴포넌트 공통 패턴 추출 ✅ 완료
심각도: 🟢 MEDIUM (코드 품질) 난이도: 중간 | 신규 파일: 1개 | 영향 파일: 5개 컴포넌트 병렬: Phase 2 — WP-4와 서로 병렬 가능 (겹치는 파일 없음)
5-A: 팩토리 함수 추출 ✅
위치: src/components/accounting/shared/index.ts (신규 생성)
4개 유틸리티 함수 생성 및 적용:
createDeleteItemHandler(deleteFn, setData, msg)— 4개 컴포넌트 적용 (Sales, Deposit, Withdrawal, Bill)- 7줄 inline handler → 1줄 팩토리 호출 (컴포넌트당 -6줄)
extractUniqueOptions(data, fieldKey, opts?)— 5개 컴포넌트 적용 (Sales, Deposit, Withdrawal, Bill, ExpectedExpense)- 4-6줄 useMemo body → 1줄 호출 (컴포넌트당 -3~5줄)
createDateAmountSortFn(dateField, amountField, direction)— 3개 컴포넌트 적용 (Sales, Deposit, Withdrawal)- 15줄 switch문 → 1줄 팩토리 호출 (컴포넌트당 -14줄)
- Bill은 'maturityDate' 추가 정렬이 있어 제외, ExpectedExpense는 createdAt 정렬이라 제외
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곳만 사용. 훅 생성 오버헤드가 이점을 초과
검증
- TypeScript 빌드 에러 없음 (
npx tsc --noEmit통과) - 삭제/정렬/필터 로직이 기존과 동일한 동작 보장 (동일한 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건):
quotes/QuoteTransactionModal.tsx— const today 초기화 (1건)vehicle-management/VehicleLogList/actions.ts— writeDate + createdAt 기본값 (2건)pricing-table-management/actions.ts— create/update changedDate (2건)pricing-table-management/PricingTableForm.tsx— 변경일 Input value (1건)material/ReceivingManagement/actions.ts— reportDate ×2 + today 변수 (3건)material/ReceivingManagement/ReceivingDetail.tsx— adjustmentDate (1건)accounting/TaxInvoiceManagement/ManualEntryModal.tsx— writeDate 기본값 (1건)
getLocalDateString() 적용 (7파일, 13건):
pricing-distribution/PriceDistributionList.tsx— startDate + endDate (2건)pricing-distribution/actions.ts— createdAt + effectiveDate (2건)material/StockStatus/StockStatusList.tsx— startDate + endDate (2건)quality/InspectionManagement/InspectionList.tsx— startDate + endDate (2건)material/ReceivingManagement/ReceivingList.tsx— startDate + endDate (2건)outbound/ShipmentManagement/ShipmentList.tsx— startDate + endDate (2건)outbound/VehicleDispatchManagement/VehicleDispatchList.tsx— startDate + endDate (2건)
both 적용 (1파일, 2건):
accounting/TaxInvoiceManagement/CardHistoryModal.tsx— today (getTodayString) + monthAgo (getLocalDateString)
검증
npx tsc --noEmit통과 — TypeScript 에러 0건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 이후 |
Phase 간 의존성 상세
| 의존 관계 | 이유 |
|---|---|
| WP-4 → WP-1 | WP-1에서 수정한 날짜 초기값 패턴을 useDateRange 훅 설계에 반영 |
| WP-5 → WP-2 | WP-2에서 통일된 formatAmount를 공통 컴포넌트에서 import |
| WP-6 → WP-4 | useDateRange가 getTodayString()을 내부 사용하므로 훅 완성 후 적용 |