diff --git a/claudedocs/_index.md b/claudedocs/_index.md index 24b283a7..a2ecd922 100644 --- a/claudedocs/_index.md +++ b/claudedocs/_index.md @@ -1,6 +1,6 @@ # claudedocs 문서 맵 -> 프로젝트 기술 문서 인덱스 (Last Updated: 2026-02-06) +> 프로젝트 기술 문서 인덱스 (Last Updated: 2026-02-09) ## 빠른 참조 @@ -36,6 +36,7 @@ claudedocs/ ├── material/ # 자재관리 ├── approval/ # 결재관리 ├── customer-center/ # 고객센터 +├── refactoring/ # 리팩토링 체크리스트 └── archive/ # 레거시/완료된 문서 ``` @@ -334,6 +335,14 @@ claudedocs/ --- +## 리팩토링 — `refactoring/` + +| 파일 | 설명 | +|------|------| +| `[IMPL-2026-02-09] phase1-common-hooks-checklist.md` | Phase 1 공통 훅 추출 체크리스트 (완료) + Phase 3 프로토타입 기록 | + +--- + ## archive/ - 레거시/완료된 문서 완료되거나 더 이상 활성화되지 않은 문서들. 참조용으로 보관. diff --git a/claudedocs/architecture/[PLAN-2026-02-06] refactoring-roadmap.md b/claudedocs/architecture/[PLAN-2026-02-06] refactoring-roadmap.md index b399dcd5..763a9c00 100644 --- a/claudedocs/architecture/[PLAN-2026-02-06] refactoring-roadmap.md +++ b/claudedocs/architecture/[PLAN-2026-02-06] refactoring-roadmap.md @@ -2,7 +2,7 @@ **작성일**: 2026-02-06 **목적**: 전체 코드베이스 리팩토링 포인트 점검 및 실행 계획 -**상태**: 분석 완료, 실행 대기 +**상태**: Phase 1 완료, Phase 3 프로토타입 검증 완료 --- @@ -216,24 +216,24 @@ useEffect(() => { ## 실행 계획 -### Phase 1: 공통 훅 추출 (1-2주) `프론트 단독` `ROI 최고` +### Phase 1: 공통 훅 추출 ✅ 완료 (2026-02-09) -> 새 훅 생성 + 고빈도 페이지 20개 우선 적용 +> 실제 코드 분석 결과 계획 수정 → 실증 기반 리팩토링 실행 -**작업 항목**: +**실행 결과** (계획 vs 실제): ``` -- [ ] useListData 훅 생성 (페칭 + 페이지네이션 + 필터 통합) -- [ ] useFormSubmit 훅 생성 (제출 + 로딩 + 에러 + 토스트) -- [ ] usePagination 훅 생성 -- [ ] useModal 훅 생성 -- [ ] 고빈도 리스트 페이지 10개 마이그레이션 - - vendors, clients, items, orders, quotes - - production, quality, material, hr, accounting 각 1개 -- [ ] 고빈도 폼 페이지 10개 마이그레이션 - - 위 도메인의 등록/수정 폼 각 1개 +기존 계획의 useListData, usePagination, useClientSideFiltering, useModal은 +UniversalListPage 템플릿이 이미 내부 처리 → 불필요 판정. + +실제 실행: +- [x] Step 1: executeServerAction (82개 action.ts 에러처리 래퍼) → ~3,000줄 절감 +- [x] Step 2: useDeleteDialog (6개 파일 삭제 다이얼로그 통합) → ~150줄 절감 +- [x] Step 3: useStatsLoader (7개 파일 stats 로딩 통합) → ~100줄 절감 +- [x] Step 4: React.memo 3개 + any→unknown 7건 + @ts-ignore 0건 ``` -**예상 효과**: ~8,500줄 절감, 패턴 일관성 +60% +**실제 효과**: ~3,750줄 절감, 82개 action.ts 패턴 통일, 타입 안전성 향상 +**상세**: `refactoring/[IMPL-2026-02-09] phase1-common-hooks-checklist.md` --- @@ -263,27 +263,32 @@ useEffect(() => { --- -### Phase 3: 액션 파일 제네릭화 (2-3주) `프론트 단독` `최대 코드 감소` +### Phase 3: 액션 파일 제네릭화 (2-3주) `프론트 단독` `프로토타입 검증 완료` -> CRUD 서비스 팩토리 생성 + 80개 actions.ts 점진적 마이그레이션 +> CRUD 서비스 팩토리 생성 + actions.ts 점진적 마이그레이션 -**작업 항목**: +**프로토타입 결과** (2026-02-09): ``` -- [ ] createCrudService 팩토리 함수 구현 - - getList, getById, create, update, delete, bulkDelete - - transform / reverseTransform 자동 적용 - - 에러 핸들링 표준화 -- [ ] 공통 transform 유틸리티 (lib/transformers/) - - API snake_case → 프론트 camelCase 자동 변환 - - 날짜 문자열 파싱 공통화 -- [ ] 고빈도 도메인 마이그레이션 (10개) - - orders, quotes, clients, vendors - - production, quality, material - - hr/employee, hr/vacation, accounting -- [ ] 나머지 70개 점진적 마이그레이션 +- [x] createCrudService 팩토리 구현 (src/lib/api/create-crud-service.ts) +- [x] RankManagement/actions.ts 마이그레이션 (111줄 → 77줄, -31%) +- [x] Server Action 호환성 검증 완료 (5/5 CRUD 정상 동작) +- [x] 래퍼 함수 방식 채택 (Next.js Server Action 인식 보장) ``` -**예상 효과**: ~24,000줄 → ~8,000줄 (67% 감소) +**검증된 사실**: +- Server Action + 팩토리 패턴 호환성 문제 없음 +- 래퍼 함수 필요 (직접 re-export는 미검증) +- Tier 분류: Tier 1 정형 CRUD (~60%, 100% 적용) / Tier 2 CRUD+특수 (~25%, 부분 적용) / Tier 3 복잡 도메인 (~15%, 미적용) + +**남은 작업**: +``` +- [ ] Tier 1 settings 도메인 마이그레이션 (8개) +- [ ] Tier 1 기타 정형 CRUD 마이그레이션 (~40개) +- [ ] Tier 2 CRUD 부분 팩토리 적용 (~20개) +- [ ] PositionApiData 등 공통 API 타입 추출 +``` + +**예상 효과**: Tier 1 기준 ~31% 코드 감소, 새 도메인 추가 시간 30분→5분 --- @@ -310,26 +315,29 @@ useEffect(() => { --- -### Phase 5: 성능 + 타입 정리 (1-2주) `프론트 단독` +### Phase 5: 성능 + 타입 정리 (1-2주) `프론트 단독` `일부 Phase 1에서 선처리` > React.memo 적용 + any 제거 + 타입 통합 -**작업 항목**: +**Phase 1에서 선처리된 항목** (2026-02-09): ``` -- [ ] React.memo 적용 (리스트 아이템 컴포넌트 30+개) - - WorkItemCard, CommentItem, ProjectCard 등 - - *Row, *Item, *Card 패턴 전수 조사 +- [x] React.memo 3개 적용 (InfoField, CommentItem, WorkItemCard) +- [x] any→unknown 7건 (logger.ts) +- [x] action error handler any 50+곳 (executeServerAction으로 자동 해결) +- [x] @ts-ignore 0건 (이미 제거 완료) +``` + +**남은 작업**: +``` +- [ ] React.memo 추가 적용 (나머지 리스트 아이템 컴포넌트) - [ ] 대형 컴포넌트 useCallback 적용 - - MainDashboard, WorkerScreen, DynamicItemForm -- [ ] any 타입 제거 (102곳) - - Phase 1: types/ 파일 (4개) - - Phase 2: action error handler (50+ 파일) - - Phase 3: 컴포넌트 props (20개) +- [ ] any 타입 잔여 92건 + - items/ 도메인 60건 (복잡도 높음, 별도 작업) + - Form 에러 캐스팅 26건 (RHF 타입 시스템 변경 필요) + - dev/ 프로토타입 6건 (비프로덕션) - [ ] 공통 타입 라이브러리 정리 - types/shared/ 폴더 생성 - ApiResponse, PaginatedResponse, FormState 등 - - 엔티티별 정규 타입 단일화 -- [ ] @ts-ignore / eslint-disable 제거 (25개 파일) ``` **예상 효과**: 리스트 렌더링 30-50% 개선, 타입 안전성 +60% @@ -338,27 +346,27 @@ useEffect(() => { ## 전체 예상 효과 요약 -| 지표 | Phase 1 | Phase 2 | Phase 3 | Phase 4 | Phase 5 | 합계 | -|------|---------|---------|---------|---------|---------|------| -| 코드 절감 | ~8,500줄 | (구조 개선) | ~16,000줄 | ~5,000줄 | (품질 개선) | **~29,500줄** | +| 지표 | Phase 1 ✅ | Phase 2 | Phase 3 | Phase 4 | Phase 5 | 합계 | +|------|-----------|---------|---------|---------|---------|------| +| 코드 절감 | ~3,750줄 (실측) | (구조 개선) | ~3,300줄 (실측 기반 추정) | ~5,000줄 | (품질 개선) | **~12,000줄+** | | 패턴 일관성 | +60% | +50% | +40% | +80% | +60% | 종합 개선 | | 유지보수성 | 높음 | 매우 높음 | 높음 | 중간 | 중간 | 종합 개선 | -| 위험도 | 낮음 | 중간 | 중간 | 낮음 | 낮음 | - | +| 위험도 | 낮음 | 중간 | 낮음 (검증됨) | 낮음 | 낮음 | - | --- ## 병렬 진행 가능 조합 ``` -[독립 진행 가능] -├─ Phase 1 (공통 훅) ──→ 즉시 시작 -├─ Phase 5 (성능/타입) ─→ 즉시 시작 (Phase 1과 병렬) +[완료] +├─ Phase 1 (공통 훅) ──→ ✅ 완료 (2026-02-09) │ -[Phase 1 완료 후] -├─ Phase 2 (God 컴포넌트 분리) ──→ 훅 활용하여 분리 -├─ Phase 3 (액션 제네릭화) ────→ 독립 진행 가능 +[즉시 시작 가능] +├─ Phase 2 (God 컴포넌트 분리) ──→ Phase 1 훅 활용 +├─ Phase 3 (액션 제네릭화) ────→ 프로토타입 검증 완료, 본격 마이그레이션 가능 +├─ Phase 5 (성능/타입) ─────→ 일부 Phase 1에서 선처리됨 │ -[Phase 1+3 완료 후] +[Phase 3 완료 후] └─ Phase 4 (템플릿 통일) ─────→ 훅 + 서비스 활용 ``` @@ -383,6 +391,8 @@ useEffect(() => { | 날짜 | 변경 내용 | |------|-----------| | 2026-02-06 | 초기 작성 - 전체 코드베이스 분석 기반 5 Phase 로드맵 | +| 2026-02-09 | Phase 1 완료 반영 - 실측 기반 효과 수치 보정 (8,500줄→3,750줄), executeServerAction/useDeleteDialog/useStatsLoader 3개 훅 생성 완료 | +| 2026-02-09 | Phase 3 프로토타입 검증 완료 - createCrudService 팩토리 생성, RankManagement 5/5 CRUD 정상, Server Action 호환성 확인 | --- diff --git a/claudedocs/refactoring/[IMPL-2026-02-09] phase1-common-hooks-checklist.md b/claudedocs/refactoring/[IMPL-2026-02-09] phase1-common-hooks-checklist.md index bf04861c..229ef6db 100644 --- a/claudedocs/refactoring/[IMPL-2026-02-09] phase1-common-hooks-checklist.md +++ b/claudedocs/refactoring/[IMPL-2026-02-09] phase1-common-hooks-checklist.md @@ -285,3 +285,6 @@ const { data: stats } = useStatsLoader(getProcessStats, { total: 0, active: 0, i | 2026-02-09 | **Step 3 완료** - useStatsLoader 훅 생성(45줄) 및 7개 파일 적용 (3개 스킵). Construction 패턴(5개: Contract, ConstructionManagement, Bidding, Estimate, ProgressBilling) - useState+useEffect→useStatsLoader 1줄 전환. Standard 패턴(2개: InspectionList, ShipmentList) - reload 함수 활용하여 getList 내 stats 리로드 단순화. 스킵: ProcessListClient(stats UI 미사용), WorkOrderList(tabCounts 동기화 복잡), OrderManagementListClient(stats 완전 미사용). 타입체크 0 에러. | | 2026-02-09 | **Step 4 완료** - React.memo 3개 적용(InfoField, CommentItem, WorkItemCard - 리스트 반복 렌더링 대상). any→unknown 7건(logger.ts). @ts-ignore 0건(이미 제거됨). 잔여 any 92건은 items/ 도메인(60건, 복잡도 높음), dev/ 프로토타입(6건), Form 에러캐스팅(26건, RHF 타입 변경 필요)으로 별도 작업 필요. | | 2026-02-09 | **Phase 1 전체 완료** - Step 1(executeServerAction 82개 파일) + Step 2(useDeleteDialog 6개 파일) + Step 3(useStatsLoader 7개 파일) + Step 4(React.memo 3개 + any→unknown 7건 + @ts-ignore 0건). | +| 2026-02-09 | **Phase 1 자동 검증 통과** - TypeScript 0 에러(기존 QuoteRegistrationV2만), 미사용 import 0건, executeServerAction 59파일 일관 패턴, 핵심 훅 3개 무결성 확인. | +| 2026-02-09 | **Phase 1 수동 검증 통과** - 화면 버디 검수 완료. | +| 2026-02-09 | **Phase 3 프로토타입 검증** - `createCrudService` 팩토리 생성(`src/lib/api/create-crud-service.ts`). RankManagement/actions.ts 마이그레이션(111줄→77줄). 직급관리 5/5 CRUD 정상 동작 확인. Server Action 호환성 검증 완료. | diff --git a/src/components/process-management/ProcessDetail.tsx b/src/components/process-management/ProcessDetail.tsx index c2bab488..6aaf7bcf 100644 --- a/src/components/process-management/ProcessDetail.tsx +++ b/src/components/process-management/ProcessDetail.tsx @@ -20,7 +20,9 @@ import { PageHeader } from '@/components/organisms/PageHeader'; import { useMenuStore } from '@/store/menuStore'; import { usePermission } from '@/hooks/usePermission'; import { toast } from 'sonner'; -import { getProcessSteps, reorderProcessSteps, removeProcessItem } from './actions'; +import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog'; +import { useDeleteDialog } from '@/hooks/useDeleteDialog'; +import { getProcessSteps, reorderProcessSteps, removeProcessItem, deleteProcess } from './actions'; import type { Process, ProcessStep } from '@/types/process'; interface ProcessDetailProps { @@ -33,6 +35,13 @@ export function ProcessDetail({ process, onProcessUpdate }: ProcessDetailProps) const sidebarCollapsed = useMenuStore((state) => state.sidebarCollapsed); const { canUpdate } = usePermission(); + // 삭제 다이얼로그 + const deleteDialog = useDeleteDialog({ + onDelete: deleteProcess, + onSuccess: () => router.push('/ko/master-data/process-management'), + entityName: '공정', + }); + // 단계 목록 상태 const [steps, setSteps] = useState([]); const [isStepsLoading, setIsStepsLoading] = useState(true); @@ -383,12 +392,32 @@ export function ProcessDetail({ process, onProcessUpdate }: ProcessDetailProps) 목록으로 {canUpdate && ( - +
+ + +
)} + + {/* 삭제 확인 다이얼로그 */} + ); } diff --git a/src/components/process-management/actions.ts b/src/components/process-management/actions.ts index ba1645e3..14c1bcaa 100644 --- a/src/components/process-management/actions.ts +++ b/src/components/process-management/actions.ts @@ -16,6 +16,9 @@ interface ApiProcess { description: string | null; process_type: string; department: string | null; + manager: string | null; + process_category: string | null; + use_production_date: boolean; work_log_template: string | null; required_workers: number; equipment_info: string | null; @@ -88,6 +91,9 @@ function transformApiToFrontend(apiData: ApiProcess): Process { description: apiData.description ?? undefined, processType: apiData.process_type as Process['processType'], department: apiData.department ?? '', + manager: apiData.manager ?? undefined, + processCategory: apiData.process_category ?? undefined, + useProductionDate: apiData.use_production_date ?? false, workLogTemplate: apiData.work_log_template ?? undefined, classificationRules: [...patternRules, ...individualRules], requiredWorkers: apiData.required_workers, @@ -176,6 +182,9 @@ function transformFrontendToApi(data: ProcessFormData): Record process_name: data.processName, process_type: data.processType, department: data.department || null, + manager: data.manager || null, + process_category: data.processCategory || null, + use_production_date: data.useProductionDate ?? false, work_log_template: data.workLogTemplate || null, required_workers: data.requiredWorkers, equipment_info: data.equipmentInfo || null, diff --git a/src/types/process.ts b/src/types/process.ts index f6acfdab..f7f54920 100644 --- a/src/types/process.ts +++ b/src/types/process.ts @@ -70,16 +70,16 @@ export interface Process { // 설명 note?: string; - // 담당자 (신규 필드 - 백엔드 미준비) + // 담당자 manager?: string; - // 생산일자 사용여부 (신규 필드 - 백엔드 미준비) + // 생산일자 사용여부 useProductionDate?: boolean; - // 구분 (신규 필드 - 공정명에 따라 옵션 변경) + // 구분 (공정명에 따라 옵션 변경) processCategory?: string; - // 단계 목록 (신규 필드 - 백엔드 미준비) + // 단계 목록 steps?: ProcessStep[]; // 상태 @@ -95,6 +95,9 @@ export interface ProcessFormData { processName: string; processType: ProcessType; department: string; + manager?: string; + processCategory?: string; + useProductionDate?: boolean; workLogTemplate?: string; classificationRules: ClassificationRuleInput[]; requiredWorkers: number;