- 모달 컴포넌트에서 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>
9.2 KiB
9.2 KiB
페이지 빌더 (Page Builder) 구현 문서
작성일: 2026-01-22 상태: 개발 중 (테스트 버전) 경로:
/dev/page-builderGit 상태:.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
interface BuilderPage {
id: string;
name: string; // 페이지 이름 (예: "소모품 등록")
itemType: ItemType; // 품목 유형 (FG, PT, SM, RM, CS)
sections: BuilderSection[];
createdAt: string;
updatedAt: string;
}
3.2 BuilderSection
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
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 (조건부 표시)
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 사용:
// 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)
const syncPageToAPI = async (page: BuilderPage) => {
// 1. 원본 데이터와 현재 데이터 비교
// 2. 삭제된 섹션/필드 → API DELETE 호출
// 3. 새로 추가된 섹션/필드 → API CREATE 호출
// 4. 수정된 섹션/필드 → API UPDATE 호출
// 5. 완료 후 API에서 최신 데이터 다시 로드
};
4.4 타입 변환 (transformers.ts)
// 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
주요 상태:
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 기본 워크플로우
/dev/page-builder접속- API 모드 ON 확인 (우측 상단 토글)
- 좌측에서 페이지 선택 (소모품, 원자재 등)
- 컴포넌트 팔레트에서 섹션/필드를 캔버스로 드래그
- 캔버스에서 요소 클릭 → 우측 패널에서 속성 편집
- "API 저장" 버튼 클릭 → 백엔드에 저장
- 품목기준관리 페이지에서 변경사항 확인
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