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:
158
claudedocs/[ANALYSIS] common-component-patterns.md
Normal file
158
claudedocs/[ANALYSIS] common-component-patterns.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# 공통 컴포넌트 패턴 분석
|
||||
|
||||
> Phase 3 마이그레이션 진행하면서 발견되는 공통화 후보 패턴 수집
|
||||
> 페이지 마이그레이션 완료 후 이 리스트 기반으로 공통 컴포넌트 설계
|
||||
|
||||
## 최종 목표: IntegratedDetailTemplate Config 통합
|
||||
|
||||
공통 컴포넌트 추출 후 `IntegratedDetailTemplate`의 필드 config 옵션으로 통합
|
||||
|
||||
**현재 지원 타입:**
|
||||
```typescript
|
||||
type: 'text' | 'select' | 'date' | 'textarea' | 'number' | 'checkbox'
|
||||
```
|
||||
|
||||
**확장 예정 타입:**
|
||||
```typescript
|
||||
// 주소 입력
|
||||
{ name: 'address', label: '주소', type: 'address', withCoords?: boolean }
|
||||
|
||||
// 파일 업로드
|
||||
{ name: 'files', label: '첨부파일', type: 'file-upload', maxSize?: number, multiple?: boolean, accept?: string }
|
||||
|
||||
// 음성 메모
|
||||
{ name: 'memo', label: '메모', type: 'voice-memo' }
|
||||
|
||||
// 삭제 확인 (페이지 레벨 옵션)
|
||||
deleteConfirm?: { title: string, message: string }
|
||||
```
|
||||
|
||||
**장점:**
|
||||
- 페이지별 config만 수정하면 UI 자동 구성
|
||||
- 일관된 UX/UI 보장
|
||||
- 유지보수 용이
|
||||
|
||||
## 발견된 패턴 목록
|
||||
|
||||
### 1. 주소 입력 (Address Input)
|
||||
| 발견 위치 | 구성 요소 | 특이사항 |
|
||||
|-----------|-----------|----------|
|
||||
| site-management/SiteDetailForm | 우편번호 찾기 버튼 + 주소 Input + 상세주소 | 경도/위도 필드 별도 |
|
||||
|
||||
**공통 요소:**
|
||||
- Daum Postcode API 연동 (`useDaumPostcode` 훅 이미 존재)
|
||||
- 우편번호 찾기 버튼
|
||||
- 기본주소 (자동 입력)
|
||||
- 상세주소 (수동 입력)
|
||||
|
||||
**변형 가능성:**
|
||||
- 경도/위도 필드 포함 여부
|
||||
- 읽기 전용 모드 지원
|
||||
|
||||
---
|
||||
|
||||
### 2. 파일 업로드 (File Upload)
|
||||
| 발견 위치 | 구성 요소 | 특이사항 |
|
||||
|-----------|-----------|----------|
|
||||
| site-management/SiteDetailForm | 드래그앤드롭 영역 + 파일 목록 | 10MB 제한, 다중 파일 |
|
||||
| structure-review/StructureReviewDetailForm | 드래그앤드롭 영역 + 파일 목록 | 10MB 제한, 다중 파일 (동일 패턴) |
|
||||
|
||||
**공통 요소:**
|
||||
- 드래그앤드롭 영역 (점선 박스)
|
||||
- 클릭하여 파일 선택
|
||||
- 드래그 중 시각적 피드백
|
||||
- 파일 크기 검증
|
||||
|
||||
**변형 가능성:**
|
||||
- 허용 파일 타입 (이미지만 / 문서만 / 전체)
|
||||
- 단일 vs 다중 파일
|
||||
- 최대 파일 크기
|
||||
- 최대 파일 개수
|
||||
|
||||
---
|
||||
|
||||
### 3. 파일 목록 (File List)
|
||||
| 발견 위치 | 구성 요소 | 특이사항 |
|
||||
|-----------|-----------|----------|
|
||||
| site-management/SiteDetailForm | 파일명 + 크기 + 다운로드/삭제 | view/edit 모드별 다른 액션 |
|
||||
| structure-review/StructureReviewDetailForm | 파일명 + 크기 + 다운로드/삭제 | 동일 패턴 |
|
||||
|
||||
**공통 요소:**
|
||||
- 파일 아이콘
|
||||
- 파일명 표시
|
||||
- 파일 크기 표시
|
||||
- 액션 버튼
|
||||
|
||||
**변형 가능성:**
|
||||
- view 모드: 다운로드 버튼
|
||||
- edit 모드: 삭제(X) 버튼
|
||||
- 미리보기 지원 (이미지)
|
||||
- 업로드 날짜 표시 여부
|
||||
|
||||
---
|
||||
|
||||
### 4. 음성 녹음 (Voice Recorder)
|
||||
| 발견 위치 | 구성 요소 | 특이사항 |
|
||||
|-----------|-----------|----------|
|
||||
| site-management/SiteDetailForm | 녹음 버튼 (Textarea 내부) | edit 모드에서만 표시 |
|
||||
|
||||
**공통 요소:**
|
||||
- 녹음 시작/중지 버튼
|
||||
- Textarea와 연동 (STT 결과 입력)
|
||||
|
||||
**변형 가능성:**
|
||||
- 버튼 위치 (Textarea 내부 / 외부)
|
||||
- 녹음 시간 제한
|
||||
- 녹음 중 시각적 피드백
|
||||
|
||||
---
|
||||
|
||||
### 5. 삭제 확인 다이얼로그 (Delete Confirmation Dialog)
|
||||
| 발견 위치 | 구성 요소 | 특이사항 |
|
||||
|-----------|-----------|----------|
|
||||
| structure-review/StructureReviewDetailForm | AlertDialog + 제목 + 설명 + 취소/삭제 버튼 | view 모드에서 삭제 버튼 클릭 시 |
|
||||
|
||||
**공통 요소:**
|
||||
- AlertDialog 컴포넌트 사용
|
||||
- 제목: "[항목명] 삭제"
|
||||
- 설명: 삭제 확인 메시지 + 되돌릴 수 없음 경고
|
||||
- 취소/삭제 버튼
|
||||
|
||||
**변형 가능성:**
|
||||
- 항목명 커스터마이징
|
||||
- 삭제 후 리다이렉트 경로
|
||||
- 추가 경고 메시지
|
||||
|
||||
---
|
||||
|
||||
## 추가 예정
|
||||
|
||||
> Phase 3 마이그레이션 진행하면서 새로운 패턴 발견 시 여기에 추가
|
||||
|
||||
### 예상 패턴 (확인 필요)
|
||||
- [ ] 이미지 미리보기 (썸네일)
|
||||
- [ ] 서명 입력
|
||||
- [ ] 날짜/시간 선택
|
||||
- [ ] 검색 가능한 Select (Combobox)
|
||||
- [ ] 태그 입력
|
||||
- [ ] 금액 입력 (천단위 콤마)
|
||||
|
||||
---
|
||||
|
||||
## 공통화 우선순위 (마이그레이션 완료 후 결정)
|
||||
|
||||
| 우선순위 | 패턴 | 사용 빈도 | 복잡도 |
|
||||
|----------|------|-----------|--------|
|
||||
| - | 주소 입력 | 1 | 중 |
|
||||
| - | 파일 업로드 | 2 | 상 |
|
||||
| - | 파일 목록 | 2 | 중 |
|
||||
| - | 음성 녹음 | 1 | 상 |
|
||||
| - | 삭제 확인 다이얼로그 | 1 | 하 |
|
||||
|
||||
> 사용 빈도는 마이그레이션 진행하면서 카운트
|
||||
|
||||
---
|
||||
|
||||
## 변경 이력
|
||||
- 2025-01-19: 초안 작성, site-management에서 4개 패턴 발견
|
||||
- 2025-01-19: structure-review에서 동일 패턴 확인 (파일 업로드/목록), 삭제 확인 다이얼로그 패턴 추가
|
||||
File diff suppressed because it is too large
Load Diff
155
claudedocs/dev/[PLAN] detail-page-pattern-classification.md
Normal file
155
claudedocs/dev/[PLAN] detail-page-pattern-classification.md
Normal 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 확인 완료
|
||||
118
claudedocs/dev/[REF] chrome-devtools-mcp-emoji-issue.md
Normal file
118
claudedocs/dev/[REF] chrome-devtools-mcp-emoji-issue.md
Normal 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 도구에서도 비슷한 문제 발생 가능성 있음
|
||||
Reference in New Issue
Block a user