# 리팩토링 로드맵

**작성일**: 2026-02-06
**목적**: 전체 코드베이스 리팩토링 포인트 점검 및 실행 계획
**상태**: Phase 1 완료, Phase 3 완료 (공용 유틸 추출), Phase 4 SearchableSelectionModal 완료

---

## 현재 코드베이스 수치 (2026-02-06 기준)

| 지표 | 수치 | 비고 |
|------|------|------|
| 전체 코드 | ~301,000줄 | TS/TSX |
| 컴포넌트 파일 | ~551개 | |
| 페이지 파일 | ~253개 | |
| action.ts 파일 | 80개 | 거의 동일 CRUD 패턴 |
| types.ts 파일 | 94개 | 중복 타입 다수 |
| 모달 컴포넌트 | 42개 | 유사 패턴 반복 |
| 2000줄+ 파일 | 4개 | God 컴포넌트 |
| 1000~2000줄 파일 | 25+개 | 분리 대상 |
| 500~1000줄 파일 | 50+개 | 검토 대상 |

---

## God 컴포넌트 / 대형 파일 목록

### 🔴 2000줄 이상 (즉시 분리 필요)

| 파일 | 줄수 | 핵심 문제 | 분리 방향 |
|------|------|-----------|-----------|
| `components/business/MainDashboard.tsx` | 2,651 | CEO/영업/생산/품질 대시보드 한 파일 | 역할별 섹션 컴포넌트 분리 |
| `contexts/ItemMasterContext.tsx` | 2,406 | useState 17개, useEffect 15개, 함수 50+개 | 도메인별 5개 Context 분리 |
| `lib/api/item-master.ts` | 2,232 | 모든 품목 API 한 파일 | 도메인별 API 모듈 분리 |
| `lib/api/dashboard/transformers.ts` | 1,576 | 전체 대시보드 변환 로직 | 섹션별 transformer 분리 |

### 🟡 1000~2000줄 (우선 검토)

| 파일 | 줄수 | 도메인 | 분리 방향 |
|------|------|--------|-----------|
| `components/orders/actions.ts` | 1,394 | 수주 | 서비스 레이어 분리 |
| `components/accounting/ExpectedExpenseManagement/index.tsx` | 1,299 | 회계 | 서브 컴포넌트 추출 |
| `layouts/AuthenticatedLayout.tsx` | 1,289 | 레이아웃 | 훅 24개 → 섹션별 분리 |
| `components/quotes/QuoteRegistration.tsx` | 1,268 | 견적 | 폼 섹션 추출, useState 13개 |
| `components/quotes/actions.ts` | 1,266 | 견적 | API 레이어 분리 |
| `components/business/construction/management/actions.ts` | 1,222 | 건설 | 도메인 서비스 추출 |
| `components/business/construction/estimates/actions.ts` | 1,222 | 건설 | 도메인 서비스 추출 |
| `components/production/WorkerScreen/index.tsx` | 1,198 | 생산 | 화면 섹션 분리 |
| `hooks/useCEODashboard.ts` | 1,172 | 대시보드 | useState 18개 → 섹션별 훅 분리 |
| `components/material/ReceivingManagement/actions.ts` | 1,152 | 자재 | API 서비스 레이어 |
| `components/quotes/types.ts` | 1,149 | 견적 | 타입 조직화 |
| `components/quality/InspectionManagement/InspectionDetail.tsx` | 1,125 | 품질 | 컴포넌트 추출 |
| `components/hr/VacationManagement/actions.ts` | 1,125 | HR | 서비스 레이어 분리 |
| `components/orders/OrderRegistration.tsx` | 1,123 | 수주 | 폼 섹션 추출, useState 12개 |
| `components/items/DynamicItemForm/index.tsx` | 1,073 | 품목 | 복합 폼 로직 추출 |
| `components/templates/IntegratedListTemplateV2.tsx` | 1,066 | 템플릿 | 템플릿 특화 |
| `components/hr/EmployeeManagement/EmployeeForm.tsx` | 1,051 | HR | 폼 섹션 분리 |
| `components/quotes/QuoteRegistrationV2.tsx` | 1,020 | 견적 | 폼 리팩토링 |
| `components/templates/UniversalListPage/index.tsx` | 1,007 | 템플릿 | 템플릿 최적화 |
| `components/items/ItemMasterDataManagement.tsx` | 1,005 | 품목 | 도메인 로직 추출 |

---

## 중복 패턴 분석

### 1. 액션 파일 80개 동일 패턴 (~24,000줄 중복)

**현재**: 모든 도메인이 이 구조를 복붙
```typescript
'use server';
import { serverFetch } from '@/lib/api/fetch-wrapper';

interface Api[Domain]Data { ... }         // 타입 정의 100~300줄
function transform(data) { ... }          // API→프론트 변환 50~100줄

export async function getList(params) {   // 목록 조회
  const url = `${API_URL}/api/v1/endpoint`;
  const { response } = await serverFetch(url, { method: 'GET' });
  return transform(response);
}
export async function getById(id) { ... } // 상세 조회
export async function create(data) { ... } // 생성
export async function update(id, data) { ... } // 수정
export async function delete(id) { ... }  // 삭제
export async function bulkDelete(ids) { ... } // 일괄 삭제
```

**해당 도메인**: orders, quotes, clients, accounting(13모듈), hr(6모듈), production(4모듈), material(2모듈), quality(2모듈), construction(17모듈), settings(14모듈)

**해결 방향**: 제네릭 API 서비스 팩토리
```typescript
// lib/api/createCrudService.ts
function createCrudService<TApi, TFront>(config: {
  endpoint: string;
  transform: (api: TApi) => TFront;
  reverseTransform: (front: TFront) => Partial<TApi>;
}) {
  return {
    getList: async (params) => { ... },
    getById: async (id) => { ... },
    create: async (data) => { ... },
    update: async (id, data) => { ... },
    delete: async (id) => { ... },
    bulkDelete: async (ids) => { ... },
  };
}

// 사용: 10줄로 끝
const orderService = createCrudService<ApiOrder, Order>({
  endpoint: 'orders',
  transform: transformOrder,
  reverseTransform: reverseTransformOrder,
});
```

### 2. 데이터 페칭 패턴 3가지 혼재

| 패턴 | 사용 비율 | 위치 |
|------|-----------|------|
| useEffect + .then() 직접 호출 | ~75% (99+ 컴포넌트) | 대부분의 도메인 |
| 커스텀 훅 (useDetailData 등) | ~15% (~15 컴포넌트) | 신규 구현 |
| ApiClient 클래스 | ~10% (15 컴포넌트) | 건설 도메인만 |

**수동 로딩 상태 관리**: 262곳에서 반복
```typescript
// 이 패턴이 262번 반복됨
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  setIsLoading(true);
  fetchData()
    .then(result => setData(result))
    .catch(err => setError(err))
    .finally(() => setIsLoading(false));
}, []);
```

### 3. 폼 검증 3가지 방식 혼재

| 방식 | 사용 파일 수 | 비율 |
|------|-------------|------|
| Zod 스키마 (정석) | 3개 (로그인, 회원가입, 품목) | 5% |
| 수동 if문 검증 | 50+개 | 60% |
| 검증 없음 | ~30개 | 35% |

### 4. 리스트 페이지 템플릿 이중화

| 방식 | 사용 | 비율 |
|------|------|------|
| `UniversalListPage` (신규 표준) | 20개 페이지 | 25% |
| 수동 구현 (레거시) | 60+ 페이지 | 75% |

### 5. 모달/다이얼로그 42개 유사 패턴

**검색/선택 모달 5개+ 거의 동일**:
- `quotes/ItemSearchModal.tsx`
- `production/WorkOrders/AssigneeSelectModal.tsx`
- `material/ReceivingManagement/SupplierSearchModal.tsx`
- `quality/InspectionManagement/OrderSelectModal.tsx`
- `production/WorkOrders/SalesOrderSelectModal.tsx`

전부 "검색 입력 → API 호출 → 목록 표시 → 체크박스 선택 → 확인" 동일 구조
→ `SearchableSelectionModal<T>` 하나로 통합 가능

---

## 성능 최적화 포인트

| 항목 | 현재 상태 | 영향도 | 해결 방향 |
|------|-----------|--------|-----------|
| React.memo | 551개 컴포넌트 중 **1개만** 사용 | 🔴 높음 | 리스트 아이템/카드 컴포넌트에 적용 |
| 인라인 화살표 함수 | **746곳** `onClick={() => ...}` | 🟡 중간 | 대형 컴포넌트에서 useCallback 적용 |
| useMemo 미사용 | 대용량 배열 필터링/정렬 곳곳 | 🟡 중간 | 비용 높은 계산에 적용 |

**React.memo 우선 적용 대상** (리스트 내 반복 렌더링 컴포넌트):
- `production/WorkerScreen/WorkItemCard.tsx`
- `board/CommentSection/CommentItem.tsx`
- `business/construction/management/ProjectCard.tsx`
- 기타 *Row, *Item, *Card 컴포넌트 30+개

---

## 타입 시스템 문제

| 항목 | 수치 | 영향 |
|------|------|------|
| `any` 타입 사용 | 102곳 (29개 파일) | 타입 안전성 저하 |
| 동일 엔티티 다중 타입 정의 | Vendor, Item, Order 등 | 변환 코드 ~800줄 중복 |
| types.ts 파일 | 94개 | 정규 타입 찾기 어려움 |
| @ts-ignore/eslint-disable | 25개 파일 | 숨겨진 타입 에러 |

---

## 추출 가능한 공통 훅 목록

### 즉시 생성 가능 (프론트 단독)

| 훅 이름 | 대체 범위 | 예상 절감 | 기존 참고 |
|---------|-----------|-----------|-----------|
| `useListData` | 60+ 리스트 페이지 | ~4,000줄 | hooks/useDetailData.ts 패턴 확장 |
| `useFormSubmit` | 80+ 폼 | ~3,000줄 | 신규 |
| `usePagination` | 60+ 컴포넌트 | ~1,000줄 | 신규 |
| `useModal<T>` | 42 모달 | ~500줄 | 신규 |
| `useClientSideFiltering` | 55+ 컴포넌트 | ~800줄 | 신규 |

### 기존 훅 (활용 확대 필요)

| 훅 | 현재 사용 | 전체 적용 시 |
|----|-----------|-------------|
| `useDetailData` | ~15 컴포넌트 | 100+ 상세 페이지 |
| `useDetailPageState` | ~10 컴포넌트 | 100+ 상세 페이지 |
| `useCRUDHandlers` | ~10 컴포넌트 | 80+ CRUD 페이지 |

---

## 실행 계획

### Phase 1: 공통 훅 추출 ✅ 완료 (2026-02-09)

> 실제 코드 분석 결과 계획 수정 → 실증 기반 리팩토링 실행

**실행 결과** (계획 vs 실제):
```
기존 계획의 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건
```

**실제 효과**: ~3,750줄 절감, 82개 action.ts 패턴 통일, 타입 안전성 향상
**상세**: `refactoring/[IMPL-2026-02-09] phase1-common-hooks-checklist.md`

---

### Phase 2: God 컴포넌트 분리 (2-3주) `프론트 단독`

> 2000줄+ 파일 4개 + 핵심 1000줄+ 파일 우선 분리

**작업 항목**:
```
- [ ] MainDashboard.tsx (2,651줄) 분리
      → sections/CEOSection, SalesSection, ProductionSection, QualitySection
      → hooks/useDashboardData
      → utils/calculations
- [ ] ItemMasterContext.tsx (2,406줄) 분리
      → ItemContext, SpecificationContext, MaterialContext
      → TemplateContext, AttributeContext
- [ ] useCEODashboard.ts (1,172줄) 분리
      → useDailyReport, useReceivables, useMonthlyExpense 등 개별 훅
      → 훅 팩토리 패턴 적용
- [ ] lib/api/item-master.ts (2,232줄) 분리
      → 도메인별 API 모듈 (items, specifications, materials, templates)
- [ ] AuthenticatedLayout.tsx (1,289줄)
      → useLayoutState, useNavigation, useTenantBranding 훅 추출
```

**예상 효과**: 유지보수성 +50%, 단위 테스트 가능성 확보

---

### Phase 3: 액션 파일 공용 유틸 추출 ✅ 완료 (2026-02-10)

> 전수 분석 → 팩토리 ROI 재평가 → 공용 유틸 추출로 전략 변경

**전수 분석 결과** (82개 action 파일):
```
- 35개: executeServerAction 패턴 (Phase 1에서 통일)
- 15개: 모의 데이터 (mock, API 미연동)
- 13개: ApiClient 클래스 패턴 (건설 도메인)
- 나머지: 특수 도메인 로직 (견적, 수주, 품목 등)
```

**팩토리 마이그레이션 ROI 재평가**:
```
- createCrudService 팩토리: 2개(Rank, Title)만 적합 → ROI ~6% (너무 낮음)
- 대부분 파일: 페이지네이션, 커스텀 쿼리 파라미터, 도메인 특화 로직으로 팩토리 패턴 부적합
- 결론: 팩토리 대량 마이그레이션 대신 공용 유틸 추출로 전략 전환
```

**실행 결과** (2026-02-10):
```
Step 1: 공용 타입 추출 (src/lib/api/types.ts)
  - [x] PaginatedApiResponse<T> — 25+ 파일에서 중복 정의 제거
  - [x] PaginationMeta, PaginatedResult<T> — 프론트엔드 표준 페이지네이션 타입
  - [x] toPaginationMeta() — snake_case → camelCase 변환 헬퍼
  - [x] SelectOption — 공용 선택 옵션 타입

Step 2: 공용 룩업 헬퍼 추출 (src/lib/api/shared-lookups.ts)
  - [x] fetchVendorOptions() — 거래처 목록 조회 (4개 파일 중복 제거)
  - [x] fetchBankAccountOptions() — 계좌 목록 조회 (심플)
  - [x] fetchBankAccountDetailOptions() — 계좌 상세 조회 (bankName, accountNumber 포함)
  - [x] BankAccountOption 타입

Step 3: PaginatedResponse 타입 마이그레이션 (~20개 파일)
  - [x] 제네릭 패턴 (interface PaginatedResponse<T>) → import PaginatedApiResponse
  - [x] 도메인 패턴 (interface XxxPaginatedResponse) → type alias
  - 스킵: VendorManagement/types.ts (page?/size? 비표준), PermissionManagement/types.ts (meta 래퍼)

Step 4: 공용 룩업 헬퍼 마이그레이션 (4개 파일)
  - [x] DepositManagement/actions.ts — getVendors + getBankAccounts 교체
  - [x] WithdrawalManagement/actions.ts — getVendors + getBankAccounts 교체
  - [x] PurchaseManagement/actions.ts — getVendors + getBankAccounts(상세) 교체
  - [x] ExpectedExpenseManagement/actions.ts — getBankAccounts(상세) 교체

Step 5: TypeScript 검증 통과 ✅
```

**실측 효과**:
- PaginatedResponse 중복 제거: ~20개 파일, 파일당 ~7줄 = ~140줄 절감
- 공용 룩업 헬퍼: 4개 파일, 파일당 ~20줄 = ~80줄 절감
- 총 ~220줄 직접 절감 + 향후 새 파일에서 중복 방지
- createCrudService + TitleManagement 마이그레이션: ~36줄 절감 (프로토타입 포함)

**생성된 공용 파일**:
- `src/lib/api/types.ts` — 공용 API 타입 (PaginatedApiResponse, PaginationMeta 등)
- `src/lib/api/shared-lookups.ts` — 공용 룩업 헬퍼 (fetchVendorOptions 등)
- `src/lib/api/create-crud-service.ts` — CRUD 팩토리 (Rank, Title 2개 사용)

---

### Phase 4: 템플릿/패턴 통일 (2-3주) `프론트 단독` `SearchableSelectionModal 완료`

> UniversalListPage 확대 + 검증 표준화 + 모달 통합

**SearchableSelectionModal 완료** (2026-02-10):
```
- [x] SearchableSelectionModal<T> 공통 컴포넌트 생성
      - types.ts, useSearchableData.ts, SearchableSelectionModal.tsx, index.ts
      - 단일선택(single) + 다중선택(multiple) + listWrapper(테이블용) 지원
- [x] ItemSearchModal 교체 (212→113줄, -47%)
- [x] SupplierSearchModal 교체 (268→161줄, -40%)
- [x] SalesOrderSelectModal 교체 (163→101줄, -38%)
- [x] QuotationSelectDialog 교체 (196→113줄, -42%)
- [x] OrderSelectModal 교체 (220→107줄, -51%)
- [x] organisms/index.ts export 추가
- [x] CLAUDE.md 공통 컴포넌트 사용 규칙 + claudedocs 가이드 문서 작성
```
**실측 효과**: 1,059줄 → 595줄 (464줄 절감, -44%) + 공통 컴포넌트 ~430줄

**남은 작업**:
```
- [ ] UniversalListPage 기능 보강
      - 고급 필터 UI
      - 컬럼 커스터마이징
      - 내보내기 기능
- [ ] 레거시 리스트 페이지 → UniversalListPage 마이그레이션 (우선 20개)
- [ ] Zod 검증 스키마 라이브러리 구축
      - lib/validations/common.ts (이메일, 전화, 사업자번호)
      - lib/validations/vendor.ts, order.ts, item.ts 등
- [ ] 수동 검증 50+ 폼 → Zod 마이그레이션 (우선 10개)
```

**예상 효과**: ~5,000줄 절감 (SearchableSelectionModal ~464줄 달성), UX 일관성 +80%

---

### Phase 5: 성능 + 타입 정리 (1-2주) `프론트 단독` `일부 Phase 1에서 선처리`

> React.memo 적용 + any 제거 + 타입 통합

**Phase 1에서 선처리된 항목** (2026-02-09):
```
- [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 적용
- [ ] any 타입 잔여 92건
      - items/ 도메인 60건 (복잡도 높음, 별도 작업)
      - Form 에러 캐스팅 26건 (RHF 타입 시스템 변경 필요)
      - dev/ 프로토타입 6건 (비프로덕션)
- [ ] 공통 타입 라이브러리 정리
      - types/shared/ 폴더 생성
      - PaginatedApiResponse<T> ✅ Phase 3에서 완료 (src/lib/api/types.ts)
      - FormState<T>, SelectOption 등 추가 타입
```

**예상 효과**: 리스트 렌더링 30-50% 개선, 타입 안전성 +60%

---

## 전체 예상 효과 요약

| 지표 | Phase 1 ✅ | Phase 2 | Phase 3 ✅ | Phase 4 | Phase 5 | 합계 |
|------|-----------|---------|---------|---------|---------|------|
| 코드 절감 | ~3,750줄 (실측) | (구조 개선) | ~256줄 (실측) | ~5,000줄 | (품질 개선) | **~9,000줄+** |
| 중복 제거 | 82개 action 통일 | - | 25+ 타입 + 4 룩업 통합 | 5 모달 통합 | - | 종합 개선 |
| 패턴 일관성 | +60% | +50% | +30% (타입 표준화) | +80% | +60% | 종합 개선 |
| 유지보수성 | 높음 | 매우 높음 | 중간 (공용 유틸) | 중간 | 중간 | 종합 개선 |
| 위험도 | 낮음 | 중간 | 낮음 (완료) | 낮음 | 낮음 | - |

---

## 병렬 진행 가능 조합

```
[완료]
├─ Phase 1 (공통 훅) ──────→ ✅ 완료 (2026-02-09)
├─ Phase 3 (공용 유틸 추출) ──→ ✅ 완료 (2026-02-10)
├─ Phase 4 (SearchableSelectionModal) → ✅ 완료 (2026-02-10)
│
[즉시 시작 가능]
├─ Phase 2 (God 컴포넌트 분리) ──→ Phase 1 훅 + Phase 3 공용 타입 활용
├─ Phase 4 남은 작업 (UniversalListPage 확대, Zod 검증)
├─ Phase 5 (성능/타입) ─────→ 일부 Phase 1/3에서 선처리됨
```

---

## 관련 문서

| 문서 | 설명 |
|------|------|
| `[PLAN-2026-02-06] multi-tenancy-optimization-roadmap.md` | 멀티테넌시 공통화 로드맵 (별도 트랙) |
| `[ANALYSIS-2026-01-20] 공통화-현황-분석.md` | 공통화 현황 분석 |
| `[ANALYSIS-2026-02-05] list-page-commonization-status.md` | 리스트 페이지 공통화 현황 |
| `[IMPL-2026-02-05] detail-hooks-migration-plan.md` | 상세 페이지 훅 마이그레이션 계획 |
| `[IMPL-2026-02-05] formatter-commonization-plan.md` | 포매터 공통화 계획 |
| `[PLAN-2026-01-22] ui-component-abstraction.md` | UI 컴포넌트 추상화 계획 |
| `guides/[PLAN-2025-12-23] common-component-extraction-plan.md` | 공통 컴포넌트 추출 계획 |

---

## 변경 이력

| 날짜 | 변경 내용 |
|------|-----------|
| 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 호환성 확인 |
| 2026-02-10 | Phase 4 SearchableSelectionModal 완료 - 5개 모달 통합, 464줄 절감(-44%), 가이드 문서 작성 |
| 2026-02-10 | Phase 3 완료 - 전수 분석 후 팩토리 ROI 재평가(~6%), 공용 유틸 추출로 전략 전환. PaginatedApiResponse 25+파일 타입 통합, 공용 룩업 헬퍼 4파일 중복 제거, ~256줄 절감 |

---

**모든 Phase 프론트 단독 가능** - 백엔드 의존성 없음
