diff --git a/claudedocs/[ANALYSIS-2026-02-19] frontend-comprehensive-review.md b/claudedocs/[ANALYSIS-2026-02-19] frontend-comprehensive-review.md new file mode 100644 index 00000000..27178ed8 --- /dev/null +++ b/claudedocs/[ANALYSIS-2026-02-19] frontend-comprehensive-review.md @@ -0,0 +1,165 @@ +# SAM ERP 프론트엔드 종합 검수 보고서 + +> 작성일: 2026-02-19 +> 분석 범위: src/ 전체 (1,438개 TS/TSX 파일, ~314K줄) +> 분석 방법: 5개 에이전트 병렬 분석 (코드품질, 번들/성능, 에러/UX, 아키텍처, 모바일/보안) + +--- + +## 종합 스코어카드 + +| 영역 | 점수 | 등급 | 핵심 이슈 | +|------|------|------|-----------| +| **코드 품질** | 7.5/10 | 🟢 양호 | TS 규율 우수, any 133건/TODO 121건 잔존 | +| **번들/성능** | 8.5/10 | 🟢 우수 | 동적 로드 적용, tree-shaking 양호 | +| **에러/UX 일관성** | 5.5/10 | 🟡 보통 | 에러바운더리 우수, 로딩UI/접근성 미흡 | +| **아키텍처** | 6.5/10 | 🟡 보통 | 순환의존 없음, 상태관리 중복 | +| **모바일 대응** | 6/10 | 🟡 보통 | 57% 반응형, 터치영역 미달 | +| **보안** | 7/10 | 🟢 양호 | 인증 강함, CSP unsafe 허용 | + +**전체: 6.8/10** — 기능적으로 안정적이나, UX 일관성과 아키텍처 정리에 개선 여지 + +--- + +## 우선순위별 개선 항목 + +### P0: 보안 이슈 (즉시 조치) + +| # | 항목 | 심각도 | 현황 | 조치 | +|---|------|--------|------|------| +| S-1 | CSP `unsafe-inline`/`unsafe-eval` | 🔴 높음 | middleware.ts에서 허용 중 | nonce 기반으로 전환 | +| S-2 | `new Function()` 코드 주입 | 🔴 높음 | ComputedField.tsx에서 사용 | 사용자 입력 검증 추가 또는 safe-eval 대체 | +| S-3 | sanitizeHTML 함수 강도 | 🟡 중간 | 5개 파일에서 사용 중 | DOMPurify 사용 여부 확인 | + +### P1: 아키텍처 정리 (1~2주) + +| # | 항목 | 현황 | 개선안 | +|---|------|------|--------| +| A-1 | **상태관리 중복** | ItemMasterContext + itemStore + useItemMasterStore 3중 | Zustand 하나로 통합 | +| A-2 | **테마 중복** | ThemeContext + themeStore 병존 | Zustand로 완전 마이그레이션 | +| A-3 | **utils 폴더 중복** | `src/utils/` (2개) + `src/lib/utils/` (11개) 병존 | `src/utils/` → `src/lib/utils/`로 통합 | +| A-4 | **상수 산재** | constants/ 1개 파일만, 나머지 각 컴포넌트 내부 하드코딩 | 도메인별 `constants/` 정리 | + +### P2: 코드 품질 (2~3주) + +| # | 항목 | 건수 | 현황 | 조치 | +|---|------|------|------|------| +| Q-1 | `as any` 타입 캐스트 | 64건 | 주로 form errors 처리 | 제네릭 타입 정의 | +| Q-2 | `: any` 타입 선언 | 48건 | API 응답/props 타입 | 인터페이스 정의 | +| Q-3 | TODO/FIXME 누적 | 121건 (68파일) | useItemMasterStore 15건 등 | 이슈화 → 점진적 해소 | +| Q-4 | God 컴포넌트 | 5개 | ItemMasterContext 2,200줄, MainDashboard 1,400줄 | 단계적 분리 | +| Q-5 | 거대 훅 | 1개 | useCEODashboard 37.9KB | stats/charts/timeline 분리 | +| Q-6 | `alert()`/`confirm()` 잔존 | 32건 | 15개 alert + 17개 confirm | ConfirmDialog/toast로 교체 | + +### P3: UX 일관성 (3~4주) + +| # | 항목 | 현황 | 목표 | +|---|------|------|------| +| U-1 | **로딩 UI** | 40+ 페이지에서 `"로딩 중..."` 텍스트만 사용, Skeleton 2개만 | Skeleton 기반 로딩으로 통일 | +| U-2 | **접근성 (a11y)** | aria-label 3건, role 9건 | 주요 폼/테이블에 ARIA 추가 | +| U-3 | **i18n 사용률** | 인프라 완성(ko/en/ja), 실제 사용 ~5% | 점진적 적용 확대 | +| U-4 | **Zod 검증** | 2개 폼만 적용 | 신규 폼 필수, 기존은 유지 | +| U-5 | **EmptyState 활용** | 컴포넌트 존재하나 하드코딩 "데이터 없음" 다수 | EmptyState 컴포넌트 통일 | + +### P4: 모바일/성능 (선택) + +| # | 항목 | 현황 | 조치 | +|---|------|------|------| +| M-1 | **반응형 커버리지** | 57% 페이지 적용 | HR/대시보드 등 미적용 페이지 보강 | +| M-2 | **터치 영역** | Checkbox 20x20px (권장 44x44px) | 모바일 터치 타겟 확대 | +| M-3 | **html2canvas + dom-to-image** 중복 | 2개 라이브러리 공존 | 하나로 통합 (~50-80KB 절감) | +| M-4 | **Tiptap 동적 로딩** | 보드/팝업에서만 사용하나 번들 포함 | next/dynamic 적용 (~80-100KB 절감) | +| M-5 | **도메인별 actions.ts 표준화** | accounting만 page-level actions, 나머지는 컴포넌트 내부 | accounting 패턴으로 통일 | + +--- + +## 잘 되어있는 점 (유지 사항) + +### 코드 품질 +- ✅ **TypeScript 규율**: @ts-ignore 0건, @ts-nocheck 1건(레거시) +- ✅ **console.log 관리**: 23건만 (16건은 logger 유틸리티) +- ✅ **에러 바운더리**: 글로벌 + Protected 레벨 4개, Slack 연동 +- ✅ **Toast 시스템**: sonner 기반 1,277개 인스턴스 일관 사용 + +### 번들/성능 +- ✅ **XLSX 동적 로드**: 버튼 클릭 시에만 ~400KB 로드 +- ✅ **대시보드 코드 스플리팅**: ~850KB 초기 번들에서 제외 +- ✅ **tree-shaking**: `import *` 0건, lodash/moment 미사용 +- ✅ **Zustand 정규화**: 체계적 상태 + Immer + selector hooks +- ✅ **Tailwind v4**: 최신 버전, 효율적 트리셰이킹 + +### 아키텍처 +- ✅ **순환 의존성 없음**: pages→components→ui 단방향 +- ✅ **API 계층**: buildApiUrl 43개 actions.ts 전면 적용 +- ✅ **executePaginatedAction**: 14개 파일 표준화 + +### 보안 +- ✅ **Bot 차단**: 25개 패턴 필터링 +- ✅ **다층 인증**: Bearer Token + Authorization 헤더 + Sanctum + API Key +- ✅ **Open Redirect 방지**: 내부 경로 검증 +- ✅ **환경변수 분리**: NEXT_PUBLIC_ 적절히 사용 +- ✅ **민감 정보 노출 없음**: console.log에 토큰/비밀번호 출력 0건 + +--- + +## 주요 파일 참조 + +### God 컴포넌트 (분리 대상) +- `src/contexts/ItemMasterContext.tsx` (2,200줄) +- `src/components/business/MainDashboard.tsx` (1,400줄) +- `src/hooks/useCEODashboard.ts` (37.9KB) + +### any 타입 집중 지역 +- `src/components/items/ItemForm/forms/parts/` (22건) +- `src/components/items/ItemMasterDataManagement/` (18건) +- `src/components/quotes/LocationDetailPanel.tsx` (10건) + +### 보안 확인 대상 +- `src/middleware.ts` (CSP 설정) +- `src/components/**/ComputedField.tsx` (new Function) +- sanitizeHTML 사용 파일 5개 (게시판, 팝업, 고객센터) + +### 상태관리 중복 +- `src/contexts/ItemMasterContext.tsx` vs `src/stores/itemStore.ts` vs `src/stores/item-master/useItemMasterStore.ts` +- `src/contexts/ThemeContext.tsx` vs `src/stores/themeStore.ts` + +--- + +## 기존 로드맵과의 관계 + +| 기존 항목 | 상태 | 이번 분석 결과 | +|-----------|------|---------------| +| D-1 God 컴포넌트 분리 | ⏳ 대기 | → P2-Q4로 재확인, 여전히 필요 | +| D-2 `as` 타입 캐스트 | 보류 | → P2-Q1/Q2로 133건 확인 (기존 ~200건에서 감소) | +| D-6 TODO 102건 | ⏳ 대기 | → P2-Q3으로 121건 확인 (소폭 증가) | +| A-2 DataTable 최적화 | ⏳ 대기 | → 에이전트 분석 결과 re-render 위험 낮음 (우선순위 하향) | + +### 신규 발견 항목 (기존 로드맵에 없었던 것) +- **S-1~S-3**: 보안 이슈 (CSP, code injection, sanitization) +- **A-1~A-2**: 상태관리 3중 중복 +- **U-1~U-5**: UX 일관성 전반 (로딩/접근성/i18n/빈상태) +- **M-3~M-4**: 라이브러리 중복/동적 로딩 기회 + +--- + +## 실행 로드맵 요약 + +``` +Week 1-2: P0 보안 + P1 아키텍처 정리 + ├── CSP nonce 전환 + ├── ComputedField 보안 패치 + ├── 상태관리 중복 정리 (Context → Zustand) + └── utils 폴더 통합 + +Week 3-4: P2 코드 품질 + ├── any 타입 정리 (form errors 제네릭) + ├── alert/confirm → ConfirmDialog 교체 + └── TODO/FIXME 이슈 정리 + +Week 5-6: P3 UX 일관성 (선택) + ├── Skeleton 로딩 UI 통일 + ├── EmptyState 활용 확대 + └── 접근성 기본 적용 + +이후: P4 모바일/성능 (필요 시) +``` \ No newline at end of file diff --git a/claudedocs/[IMPL-2026-02-19] frontend-improvement-checklist.md b/claudedocs/[IMPL-2026-02-19] frontend-improvement-checklist.md new file mode 100644 index 00000000..30d2c113 --- /dev/null +++ b/claudedocs/[IMPL-2026-02-19] frontend-improvement-checklist.md @@ -0,0 +1,465 @@ +# SAM ERP 프론트엔드 개선 체크리스트 + +> 작성일: 2026-02-19 +> 기반: `[ANALYSIS-2026-02-19] frontend-comprehensive-review.md` 분석 결과 +> 구조: 8개 독립 작업 패키지 (WP) — 에이전트 병렬 작업 가능 + +--- + +## 작업 패키지 의존성 맵 + +``` +완료됨: + ✅ WP-1: alert/confirm 제거 + ✅ WP-2: utils 폴더 통합 + ✅ WP-3: 미사용 패키지 제거 + ✅ WP-4: ThemeContext → themeStore 통합 + ✅ WP-5: 로딩 UI Skeleton 통일 + ✅ WP-6: itemStore 제거 (미사용 store 정리) + ✅ WP-7: TODO 이슈 정리 (문서 작업) + +보류 (선택): + WP-8: any 타입 정리 +``` + +--- + +## WP-1: alert()/confirm() → Toast/Dialog 교체 ✅ 완료 (2026-02-19) + +**범위**: 42개 alert + 14개 confirm = 56건 +**난이도**: 낮음 | **영향**: 중간 + +### alert() → toast 교체 (42건) ✅ 전체 완료 + +#### 파일 1: `src/lib/print-utils.ts` +- [x] `alert('팝업이 차단되었습니다...')` → `toast.error(...)` + +#### 파일 2: `src/components/items/ItemListClient.tsx` +- [x] 삭제 결과 → `toast.success(...)` + 실패 시 `toast.error(...)` +- [x] 업로드 결과 → `toast.success(...)` / `toast.error('업로드 오류', { description })` +- [x] 기타 alert 4건 → toast 교체 + +#### 파일 3: `src/components/hr/VacationManagement/index.tsx` +- [x] 유효성 3건 → `toast.warning(...)` +- [x] 에러/성공 4건 → `toast.error(...)` / `toast.success(...)` + +#### 파일 4: `src/components/outbound/ShipmentManagement/ShipmentDetail.tsx` +- [x] 에러 4건 → `toast.error(...)` + +#### 파일 5: `src/components/items/ItemMasterDataManagement/tabs/HierarchyTab/index.tsx` +- [x] 클립보드 복사 4건 → `toast.success(...)` / `toast.error(...)` + +#### 파일 6: `src/components/items/DynamicItemForm/hooks/useFileHandling.ts` +- [x] 4건 → toast 교체 + +#### 파일 7: `src/components/items/ItemForm/BendingDiagramSection.tsx` +- [x] 2건 → `toast.error(...)` + +#### 파일 8: `src/components/items/DynamicItemForm/index.tsx` +- [x] 4건 → toast 교체 + +#### 파일 9: `src/components/items/ItemDetailClient.tsx` +- [x] 3건 → toast 교체 + +#### 파일 10: `src/components/hr/VacationManagement/VacationRequestDialog.tsx` +- [x] 2건 → toast 교체 + +#### 파일 11: `src/components/hr/VacationManagement/VacationGrantDialog.tsx` +- [x] 2건 → toast 교체 + +#### 파일 12: `src/components/process-management/RuleModal.tsx` +- [x] 1건 → toast 교체 + +#### 파일 13: `src/app/[locale]/(protected)/dev/page-builder/PageBuilderClient.tsx` +- [x] 5건 → toast 교체 (개발 도구) + +#### 파일 14: `src/app/[locale]/(protected)/dev/page-builder/components/PageSelector.tsx` +- [x] 2건 → toast 교체 (개발 도구) + +#### 파일 15: `src/components/board/BoardList/BoardListUnified.tsx` +- [x] 1건 → toast 교체 + +### confirm() → ConfirmDialog 교체 (14건) + +#### Component-level (9건) ✅ 완료 + +- [x] `src/components/production/WorkOrders/WorkOrderEdit.tsx` — `DeleteConfirmDialog` + state 패턴 +- [x] `src/components/approval/DocumentCreate/index.tsx` — `DeleteConfirmDialog` + state 패턴 +- [x] `src/components/board/BoardList/BoardListUnified.tsx` (2건) — `DeleteConfirmDialog` + `deleteTarget` state +- [x] `src/components/items/BOMManagementSection.tsx` — `DeleteConfirmDialog` + `deleteTargetId` state +- [x] `src/components/items/ItemMasterDataManagement/tabs/HierarchyTab/index.tsx` (3건) — `ConfirmDialog` + discriminated union `HierarchyConfirmAction` state + +#### Hook-level (5건) ⏸️ 후순위 — hooks에서 JSX 렌더링 불가, callback 패턴 리팩토링 필요 + +- [ ] `src/components/items/DynamicItemForm/hooks/useFileHandling.ts:258` +- [ ] `src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts:82` +- [ ] `src/components/items/ItemMasterDataManagement/hooks/useMasterFieldManagement.ts:220` +- [ ] `src/components/items/ItemMasterDataManagement/hooks/useTemplateManagement.ts:227` +- [ ] `src/components/items/ItemMasterDataManagement/hooks/useTemplateManagement.ts:402` + +#### Dev 도구 (1건) ⏸️ 낮은 우선순위 + +- [ ] `src/app/[locale]/(protected)/dev/page-builder/components/PageSelector.tsx:293` + +### 완료 검증 +- [x] 프로젝트 전체에서 `alert(` 검색 → 0건 (src/ 기준) +- [x] 프로젝트 전체에서 `confirm(` 검색 → 6건 잔여 (hook 5건 + dev 1건, 위 후순위 항목) + +--- + +## WP-2: utils 폴더 통합 (`src/utils/` → `src/lib/utils/`) ✅ 완료 (2026-02-19) + +**범위**: 2개 파일 이동 + 49개 import 경로 수정 +**난이도**: 낮음 | **영향**: 낮음 + +### Step 1: 파일 이동 + +- [x] `src/utils/date.ts` → `src/lib/utils/date.ts` (내용 변경 없음) +- [x] `src/utils/formatAmount.ts` → `src/lib/utils/amount.ts` (내용 변경 없음) + +### Step 2: import 경로 수정 + +#### `@/utils/date` → `@/lib/utils/date` (25파일) +- [x] 프로젝트 전체에서 `from '@/utils/date'` 검색 → 모두 교체 완료 + +#### `@/utils/formatAmount` → `@/lib/utils/amount` (24파일) +- [x] 프로젝트 전체에서 `from '@/utils/formatAmount'` 검색 → 모두 교체 완료 + +### Step 3: 정리 +- [x] `src/utils/` 디렉토리 삭제 +- [x] import 경로 검증: `@/utils/` 패턴 0건 확인 + +--- + +## WP-3: 미사용 패키지 제거 + Tiptap 동적 로딩 ✅ 완료 (2026-02-19) + +**범위**: package.json 정리 + 3개 파일 dynamic import +**난이도**: 낮음~중간 | **영향**: 번들 사이즈 감소 + +### Step 1: 미사용 패키지 확인 및 제거 + +- [x] `html2canvas` (^1.4.1) — 소스코드에서 import 0건 확인 → `npm uninstall` 완료 +- [x] `dom-to-image-more` (^3.7.2) — 소스코드에서 import 0건 확인 → `npm uninstall` 완료 + +### Step 2: Tiptap 동적 로딩 + +- [x] `src/components/board/BoardForm/index.tsx` — `dynamic(() => import('../RichTextEditor'), { ssr: false })` +- [x] `src/components/customer-center/InquiryManagement/InquiryForm.tsx` — 동일 패턴 +- [x] `src/components/settings/PopupManagement/PopupForm.tsx` — 동일 패턴 +- [x] `src/components/settings/PopupManagement/popupDetailConfig.ts` — ⏭️ 스킵 (createElement 패턴, dynamic 비호환) + +### 완료 검증 +- [x] `npm ls html2canvas` → not found +- [x] `npm ls dom-to-image-more` → not found + +--- + +## WP-4: ThemeContext → themeStore 통합 ✅ 완료 (2026-02-19) + +**범위**: Context 제거 + 3개 파일 import 수정 +**난이도**: 낮음 | **영향**: 낮음 (3파일) | **예상**: 30분 + +### 현황 +- `ThemeContext.tsx`: Provider 패턴, localStorage 직접 사용 +- `themeStore.ts`: Zustand + persist, 동일 기능 +- ThemeContext 사용처: 3개 파일만 + +### Step 1: 사용처 마이그레이션 + +- [x] `src/components/ThemeSelect.tsx`: + - `import { useTheme } from '@/contexts/ThemeContext'` → `import { useThemeStore } from '@/stores/themeStore'` + - `const { theme, setTheme } = useTheme()` → `const { theme, setTheme } = useThemeStore()` + +- [x] `src/layouts/AuthenticatedLayout.tsx`: + - 동일 패턴으로 교체 + +- [x] `src/app/[locale]/layout.tsx`: + - ThemeProvider import 및 래핑 제거 (Zustand는 Provider 불필요) + +### Step 2: ThemeContext 제거 + +- [x] `src/contexts/ThemeContext.tsx` 삭제 +- [x] `src/contexts/RootProvider.tsx` — ThemeProvider 미사용 확인 (이미 없었음) + +### Step 3: themeStore DOM 클래스 버그 수정 + +- [x] `themeStore.ts`의 `setTheme()`: `document.documentElement.className = ...` → `classList.remove/add` 방식으로 수정 (폰트 클래스 보존) +- [x] `themeStore.ts`의 `onRehydrateStorage`: 동일하게 `classList` 방식으로 수정 + +### 완료 검증 +- [x] `ThemeContext` import 0건 확인 (주석 1건만 잔존) +- [x] TypeScript 에러 없음 + +--- + +## WP-5: 로딩 UI Skeleton 통일 ✅ 완료 (2026-02-19) + +**범위**: GenericPageSkeleton 활용하여 44개 페이지 표준화 +**난이도**: 중간 | **영향**: UX 일관성 + +### 현황 +- 이미 존재: `GenericPageSkeleton` 컴포넌트 (protected/loading.tsx에서 사용) +- 44개 page.tsx에서 `
로딩 중...
-