feat(WEB): Phase 2-3 V2 마이그레이션 완료 및 ServerErrorPage 적용

Phase 2 완료 (4개):
- 노무관리, 단가관리(건설), 입금, 출금

Phase 3 라우팅 구조 변경 완료 (22개):
- 거래처(영업), 팝업관리, 공정관리, 게시판관리, 대손추심, Q&A
- 현장관리, 실행내역, 견적관리, 견적(테스트)
- 입찰관리, 이슈관리, 현장설명회, 견적서(건설)
- 협력업체, 시공관리, 기성관리, 품목관리(건설)
- 회계 도메인: 거래처, 매출, 세금계산서, 매입

신규 컴포넌트:
- ErrorCard: 에러 페이지 UI 통일
- ServerErrorPage: V2 페이지 에러 처리 필수
- V2 Client 컴포넌트 및 Config 파일들

총 47개 상세 페이지 중 28개 완료, 19개 제외/불필요

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-19 17:31:28 +09:00
parent 1a6cde2d36
commit 1d7b028693
109 changed files with 6811 additions and 2562 deletions

View File

@@ -0,0 +1,155 @@
# 상세/등록/수정 페이지 패턴 분류표
> Chrome DevTools MCP로 직접 확인한 결과 기반 (2026-01-19)
## 패턴 분류 기준
### 1⃣ 페이지 형태 - 하단 버튼 (표준 패턴)
- URL이 변경되며 별도 페이지로 이동
- 버튼 위치: **하단** (좌: 목록/취소, 우: 삭제/수정/저장)
- **IntegratedDetailTemplate 적용 대상**
### 2⃣ 페이지 형태 - 상단 버튼
- URL이 변경되며 별도 페이지로 이동
- 버튼 위치: **상단**
- IntegratedDetailTemplate 확장 필요 (`buttonPosition="top"`)
### 3⃣ 모달 형태
- URL 변경 없음, Dialog/Modal로 표시
- **IntegratedDetailTemplate 적용 제외**
### 4⃣ 인라인 입력 형태
- 리스트 페이지 내에서 직접 입력/수정
- **IntegratedDetailTemplate 적용 제외**
### 5⃣ DynamicForm 형태
- API 기반 동적 폼 생성
- IntegratedDetailTemplate의 `renderForm` prop으로 분기 처리
---
## 📄 페이지 형태 - 하단 버튼 (통합 대상)
| 도메인 | 페이지 | URL 패턴 | 상태 |
|--------|--------|----------|------|
| **설정** | 계좌관리 | `/settings/accounts/[id]`, `/new` | ✅ 이미 마이그레이션 완료 |
| **설정** | 카드관리 | `/hr/card-management/[id]`, `/new` | ✅ 이미 마이그레이션 완료 |
| **설정** | 팝업관리 | `/settings/popup-management/[id]`, `/new` | 🔄 대상 |
| **설정** | 게시판관리 | `/board/board-management/[id]`, `/new` | 🔄 대상 |
| **기준정보** | 공정관리 | `/master-data/process-management/[id]`, `/new` | 🔄 대상 |
| **판매** | 거래처관리 | `/sales/client-management-sales-admin/[id]`, `/new` | 🔄 대상 |
| **판매** | 견적관리 | `/sales/quote-management/[id]`, `/new` | 🔄 대상 |
| **판매** | 수주관리 | `/sales/order-management-sales/[id]`, `/new` | 🔄 대상 |
| **품질** | 검사관리 | `/quality/inspections/[id]`, `/new` | 🔄 대상 |
| **출고** | 출하관리 | `/outbound/shipments/[id]`, `/new` | 🔄 대상 |
| **고객센터** | 공지사항 | `/customer-center/notices/[id]` | 🔄 대상 |
| **고객센터** | 이벤트 | `/customer-center/events/[id]` | 🔄 대상 |
---
## 📄 페이지 형태 - 상단 버튼 (확장 필요)
| 도메인 | 페이지 | URL 패턴 | 버튼 구성 | 비고 |
|--------|--------|----------|-----------|------|
| **회계** | 거래처관리 | `/accounting/vendors/[id]`, `/new` | 목록/삭제/수정 | 다중 섹션 구조 |
| **회계** | 매출관리 | `/accounting/sales/[id]`, `/new` | - | 🔄 대상 |
| **회계** | 매입관리 | `/accounting/purchase/[id]` | - | 🔄 대상 |
| **회계** | 입금관리 | `/accounting/deposits/[id]` | - | 🔄 대상 |
| **회계** | 출금관리 | `/accounting/withdrawals/[id]` | - | 🔄 대상 |
| **회계** | 어음관리 | `/accounting/bills/[id]`, `/new` | - | 🔄 대상 |
| **회계** | 악성채권 | `/accounting/bad-debt-collection/[id]`, `/new` | - | 🔄 대상 |
| **전자결재** | 기안함 (임시저장) | `/approval/draft/new?id=:id&mode=edit` | 상세/삭제/상신/저장 | 복잡한 섹션 구조 |
---
## 🔲 모달 형태 (통합 제외)
| 도메인 | 페이지 | 모달 컴포넌트 | 비고 |
|--------|--------|--------------|------|
| **설정** | 직급관리 | `RankDialog.tsx` | 인라인 입력 + 수정 모달 |
| **설정** | 직책관리 | `TitleDialog.tsx` | 인라인 입력 + 수정 모달 |
| **인사** | 부서관리 | `DepartmentDialog.tsx` | 트리 구조 |
| **인사** | 근태관리 | `AttendanceInfoDialog.tsx` | 모달로 등록 |
| **인사** | 휴가관리 | `VacationRequestDialog.tsx` | 모달로 등록/조정 |
| **인사** | 급여관리 | `SalaryDetailDialog.tsx` | 모달로 상세 |
| **전자결재** | 기안함 (결재대기) | 품의서 상세 Dialog | 상세만 모달 |
| **건설** | 카테고리관리 | `CategoryDialog.tsx` | 모달로 등록/수정 |
---
## 🔧 DynamicForm 형태 (renderForm 분기)
| 도메인 | 페이지 | URL 패턴 | 비고 |
|--------|--------|----------|------|
| **품목** | 품목관리 | `/items/[id]` | `DynamicItemForm` 사용 |
---
## ⚠️ 특수 케이스 (개별 처리 필요)
| 도메인 | 페이지 | URL 패턴 | 특이사항 |
|--------|--------|----------|----------|
| **설정** | 권한관리 | `/settings/permissions/[id]`, `/new` | Matrix UI, 복잡한 구조 |
| **인사** | 사원관리 | `/hr/employee-management/[id]`, `/new` | 40+ 필드, 탭 구조 |
| **게시판** | 게시글 | `/board/[boardCode]/[postId]` | 동적 게시판 |
| **건설** | 다수 페이지 | `/construction/...` | 별도 분류 필요 |
---
## 📊 통합 우선순위
### Phase 1: 단순 CRUD (우선 작업)
1. 팝업관리
2. 게시판관리
3. 공정관리
4. 공지사항/이벤트
### Phase 2: 중간 복잡도
1. 판매 > 거래처관리
2. 판매 > 견적관리
3. 품질 > 검사관리
4. 출고 > 출하관리
### Phase 3: 회계 도메인 (상단 버튼 확장 후)
1. 회계 > 거래처관리
2. 회계 > 매출/매입/입금/출금
3. 회계 > 어음/악성채권
### 제외 (개별 유지)
- 권한관리 (Matrix UI)
- 사원관리 (40+ 필드)
- 부서관리 (트리 구조)
- 전자결재 (복잡한 워크플로우)
- DynamicForm 페이지 (renderForm 분기)
- 모달 형태 페이지들
---
## IntegratedDetailTemplate 확장 필요 Props
```typescript
interface IntegratedDetailTemplateProps {
// 기존 props...
// 버튼 위치 제어
buttonPosition?: 'top' | 'bottom'; // default: 'bottom'
// 뒤로가기 버튼 표시 여부
showBackButton?: boolean; // default: true
// 상단 버튼 커스텀 (문서 결재 등)
headerActions?: ReactNode;
// 다중 섹션 지원
sections?: Array<{
title: string;
fields: FieldConfig[];
}>;
}
```
---
## 작성일
- 최초 작성: 2026-01-19
- Chrome DevTools MCP 확인 완료

View File

@@ -0,0 +1,118 @@
# Chrome DevTools MCP - 이모지 JSON 직렬화 오류
> 작성일: 2025-01-17
## 문제 현상
Chrome DevTools MCP가 특정 페이지 접근 시 다운되는 현상
### 에러 메시지
```
API Error: 400 {"type":"error","error":{"type":"invalid_request_error",
"message":"The request body is not valid JSON: invalid high surrogate in string:
line 1 column XXXXX (char XXXXX)"},"request_id":"req_XXXXX"}
```
### 발생 조건
- 페이지에 **이모지**가 많이 포함된 경우
- `take_snapshot` 또는 다른 MCP 도구 호출 시
- a11y tree를 JSON으로 직렬화하는 과정에서 발생
## 원인
### 유니코드 서로게이트 쌍 (Surrogate Pair) 문제
이모지는 UTF-16에서 **서로게이트 쌍**으로 인코딩됨:
- High surrogate: U+D800 ~ U+DBFF
- Low surrogate: U+DC00 ~ U+DFFF
Chrome DevTools MCP가 페이지 스냅샷을 JSON으로 직렬화할 때, 이모지의 서로게이트 쌍이 깨지면서 "invalid high surrogate" 오류 발생.
### 문제가 되는 케이스
1. **DOM에 직접 렌더링된 이모지**: `<span>🏠</span>`
2. **데이터에 포함된 이모지**: API 응답, 파싱된 데이터
3. **대량의 이모지**: 수십 개 이상의 이모지가 한 페이지에 존재
## 해결 방법
### 1. 이모지를 Lucide 아이콘으로 교체 (UI)
**Before**
```tsx
const iconMap = {
'기본': '🏠',
'인사관리': '👥',
};
<span className="text-xl">{category.icon}</span>
```
**After**
```tsx
import { Home, Users, type LucideIcon } from 'lucide-react';
const iconComponents: Record<string, LucideIcon> = {
Home,
Users,
};
function CategoryIcon({ name }: { name: string }) {
const IconComponent = iconComponents[name] || FileText;
return <IconComponent className="w-5 h-5" />;
}
<CategoryIcon name={category.icon} />
```
### 2. 데이터 파싱 시 이모지 제거/변환 (Server)
```typescript
function convertEmojiToText(text: string): string {
// 특정 이모지를 의미있는 텍스트로 변환
let result = text
.replace(/✅/g, '[완료]')
.replace(/⚠️?/g, '[주의]')
.replace(/🧪/g, '[테스트]')
.replace(/🆕/g, '[NEW]')
.replace(/•/g, '-');
// 모든 이모지 및 특수 유니코드 문자 제거
result = result
.replace(/[\u{1F300}-\u{1F9FF}]/gu, '') // 이모지 범위
.replace(/[\u{2600}-\u{26FF}]/gu, '') // 기타 기호
.replace(/[\u{2700}-\u{27BF}]/gu, '') // 딩뱃
.replace(/[\u{FE00}-\u{FE0F}]/gu, '') // Variation Selectors
.replace(/[\u{1F000}-\u{1F02F}]/gu, '') // 마작 타일
.replace(/[\u{1F0A0}-\u{1F0FF}]/gu, '') // 플레잉 카드
.replace(/[\u200D]/g, '') // Zero Width Joiner
.trim();
return result;
}
```
## 체크리스트
새 페이지 개발 시 Chrome DevTools MCP 호환성 확인:
- [ ] 페이지에 이모지 직접 렌더링하지 않음
- [ ] 아이콘은 Lucide 또는 SVG 사용
- [ ] 외부 데이터(API, 파일) 파싱 시 이모지 제거 처리
- [ ] status, label 등에 이모지 대신 텍스트 사용
## 관련 파일
이 문제로 수정된 파일들:
| 파일 | 변경 내용 |
|------|----------|
| `dev/test-urls/actions.ts` | iconMap, convertEmojiToText 함수 추가 |
| `dev/test-urls/TestUrlsClient.tsx` | Lucide 아이콘 동적 렌더링 |
| `dev/construction-test-urls/actions.ts` | 동일 |
| `dev/construction-test-urls/ConstructionTestUrlsClient.tsx` | 동일 |
## 참고
- 이 문제는 Chrome DevTools MCP의 JSON 직렬화 로직에서 발생
- MCP 자체 버그일 가능성 있으나, 클라이언트에서 이모지 제거로 우회 가능
- 다른 MCP 도구에서도 비슷한 문제 발생 가능성 있음