refactor(WEB): 회계/견적/설정/생산 등 전반적 코드 개선 및 공통화 2차

- 회계 모듈 전면 개선: 청구/입금/출금/매입/매출/세금계산서/일반전표/거래처원장 등
- 견적 모듈 금액 포맷/할인/수식/미리보기 등 코드 정리
- 설정 모듈: 계정관리/직급/직책/권한 상세 간소화
- 생산 모듈: 작업지시서/작업자화면/검수 문서 코드 정리
- UniversalListPage 엑셀 다운로드 및 필터 기능 확장
- 대시보드/게시판/수주 등 날짜 유틸 공통화 적용
- claudedocs 문서 인덱스 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-20 10:45:47 +09:00
parent 71352923c8
commit f344dc7d00
123 changed files with 877 additions and 789 deletions

View File

@@ -297,6 +297,77 @@ Phase 3 (Phase 2 완료 후):
| 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 간 의존성 상세
| 의존 관계 | 이유 |

View File

@@ -1,6 +1,6 @@
# claudedocs 문서 맵
> 프로젝트 기술 문서 인덱스 (Last Updated: 2026-02-12)
> 프로젝트 기술 문서 인덱스 (Last Updated: 2026-02-19)
## 빠른 참조
@@ -214,6 +214,62 @@ export const remove = service.remove;
- `toPaginationMeta` 자동 활용 (직접 import 불필요)
- URL 빌딩 패턴 완전 일관화 (undefined/null/'' 자동 필터링, boolean/number 자동 변환)
### KST 안전 날짜 유틸리티 — `toISOString` 사용 금지 (2026-02-19)
**현황**: `new Date().toISOString().split('T')[0]` — 15개 파일 26곳에서 사용 중이었음
**문제**: `toISOString()`**UTC 기준**으로 변환. 한국(KST, UTC+9)에서 오전 9시 이전에 실행하면 **전날 날짜** 반환
```
// 2026-02-19 08:30 KST → UTC는 2026-02-18 23:30
new Date().toISOString().split('T')[0] // "2026-02-18" ← 잘못됨
```
**결정**: KST 안전 유틸리티 함수로 전량 교체, 직접 `toISOString` 사용 금지
**유틸리티** (`src/lib/utils/date.ts`):
| 함수 | 용도 | 대체 대상 |
|------|------|-----------|
| `getTodayString()` | 오늘 날짜 문자열 | `new Date().toISOString().split('T')[0]` |
| `getLocalDateString(date)` | 임의 Date 객체 문자열 | `someDate.toISOString().split('T')[0]` |
**사용 규칙**:
```typescript
// ✅ 올바른 패턴
import { getTodayString, getLocalDateString } from '@/lib/utils/date';
const today = getTodayString(); // "2026-02-19"
const thirtyDaysAgo = getLocalDateString(pastDate); // "2026-01-20"
// ❌ 금지 패턴
const today = new Date().toISOString().split('T')[0];
```
**현재 상태**: `src/``toISOString().split` 사용 0건 (date.ts 내 구현부 제외)
### `useDateRange` 훅 — 날짜 필터 보일러플레이트 제거 (2026-02-19)
**현황**: 20+ 리스트 페이지에서 `useState('2025-01-01')` / `useState('2025-12-31')` 하드코딩
**문제**: 연도가 바뀌면 수동으로 모든 파일 수정 필요 (2025→2026 전환 시 데이터 미표시 버그 발생)
**결정**: `useDateRange` 훅으로 동적 날짜 범위 자동 계산
**훅** (`src/hooks/useDateRange.ts`):
```typescript
import { useDateRange } from '@/hooks';
// 프리셋
const { startDate, endDate, setStartDate, setEndDate } = useDateRange('currentYear'); // 2026-01-01 ~ 2026-12-31
const { startDate, endDate, setStartDate, setEndDate } = useDateRange('currentMonth'); // 2026-02-01 ~ 2026-02-28
const { startDate, endDate, setStartDate, setEndDate } = useDateRange('today'); // 2026-02-19 ~ 2026-02-19
```
**적용 규칙**:
- 리스트 페이지 날짜 필터 → `useDateRange` 필수 사용
- 연간 조회 → `'currentYear'`, 월간 조회 → `'currentMonth'`
- `useState('YYYY-MM-DD')` 하드코딩 금지
**현재 상태**: `useState('2025` 패턴 0건 (전량 `useDateRange`로 전환 완료)
### Zod 스키마 검증 — 신규 폼 적용 규칙 (2026-02-11)
**결정**: 기존 폼은 건드리지 않음. **신규 폼에만 Zod + zodResolver 적용**