refactor: 모달 Content 컴포넌트 분리 및 파일 입력 UI 공통화
- 모달 컴포넌트에서 Content 분리하여 재사용성 향상 - EstimateDocumentContent, DirectConstructionContent 등 - WorkLogContent, QuotePreviewContent, ReceivingReceiptContent - 파일 입력 공통 UI 컴포넌트 추가 - file-dropzone, file-input, file-list, image-upload - 폼 컴포넌트 코드 정리 및 중복 제거 (-4,056줄) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
298
claudedocs/dev/[REF] page-builder-implementation.md
Normal file
298
claudedocs/dev/[REF] page-builder-implementation.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# 페이지 빌더 (Page Builder) 구현 문서
|
||||
|
||||
> **작성일**: 2026-01-22
|
||||
> **상태**: 개발 중 (테스트 버전)
|
||||
> **경로**: `/dev/page-builder`
|
||||
> **Git 상태**: `.gitignore`에 등록되어 버전 관리 제외 (테스트용)
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 목적
|
||||
품목기준관리(Item Master Data Management)의 폼 구조를 **시각적으로(WYSIWYG)** 편집할 수 있는 Framer 스타일의 페이지 빌더입니다.
|
||||
|
||||
### 1.2 핵심 기능
|
||||
- 드래그 앤 드롭으로 섹션/필드 배치
|
||||
- 실시간 미리보기 (데스크탑/태블릿/모바일)
|
||||
- **API 연동**: 품목기준관리 API와 동기화
|
||||
- 조건부 표시 설정 (필드 값에 따른 섹션/필드 표시/숨김)
|
||||
- Undo/Redo 지원
|
||||
- JSON 내보내기/가져오기
|
||||
|
||||
### 1.3 접근 방법
|
||||
```
|
||||
URL: http://localhost:3000/dev/page-builder
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 파일 구조
|
||||
|
||||
```
|
||||
src/app/[locale]/(protected)/dev/page-builder/
|
||||
├── page.tsx # 페이지 진입점
|
||||
├── PageBuilderClient.tsx # 메인 클라이언트 컴포넌트
|
||||
├── PLAN.md # 초기 기획 문서
|
||||
│
|
||||
├── components/
|
||||
│ ├── index.ts # 컴포넌트 배럴 export
|
||||
│ ├── ComponentPalette.tsx # 좌측 컴포넌트 팔레트 (섹션/필드 드래그)
|
||||
│ ├── BuilderCanvas.tsx # 중앙 캔버스 (드롭 영역, 미리보기)
|
||||
│ ├── PropertyPanel.tsx # 우측 속성 패널 (섹션/필드 편집)
|
||||
│ ├── PageSelector.tsx # 페이지 목록 관리
|
||||
│ └── ConditionEditor.tsx # 조건부 표시 설정 UI
|
||||
│
|
||||
├── hooks/
|
||||
│ ├── usePageBuilder.ts # 섹션/필드 CRUD, 선택 상태 관리
|
||||
│ ├── usePageManager.ts # 페이지 CRUD, API 연동, 저장/로드
|
||||
│ ├── useHistory.ts # Undo/Redo 히스토리 관리
|
||||
│ └── useItemMasterSync.ts # (미사용) 초기 API 동기화 시도
|
||||
│
|
||||
├── types/
|
||||
│ ├── index.ts # 타입 배럴 export
|
||||
│ ├── builder.types.ts # BuilderPage, BuilderSection, BuilderField 등
|
||||
│ └── constants.ts # 필드 타입, 섹션 타입 옵션 상수
|
||||
│
|
||||
└── utils/
|
||||
├── index.ts # 유틸 배럴 export
|
||||
└── transformers.ts # API ↔ Builder 타입 변환 함수
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 핵심 타입 정의
|
||||
|
||||
### 3.1 BuilderPage
|
||||
```typescript
|
||||
interface BuilderPage {
|
||||
id: string;
|
||||
name: string; // 페이지 이름 (예: "소모품 등록")
|
||||
itemType: ItemType; // 품목 유형 (FG, PT, SM, RM, CS)
|
||||
sections: BuilderSection[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 BuilderSection
|
||||
```typescript
|
||||
interface BuilderSection {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
sectionType: SectionType; // BASIC, BOM, CERTIFICATION 등
|
||||
columns: 1 | 2 | 3; // 레이아웃 열 수
|
||||
isCollapsible?: boolean;
|
||||
isDefaultOpen?: boolean;
|
||||
fields: BuilderField[];
|
||||
displayCondition?: DisplayCondition; // 조건부 표시
|
||||
order: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 BuilderField
|
||||
```typescript
|
||||
interface BuilderField {
|
||||
id: string;
|
||||
name: string; // 필드 라벨
|
||||
fieldKey: string; // API 키 (예: "item_name")
|
||||
inputType: FieldInputType; // textbox, number, dropdown 등
|
||||
required: boolean;
|
||||
placeholder?: string;
|
||||
defaultValue?: string;
|
||||
options?: DropdownOption[]; // 드롭다운용
|
||||
colSpan?: 1 | 2 | 3;
|
||||
description?: string;
|
||||
displayCondition?: DisplayCondition;
|
||||
validationRules?: ValidationRule[];
|
||||
order: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 DisplayCondition (조건부 표시)
|
||||
```typescript
|
||||
interface DisplayCondition {
|
||||
enabled: boolean;
|
||||
logic: 'AND' | 'OR';
|
||||
conditions: FieldCondition[];
|
||||
}
|
||||
|
||||
interface FieldCondition {
|
||||
fieldKey: string;
|
||||
operator: 'equals' | 'not_equals' | 'contains' | 'not_contains';
|
||||
expectedValue: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. API 연동
|
||||
|
||||
### 4.1 사용하는 API
|
||||
품목기준관리와 **동일한 API** 사용:
|
||||
```typescript
|
||||
// src/lib/api/item-master.ts
|
||||
itemMasterApi.init() // 전체 페이지/섹션/필드 조회
|
||||
itemMasterApi.sections.create(pageId, data) // 섹션 생성
|
||||
itemMasterApi.sections.update(id, data) // 섹션 수정
|
||||
itemMasterApi.sections.delete(id) // 섹션 삭제
|
||||
itemMasterApi.fields.create(sectionId, data)// 필드 생성
|
||||
itemMasterApi.fields.update(id, data) // 필드 수정
|
||||
itemMasterApi.fields.delete(id) // 필드 삭제
|
||||
```
|
||||
|
||||
### 4.2 API 모드 토글
|
||||
- **API 모드 ON**: 백엔드 API에서 데이터 로드/저장
|
||||
- **API 모드 OFF**: localStorage에서 로드/저장 (오프라인 테스트용)
|
||||
- 설정은 localStorage에 저장되어 새로고침 후에도 유지
|
||||
|
||||
### 4.3 동기화 로직 (usePageManager.ts)
|
||||
```typescript
|
||||
const syncPageToAPI = async (page: BuilderPage) => {
|
||||
// 1. 원본 데이터와 현재 데이터 비교
|
||||
// 2. 삭제된 섹션/필드 → API DELETE 호출
|
||||
// 3. 새로 추가된 섹션/필드 → API CREATE 호출
|
||||
// 4. 수정된 섹션/필드 → API UPDATE 호출
|
||||
// 5. 완료 후 API에서 최신 데이터 다시 로드
|
||||
};
|
||||
```
|
||||
|
||||
### 4.4 타입 변환 (transformers.ts)
|
||||
```typescript
|
||||
// API → Builder 변환
|
||||
transformPagesToBuilder(apiData): BuilderPage[]
|
||||
|
||||
// Builder → API 변환
|
||||
transformSectionToAPI(section): APISectionCreateData
|
||||
transformFieldToAPI(field): APIFieldCreateData
|
||||
|
||||
// ID 구분 (API ID는 숫자, 로컬 ID는 문자열)
|
||||
isApiId(id: string): boolean // "123" → true, "section_abc123" → false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 주요 컴포넌트 설명
|
||||
|
||||
### 5.1 PageBuilderClient.tsx
|
||||
메인 컴포넌트로 전체 레이아웃 구성:
|
||||
- 상단: 툴바 (미리보기 모드, Undo/Redo, 저장 버튼들)
|
||||
- 좌측: PageSelector + ComponentPalette
|
||||
- 중앙: BuilderCanvas
|
||||
- 우측: PropertyPanel
|
||||
|
||||
주요 상태:
|
||||
```typescript
|
||||
const [isAPIMode, setIsAPIMode] = useState(true); // API 모드
|
||||
const [previewMode, setPreviewMode] = useState<'desktop' | 'tablet' | 'mobile'>('desktop');
|
||||
```
|
||||
|
||||
### 5.2 PageSelector.tsx
|
||||
페이지 목록 관리:
|
||||
- 페이지 선택/생성/삭제/복제/이름변경
|
||||
- 호버 시 액션 버튼 표시 (배경색 포함으로 긴 제목도 가리지 않음)
|
||||
|
||||
### 5.3 BuilderCanvas.tsx
|
||||
드래그 앤 드롭 캔버스:
|
||||
- 섹션 드롭 → 새 섹션 추가
|
||||
- 필드 드롭 → 해당 섹션에 필드 추가
|
||||
- 요소 클릭 → 선택 (PropertyPanel에서 편집)
|
||||
|
||||
### 5.4 PropertyPanel.tsx
|
||||
선택된 요소의 속성 편집:
|
||||
- 섹션: 제목, 설명, 타입, 열 수, 접기 설정, 조건부 표시
|
||||
- 필드: 이름, 키, 타입, 필수여부, 옵션, 조건부 표시
|
||||
|
||||
### 5.5 ConditionEditor.tsx
|
||||
조건부 표시 설정 UI:
|
||||
- 다른 필드 값에 따라 현재 섹션/필드 표시/숨김
|
||||
- AND/OR 논리 연산 지원
|
||||
- 여러 조건 추가 가능
|
||||
|
||||
---
|
||||
|
||||
## 6. 사용 방법
|
||||
|
||||
### 6.1 기본 워크플로우
|
||||
1. `/dev/page-builder` 접속
|
||||
2. **API 모드 ON** 확인 (우측 상단 토글)
|
||||
3. 좌측에서 페이지 선택 (소모품, 원자재 등)
|
||||
4. 컴포넌트 팔레트에서 섹션/필드를 캔버스로 드래그
|
||||
5. 캔버스에서 요소 클릭 → 우측 패널에서 속성 편집
|
||||
6. **"API 저장"** 버튼 클릭 → 백엔드에 저장
|
||||
7. 품목기준관리 페이지에서 변경사항 확인
|
||||
|
||||
### 6.2 저장 버튼 종류
|
||||
| 버튼 | 기능 |
|
||||
|------|------|
|
||||
| API 저장 (초록색) | 현재 페이지를 백엔드 API에 저장 |
|
||||
| 현재 페이지 저장 (JSON) | 현재 페이지를 JSON 파일로 다운로드 |
|
||||
| 전체 저장 | 모든 페이지를 JSON 파일로 다운로드 |
|
||||
| 가져오기 | JSON 파일에서 페이지 데이터 로드 |
|
||||
|
||||
### 6.3 Undo/Redo
|
||||
- **실행 취소**: 마지막 작업 취소
|
||||
- **다시 실행**: 취소한 작업 복원
|
||||
- 히스토리는 세션 동안만 유지
|
||||
|
||||
---
|
||||
|
||||
## 7. 테스트 완료 항목
|
||||
|
||||
### 7.1 API 연동 테스트 (2026-01-22)
|
||||
| 테스트 | 결과 |
|
||||
|--------|------|
|
||||
| 페이지 빌더에서 섹션 제목 수정 | ✅ "기본정보" → "기본정보 (빌더테스트)" |
|
||||
| API 저장 버튼 클릭 | ✅ `PUT /api/proxy/item-master/sections/92` (200 OK) |
|
||||
| 품목기준관리 페이지 반영 확인 | ✅ 변경된 제목 표시 확인 |
|
||||
|
||||
### 7.2 UI 수정 (2026-01-22)
|
||||
- 페이지 목록 호버 버튼에 배경색 추가 (긴 제목 가림 방지)
|
||||
|
||||
---
|
||||
|
||||
## 8. 알려진 이슈 / TODO
|
||||
|
||||
### 8.1 현재 이슈
|
||||
- [ ] 새 섹션/필드 생성 후 order 값 자동 계산 필요
|
||||
- [ ] BOM 섹션 타입 특수 처리 (자재명세표)
|
||||
- [ ] 드래그 앤 드롭 순서 변경 시 API order 업데이트
|
||||
|
||||
### 8.2 향후 개선 사항
|
||||
- [ ] 실시간 미리보기에서 실제 폼 렌더링 (DynamicItemForm 연동)
|
||||
- [ ] 필드 유효성 검사 규칙 편집 UI
|
||||
- [ ] 섹션/필드 복사-붙여넣기
|
||||
- [ ] 다중 선택 및 일괄 편집
|
||||
- [ ] 변경사항 diff 뷰어
|
||||
|
||||
### 8.3 Git 관련
|
||||
- 현재 `.gitignore`에 등록되어 있음: `src/app/**/dev/page-builder/`
|
||||
- 정식 배포 시 gitignore에서 제거 필요
|
||||
|
||||
---
|
||||
|
||||
## 9. 관련 파일
|
||||
|
||||
### 9.1 품목기준관리 (연동 대상)
|
||||
```
|
||||
src/app/[locale]/(protected)/master-data/item-master-data-management/page.tsx
|
||||
src/components/items/ItemMasterDataManagement/
|
||||
src/lib/api/item-master.ts
|
||||
```
|
||||
|
||||
### 9.2 동적 품목 폼 (렌더링 대상)
|
||||
```
|
||||
src/components/items/DynamicItemForm/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 참고 문서
|
||||
|
||||
- 초기 기획: `src/app/[locale]/(protected)/dev/page-builder/PLAN.md`
|
||||
- API 문서: `claudedocs/api/item-master-api.md` (있다면)
|
||||
|
||||
---
|
||||
|
||||
*마지막 업데이트: 2026-01-22*
|
||||
Reference in New Issue
Block a user