- quotes/actions.ts 중복 코드 제거 및 간소화 - quotes/types.ts 타입 확장 - claudedocs/_index.md 업데이트 - 멀티테넌시 최적화 로드맵 문서 추가 - 리팩토링 로드맵 문서 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
15 KiB
리팩토링 로드맵
작성일: 2026-02-06 목적: 전체 코드베이스 리팩토링 포인트 점검 및 실행 계획 상태: 분석 완료, 실행 대기
현재 코드베이스 수치 (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줄 중복)
현재: 모든 도메인이 이 구조를 복붙
'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 서비스 팩토리
// 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곳에서 반복
// 이 패턴이 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.tsxproduction/WorkOrders/AssigneeSelectModal.tsxmaterial/ReceivingManagement/SupplierSearchModal.tsxquality/InspectionManagement/OrderSelectModal.tsxproduction/WorkOrders/SalesOrderSelectModal.tsx
전부 "검색 입력 → API 호출 → 목록 표시 → 체크박스 선택 → 확인" 동일 구조
→ SearchableSelectionModal<T> 하나로 통합 가능
성능 최적화 포인트
| 항목 | 현재 상태 | 영향도 | 해결 방향 |
|---|---|---|---|
| React.memo | 551개 컴포넌트 중 1개만 사용 | 🔴 높음 | 리스트 아이템/카드 컴포넌트에 적용 |
| 인라인 화살표 함수 | 746곳 onClick={() => ...} |
🟡 중간 | 대형 컴포넌트에서 useCallback 적용 |
| useMemo 미사용 | 대용량 배열 필터링/정렬 곳곳 | 🟡 중간 | 비용 높은 계산에 적용 |
React.memo 우선 적용 대상 (리스트 내 반복 렌더링 컴포넌트):
production/WorkerScreen/WorkItemCard.tsxboard/CommentSection/CommentItem.tsxbusiness/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: 공통 훅 추출 (1-2주) 프론트 단독 ROI 최고
새 훅 생성 + 고빈도 페이지 20개 우선 적용
작업 항목:
- [ ] useListData 훅 생성 (페칭 + 페이지네이션 + 필터 통합)
- [ ] useFormSubmit 훅 생성 (제출 + 로딩 + 에러 + 토스트)
- [ ] usePagination 훅 생성
- [ ] useModal<T> 훅 생성
- [ ] 고빈도 리스트 페이지 10개 마이그레이션
- vendors, clients, items, orders, quotes
- production, quality, material, hr, accounting 각 1개
- [ ] 고빈도 폼 페이지 10개 마이그레이션
- 위 도메인의 등록/수정 폼 각 1개
예상 효과: ~8,500줄 절감, 패턴 일관성 +60%
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: 액션 파일 제네릭화 (2-3주) 프론트 단독 최대 코드 감소
CRUD 서비스 팩토리 생성 + 80개 actions.ts 점진적 마이그레이션
작업 항목:
- [ ] createCrudService<TApi, TFront> 팩토리 함수 구현
- 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개 점진적 마이그레이션
예상 효과: ~24,000줄 → ~8,000줄 (67% 감소)
Phase 4: 템플릿/패턴 통일 (2-3주) 프론트 단독
UniversalListPage 확대 + 검증 표준화 + 모달 통합
작업 항목:
- [ ] UniversalListPage 기능 보강
- 고급 필터 UI
- 컬럼 커스터마이징
- 내보내기 기능
- [ ] 레거시 리스트 페이지 → UniversalListPage 마이그레이션 (우선 20개)
- [ ] SearchableSelectionModal<T> 공통 컴포넌트 생성
- ItemSearchModal, AssigneeSelectModal 등 5개 통합
- [ ] Zod 검증 스키마 라이브러리 구축
- lib/validations/common.ts (이메일, 전화, 사업자번호)
- lib/validations/vendor.ts, order.ts, item.ts 등
- [ ] 수동 검증 50+ 폼 → Zod 마이그레이션 (우선 10개)
예상 효과: ~5,000줄 절감, UX 일관성 +80%
Phase 5: 성능 + 타입 정리 (1-2주) 프론트 단독
React.memo 적용 + any 제거 + 타입 통합
작업 항목:
- [ ] React.memo 적용 (리스트 아이템 컴포넌트 30+개)
- WorkItemCard, CommentItem, ProjectCard 등
- *Row, *Item, *Card 패턴 전수 조사
- [ ] 대형 컴포넌트 useCallback 적용
- MainDashboard, WorkerScreen, DynamicItemForm
- [ ] any 타입 제거 (102곳)
- Phase 1: types/ 파일 (4개)
- Phase 2: action error handler (50+ 파일)
- Phase 3: 컴포넌트 props (20개)
- [ ] 공통 타입 라이브러리 정리
- types/shared/ 폴더 생성
- ApiResponse<T>, PaginatedResponse<T>, FormState<T> 등
- 엔티티별 정규 타입 단일화
- [ ] @ts-ignore / eslint-disable 제거 (25개 파일)
예상 효과: 리스트 렌더링 30-50% 개선, 타입 안전성 +60%
전체 예상 효과 요약
| 지표 | Phase 1 | Phase 2 | Phase 3 | Phase 4 | Phase 5 | 합계 |
|---|---|---|---|---|---|---|
| 코드 절감 | ~8,500줄 | (구조 개선) | ~16,000줄 | ~5,000줄 | (품질 개선) | ~29,500줄 |
| 패턴 일관성 | +60% | +50% | +40% | +80% | +60% | 종합 개선 |
| 유지보수성 | 높음 | 매우 높음 | 높음 | 중간 | 중간 | 종합 개선 |
| 위험도 | 낮음 | 중간 | 중간 | 낮음 | 낮음 | - |
병렬 진행 가능 조합
[독립 진행 가능]
├─ Phase 1 (공통 훅) ──→ 즉시 시작
├─ Phase 5 (성능/타입) ─→ 즉시 시작 (Phase 1과 병렬)
│
[Phase 1 완료 후]
├─ Phase 2 (God 컴포넌트 분리) ──→ 훅 활용하여 분리
├─ Phase 3 (액션 제네릭화) ────→ 독립 진행 가능
│
[Phase 1+3 완료 후]
└─ Phase 4 (템플릿 통일) ─────→ 훅 + 서비스 활용
관련 문서
| 문서 | 설명 |
|---|---|
[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 로드맵 |
모든 Phase 프론트 단독 가능 - 백엔드 의존성 없음