- 모달 컴포넌트에서 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>
299 lines
9.2 KiB
Markdown
299 lines
9.2 KiB
Markdown
# 페이지 빌더 (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*
|