docs: [plans] 양식 디자이너 고도화 계획 수립 (6 Phase)
- Phase 2: 블록 런타임 렌더러 + EAV 데이터 바인딩 - Phase 3: 결재선 블록 + 워크플로우 연동 - Phase 4: 동적 테이블 + 변수/매크로 시스템 - Phase 5: 수식 엔진 + 조건부 표시 + 이미지 블록 - Phase 6: 인쇄/PDF + Legacy Builder 대체 - INDEX.md에 계획 문서 등록
This commit is contained in:
@@ -261,6 +261,7 @@ docs/
|
||||
| [attendance-management-plan.md](plans/attendance-management-plan.md) | 근태현황 개발 계획 (Phase 1~2, HTMX 기반) |
|
||||
| [leave-management-plan.md](plans/leave-management-plan.md) | 휴가관리 모듈 개발 계획 (연차 발생/신청/승인/정책) |
|
||||
| [approval-management-system-plan.md](plans/approval-management-system-plan.md) | 결재관리 시스템 기획서 (전자결재 전체 설계: 기안~회수, DB 설계, 17개 양식, 4 Phase) |
|
||||
| [block-builder-evolution-plan.md](plans/block-builder-evolution-plan.md) | 양식 디자이너(Block Builder) 고도화 계획 (6 Phase: 렌더러→결재→동적테이블→수식→인쇄→Legacy 대체) |
|
||||
|
||||
### features/ - 기능별 문서
|
||||
|
||||
|
||||
706
sam/docs/plans/block-builder-evolution-plan.md
Normal file
706
sam/docs/plans/block-builder-evolution-plan.md
Normal file
@@ -0,0 +1,706 @@
|
||||
# 양식 디자이너(Block Builder) 고도화 계획
|
||||
|
||||
> **작성일**: 2026-03-06
|
||||
> **상태**: 계획 수립
|
||||
> **담당**: Claude Code + 개발팀
|
||||
> **관련**: [문서양식관리](../features/documents/mng-document-template.md) | [문서관리](../features/documents/mng-document-system.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 현재 상태 진단
|
||||
|
||||
### 1.1 구현 완료 (Phase 1 — 2026-02-28)
|
||||
|
||||
- 13개 블록 타입 (기본 6 + 폼 7)
|
||||
- 3패널 UI (팔레트 / 캔버스 / 속성)
|
||||
- SortableJS 드래그-앤-드롭 정렬
|
||||
- Undo/Redo (최대 50단계)
|
||||
- JSON 스키마 저장 (`document_templates.schema`)
|
||||
- 페이지 설정 (A4/A3/B5, 세로/가로, 여백)
|
||||
|
||||
### 1.2 핵심 미구현 사항
|
||||
|
||||
| 기능 | 상태 | 영향도 |
|
||||
|------|------|--------|
|
||||
| 문서 생성 시 블록 렌더링 | 미구현 | 블록 서식으로 문서 작성 불가 |
|
||||
| 결재선 블록 | 미구현 | 결재 워크플로우 연동 불가 |
|
||||
| 데이터 바인딩 (EAV 연동) | 미구현 | 입력값 저장/로드 불가 |
|
||||
| 동적 행 추가 | 미구현 | 검사 데이터 행 추가 불가 |
|
||||
| 변수/매크로 시스템 | 미구현 | 자동 값 주입 불가 |
|
||||
| 인쇄/PDF 출력 | 미구현 | 블록 문서 인쇄 불가 |
|
||||
| Columns 내부 블록 | 미구현 | 다단 레이아웃 활용 불가 |
|
||||
|
||||
> **결론**: 양식 디자이너는 **레이아웃 편집기**로만 동작. 실제 문서 생성/결재/인쇄에서는 Legacy Builder만 사용 가능.
|
||||
|
||||
---
|
||||
|
||||
## 2. 목표
|
||||
|
||||
Legacy Builder의 모든 기능을 양식 디자이너에서 지원하면서, 더 유연하고 확장 가능한 문서 시스템 구축.
|
||||
|
||||
**최종 목표:**
|
||||
```
|
||||
양식 디자이너로 서식 설계
|
||||
↓
|
||||
블록 스키마 기반 문서 생성 (데이터 입력)
|
||||
↓
|
||||
결재 워크플로우 (작성 → 검토 → 승인)
|
||||
↓
|
||||
인쇄/PDF 출력
|
||||
↓
|
||||
Legacy Builder 완전 대체
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 개발 로드맵 (6단계)
|
||||
|
||||
### Phase 2: 블록 런타임 렌더러 (기반 인프라)
|
||||
|
||||
> **목표**: 저장된 블록 스키마를 문서 생성/조회/인쇄에서 렌더링
|
||||
|
||||
#### 2-1. 블록 렌더러 엔진
|
||||
|
||||
**위치**: `mng/resources/views/documents/partials/block-renderer.blade.php`
|
||||
|
||||
```
|
||||
schema JSON 입력
|
||||
↓
|
||||
블록 타입별 Blade 컴포넌트 렌더링
|
||||
↓
|
||||
모드별 출력:
|
||||
- view 모드: 읽기 전용 HTML
|
||||
- edit 모드: 입력 폼 HTML
|
||||
- print 모드: 인쇄 최적화 HTML
|
||||
```
|
||||
|
||||
**핵심 구현:**
|
||||
|
||||
```php
|
||||
// BlockRendererService
|
||||
class BlockRendererService
|
||||
{
|
||||
public function render(array $schema, string $mode, array $data = []): string
|
||||
{
|
||||
$html = '';
|
||||
foreach ($schema['blocks'] as $block) {
|
||||
$html .= $this->renderBlock($block, $mode, $data);
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function renderBlock(array $block, string $mode, array $data): string
|
||||
{
|
||||
return match($block['type']) {
|
||||
'heading' => $this->renderHeading($block, $mode),
|
||||
'paragraph' => $this->renderParagraph($block, $mode),
|
||||
'table' => $this->renderTable($block, $mode, $data),
|
||||
'text_field' => $this->renderTextField($block, $mode, $data),
|
||||
'number_field' => $this->renderNumberField($block, $mode, $data),
|
||||
'date_field' => $this->renderDateField($block, $mode, $data),
|
||||
'select_field' => $this->renderSelectField($block, $mode, $data),
|
||||
'checkbox_field' => $this->renderCheckboxField($block, $mode, $data),
|
||||
'textarea_field' => $this->renderTextareaField($block, $mode, $data),
|
||||
'signature_field'=> $this->renderSignatureField($block, $mode, $data),
|
||||
'divider' => $this->renderDivider($block),
|
||||
'spacer' => $this->renderSpacer($block),
|
||||
'columns' => $this->renderColumns($block, $mode, $data),
|
||||
'approval_line' => $this->renderApprovalLine($block, $mode, $data),
|
||||
'dynamic_table' => $this->renderDynamicTable($block, $mode, $data),
|
||||
default => '',
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2-2. 문서 편집 화면 통합
|
||||
|
||||
**수정 대상**: `mng/resources/views/documents/edit.blade.php`
|
||||
|
||||
```
|
||||
Template 로드
|
||||
↓
|
||||
isBlockBuilder() 체크
|
||||
├── true → BlockRendererService::render(schema, 'edit', data)
|
||||
└── false → 기존 Legacy 렌더링 (변경 없음)
|
||||
```
|
||||
|
||||
#### 2-3. 데이터 바인딩 (EAV 매핑)
|
||||
|
||||
블록의 `binding` 속성으로 EAV 데이터와 연결:
|
||||
|
||||
```javascript
|
||||
// 블록 스키마 예시
|
||||
{
|
||||
"type": "text_field",
|
||||
"props": {
|
||||
"label": "제품명",
|
||||
"binding": "bf_product_name", // ← EAV field_key
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
저장 시:
|
||||
block.binding → document_data.field_key
|
||||
input.value → document_data.field_value
|
||||
block.id → document_data.section_id (블록 ID를 섹션으로 활용)
|
||||
|
||||
로드 시:
|
||||
document_data 조회 → field_key로 블록 매칭 → 값 주입
|
||||
```
|
||||
|
||||
**산출물:**
|
||||
|
||||
| 파일 | 작업 |
|
||||
|------|------|
|
||||
| `mng/app/Services/BlockRendererService.php` | 신규 생성 |
|
||||
| `mng/resources/views/documents/partials/block-renderer.blade.php` | 신규 생성 |
|
||||
| `mng/resources/views/documents/edit.blade.php` | 블록 빌더 분기 추가 |
|
||||
| `mng/resources/views/documents/show.blade.php` | 블록 빌더 분기 추가 |
|
||||
| `api/app/Services/DocumentService.php` | 블록 데이터 저장/로드 로직 |
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 결재선 블록
|
||||
|
||||
> **목표**: 블록 스키마 내에서 결재 워크플로우 정의
|
||||
|
||||
#### 3-1. approval_line 블록 타입 추가
|
||||
|
||||
**스키마:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "approval_line",
|
||||
"props": {
|
||||
"steps": [
|
||||
{ "role": "작성", "department": "", "name": "" },
|
||||
{ "role": "검토", "department": "", "name": "" },
|
||||
{ "role": "승인", "department": "", "name": "" }
|
||||
],
|
||||
"style": "horizontal",
|
||||
"showStamp": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3-2. 팔레트에 결재선 블록 추가
|
||||
|
||||
```javascript
|
||||
// 블록 팔레트 추가
|
||||
{ type: 'approval_line', icon: '✓', label: '결재선', category: '워크플로우' }
|
||||
```
|
||||
|
||||
#### 3-3. 속성 패널 결재선 편집기
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ 결재선 설정 │
|
||||
│ │
|
||||
│ [+ 단계 추가] │
|
||||
│ │
|
||||
│ 1. 역할: [작성 ▼] │
|
||||
│ 부서: [___________] │
|
||||
│ 이름: [___________] │
|
||||
│ │
|
||||
│ 2. 역할: [검토 ▼] │
|
||||
│ 부서: [___________] │
|
||||
│ 이름: [___________] │
|
||||
│ │
|
||||
│ 3. 역할: [승인 ▼] │
|
||||
│ 부서: [___________] │
|
||||
│ 이름: [___________] │
|
||||
│ │
|
||||
│ ☐ 직인 표시 │
|
||||
│ 스타일: [가로형 ▼] │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 3-4. 문서 생성 시 결재 연동
|
||||
|
||||
```
|
||||
블록 스키마 → approval_line 블록 추출
|
||||
↓
|
||||
DocumentApproval 레코드 자동 생성
|
||||
↓
|
||||
기존 결재 워크플로우 (submit → approve → reject) 그대로 활용
|
||||
```
|
||||
|
||||
**산출물:**
|
||||
|
||||
| 파일 | 작업 |
|
||||
|------|------|
|
||||
| `block-editor.blade.php` | approval_line 블록 추가 |
|
||||
| `block-canvas.blade.php` | 결재선 렌더링 |
|
||||
| `BlockRendererService.php` | 결재선 view/edit/print 렌더 |
|
||||
| `DocumentService.php` | 블록 결재선 → DocumentApproval 변환 |
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: 동적 테이블 블록 + 변수 시스템
|
||||
|
||||
> **목표**: 문서 작성 시 행 추가/삭제 가능한 테이블 + 자동 값 주입
|
||||
|
||||
#### 4-1. dynamic_table 블록 타입
|
||||
|
||||
기존 `table` 블록은 정적 (양식 설계 시 행 고정). `dynamic_table`은 문서 작성 시 행 동적 추가.
|
||||
|
||||
**스키마:**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "dynamic_table",
|
||||
"props": {
|
||||
"label": "검사 데이터",
|
||||
"columns": [
|
||||
{ "key": "col_item", "label": "항목", "type": "text", "width": 120 },
|
||||
{ "key": "col_standard", "label": "기준값", "type": "text", "width": 100 },
|
||||
{ "key": "col_measured", "label": "측정값", "type": "number", "width": 100 },
|
||||
{ "key": "col_result", "label": "판정", "type": "select",
|
||||
"options": ["합격", "불합격", "보류"], "width": 80 }
|
||||
],
|
||||
"minRows": 1,
|
||||
"maxRows": 50,
|
||||
"initialRows": 3,
|
||||
"showRowNumber": true,
|
||||
"binding": "inspection_data"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4-2. EAV 데이터 매핑
|
||||
|
||||
```
|
||||
dynamic_table 블록 데이터 저장:
|
||||
|
||||
document_data 레코드:
|
||||
section_id = (dynamic_table 블록 ID → section 매핑)
|
||||
column_id = (columns[].key → column 매핑)
|
||||
row_index = 0, 1, 2, ...
|
||||
field_key = "col_item", "col_standard", ...
|
||||
field_value = 입력값
|
||||
```
|
||||
|
||||
#### 4-3. 변수/매크로 시스템
|
||||
|
||||
**내장 변수:**
|
||||
|
||||
| 변수 | 값 | 설명 |
|
||||
|------|-----|------|
|
||||
| `{{today}}` | 현재 날짜 | YYYY-MM-DD |
|
||||
| `{{now}}` | 현재 시각 | YYYY-MM-DD HH:mm |
|
||||
| `{{user.name}}` | 로그인 사용자명 | |
|
||||
| `{{user.department}}` | 로그인 사용자 부서 | |
|
||||
| `{{doc.number}}` | 문서 번호 | 자동채번 |
|
||||
| `{{doc.title}}` | 문서 제목 | |
|
||||
| `{{template.company}}` | 서식 회사명 | |
|
||||
|
||||
**연결 데이터 변수 (linked data):**
|
||||
|
||||
| 변수 | 설명 |
|
||||
|------|------|
|
||||
| `{{item.name}}` | 연결 품목명 |
|
||||
| `{{item.code}}` | 연결 품목 코드 |
|
||||
| `{{order.number}}` | 연결 작업지시서 번호 |
|
||||
| `{{order.quantity}}` | 연결 수량 |
|
||||
|
||||
**변수 사용 예시 (블록 속성):**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "text_field",
|
||||
"props": {
|
||||
"label": "검사일자",
|
||||
"binding": "bf_inspection_date",
|
||||
"default": "{{today}}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"text": "작성자: {{user.name}} ({{user.department}})"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4-4. 변수 해석 엔진
|
||||
|
||||
```php
|
||||
// VariableResolver
|
||||
class VariableResolver
|
||||
{
|
||||
public function resolve(string $text, array $context): string
|
||||
{
|
||||
return preg_replace_callback('/\{\{(\w+(?:\.\w+)*)\}\}/', function ($m) use ($context) {
|
||||
return data_get($context, $m[1], $m[0]);
|
||||
}, $text);
|
||||
}
|
||||
|
||||
public function buildContext(Document $document, ?User $user = null): array
|
||||
{
|
||||
return [
|
||||
'today' => now()->format('Y-m-d'),
|
||||
'now' => now()->format('Y-m-d H:i'),
|
||||
'user' => [
|
||||
'name' => $user?->name,
|
||||
'department' => $user?->department?->name,
|
||||
],
|
||||
'doc' => [
|
||||
'number' => $document->document_number,
|
||||
'title' => $document->title,
|
||||
],
|
||||
'item' => $this->resolveLinkedItem($document),
|
||||
'order' => $this->resolveLinkedOrder($document),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**산출물:**
|
||||
|
||||
| 파일 | 작업 |
|
||||
|------|------|
|
||||
| `block-editor.blade.php` | dynamic_table 블록 추가 |
|
||||
| `BlockRendererService.php` | 동적 테이블 렌더링 (edit: 행 추가/삭제 UI) |
|
||||
| `mng/app/Services/VariableResolver.php` | 신규 생성 |
|
||||
| `DocumentService.php` | 동적 테이블 EAV 저장/로드 |
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: 고급 블록 + 조건부 로직
|
||||
|
||||
> **목표**: 수식 계산, 조건부 표시, 이미지 블록 등 고급 기능
|
||||
|
||||
#### 5-1. 수식 블록 (formula)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "formula_field",
|
||||
"props": {
|
||||
"label": "합계",
|
||||
"expression": "SUM(inspection_data.col_measured)",
|
||||
"format": "number",
|
||||
"decimal": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**지원 함수:**
|
||||
|
||||
| 함수 | 설명 | 예시 |
|
||||
|------|------|------|
|
||||
| `SUM()` | 합계 | `SUM(table.col_amount)` |
|
||||
| `AVG()` | 평균 | `AVG(table.col_measured)` |
|
||||
| `COUNT()` | 개수 | `COUNT(table.col_item)` |
|
||||
| `MIN()` / `MAX()` | 최솟값/최댓값 | `MIN(table.col_value)` |
|
||||
| `IF()` | 조건 | `IF(AVG(table.col_measured) > 5, "합격", "불합격")` |
|
||||
| `ROUND()` | 반올림 | `ROUND(AVG(table.col_measured), 2)` |
|
||||
|
||||
#### 5-2. 조건부 표시 (conditional visibility)
|
||||
|
||||
모든 블록에 `visibility` 속성 추가:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "paragraph",
|
||||
"props": {
|
||||
"text": "불합격 사유를 기재해 주세요.",
|
||||
"visibility": {
|
||||
"condition": "field",
|
||||
"field": "bf_judgement",
|
||||
"operator": "equals",
|
||||
"value": "불합격"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**연산자:**
|
||||
|
||||
| 연산자 | 설명 |
|
||||
|--------|------|
|
||||
| `equals` | 같으면 표시 |
|
||||
| `not_equals` | 다르면 표시 |
|
||||
| `contains` | 포함하면 표시 |
|
||||
| `greater_than` | 크면 표시 |
|
||||
| `less_than` | 작으면 표시 |
|
||||
| `is_empty` | 비어있으면 표시 |
|
||||
| `is_not_empty` | 비어있지 않으면 표시 |
|
||||
|
||||
#### 5-3. 이미지 블록
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "image",
|
||||
"props": {
|
||||
"label": "검사 사진",
|
||||
"source": "upload",
|
||||
"maxSize": 10,
|
||||
"accept": ["jpeg", "png", "webp"],
|
||||
"width": "100%",
|
||||
"align": "center"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5-4. Columns 내부 블록 (중첩 렌더링)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "columns",
|
||||
"props": {
|
||||
"count": 2,
|
||||
"ratio": "1:1",
|
||||
"children": [
|
||||
[
|
||||
{ "type": "text_field", "props": { "label": "품명" } },
|
||||
{ "type": "date_field", "props": { "label": "검사일" } }
|
||||
],
|
||||
[
|
||||
{ "type": "text_field", "props": { "label": "LOT NO" } },
|
||||
{ "type": "select_field", "props": { "label": "판정", "options": ["합격","불합격"] } }
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**산출물:**
|
||||
|
||||
| 파일 | 작업 |
|
||||
|------|------|
|
||||
| `block-editor.blade.php` | formula, image, conditional 블록 추가 |
|
||||
| `mng/app/Services/FormulaEngine.php` | 수식 해석 엔진 |
|
||||
| `BlockRendererService.php` | 조건부 표시, 수식 계산 렌더링 |
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: 인쇄/PDF + Legacy 대체
|
||||
|
||||
> **목표**: 블록 문서 인쇄 완성, Legacy Builder 완전 대체
|
||||
|
||||
#### 6-1. 인쇄 레이아웃
|
||||
|
||||
```
|
||||
print 모드 렌더링:
|
||||
- 페이지 설정 (A4/A3) 적용
|
||||
- 여백 적용
|
||||
- 폼 필드 → 값 표시 (입력란 제거)
|
||||
- 서명 → 서명 이미지 표시
|
||||
- 결재선 → 직인 표시
|
||||
- 페이지 넘김 (page-break) 자동 계산
|
||||
```
|
||||
|
||||
#### 6-2. PDF 내보내기
|
||||
|
||||
```
|
||||
블록 렌더러 (print 모드 HTML)
|
||||
↓
|
||||
Puppeteer / wkhtmltopdf
|
||||
↓
|
||||
PDF 파일 생성
|
||||
↓
|
||||
다운로드 또는 첨부
|
||||
```
|
||||
|
||||
#### 6-3. Legacy → Block 마이그레이션 도구
|
||||
|
||||
기존 Legacy 서식을 Block 스키마로 자동 변환:
|
||||
|
||||
```php
|
||||
// LegacyToBlockMigrator
|
||||
class LegacyToBlockMigrator
|
||||
{
|
||||
public function convert(DocumentTemplate $legacy): array
|
||||
{
|
||||
$blocks = [];
|
||||
|
||||
// 1. 결재선 → approval_line 블록
|
||||
if ($legacy->approvalLines->isNotEmpty()) {
|
||||
$blocks[] = $this->convertApprovalLines($legacy->approvalLines);
|
||||
}
|
||||
|
||||
// 2. 기본필드 → text_field / date_field 블록
|
||||
foreach ($legacy->basicFields as $field) {
|
||||
$blocks[] = $this->convertBasicField($field);
|
||||
}
|
||||
|
||||
// 3. 섹션 → heading + image 블록
|
||||
foreach ($legacy->sections as $section) {
|
||||
$blocks[] = ['type' => 'heading', 'props' => ['text' => $section->title]];
|
||||
if ($section->image_path) {
|
||||
$blocks[] = ['type' => 'image', 'props' => ['source' => $section->image_path]];
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 컬럼 → dynamic_table 블록
|
||||
if ($legacy->columns->isNotEmpty()) {
|
||||
$blocks[] = $this->convertColumns($legacy->columns);
|
||||
}
|
||||
|
||||
return [
|
||||
'version' => '1.0',
|
||||
'page' => ['size' => 'A4', 'orientation' => 'portrait'],
|
||||
'blocks' => $blocks,
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 6-4. Legacy Builder 비활성화
|
||||
|
||||
```
|
||||
Phase 6 완료 후:
|
||||
- 새 양식 생성: 양식 디자이너만 허용
|
||||
- 기존 Legacy 서식: 조회/편집 가능 (변환 유도)
|
||||
- Legacy Builder "새 양식" 버튼: "양식 디자이너" 사용 안내
|
||||
```
|
||||
|
||||
**산출물:**
|
||||
|
||||
| 파일 | 작업 |
|
||||
|------|------|
|
||||
| `mng/resources/views/documents/print-block.blade.php` | 인쇄 전용 뷰 |
|
||||
| `mng/app/Services/LegacyToBlockMigrator.php` | 변환 도구 |
|
||||
| `mng/app/Services/PdfExportService.php` | PDF 생성 |
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase별 우선순위 및 의존관계
|
||||
|
||||
```
|
||||
Phase 2: 블록 런타임 렌더러 ──────────────────────┐
|
||||
(렌더러 엔진, 데이터 바인딩, 문서 편집 통합) │
|
||||
│
|
||||
Phase 3: 결재선 블록 ─────────────────────┐ │
|
||||
(approval_line 블록, 결재 워크플로우) │ │
|
||||
↓ ↓
|
||||
Phase 4: 동적 테이블 + 변수 ──────→ Phase 5: 고급 블록
|
||||
(dynamic_table, 매크로) (수식, 조건부, 이미지)
|
||||
│
|
||||
↓
|
||||
Phase 6: 인쇄/PDF + Legacy 대체
|
||||
(마이그레이션 도구)
|
||||
```
|
||||
|
||||
| Phase | 의존 | 난이도 | 예상 범위 |
|
||||
|-------|------|--------|----------|
|
||||
| **Phase 2** | 없음 (기반) | 높음 | 렌더러 엔진 + EAV 매핑 |
|
||||
| **Phase 3** | Phase 2 | 중간 | 결재선 블록 + 워크플로우 연동 |
|
||||
| **Phase 4** | Phase 2 | 높음 | 동적 테이블 + 변수 해석 |
|
||||
| **Phase 5** | Phase 4 | 높음 | 수식 엔진 + 조건부 로직 |
|
||||
| **Phase 6** | Phase 3~5 | 중간 | 인쇄 + 마이그레이션 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 스키마 버전 관리
|
||||
|
||||
### 5.1 버전 규칙
|
||||
|
||||
| 버전 | Phase | 변경 내용 |
|
||||
|------|-------|----------|
|
||||
| `1.0` | Phase 1 (현재) | 기본 13개 블록 |
|
||||
| `2.0` | Phase 2~3 | 데이터 바인딩, approval_line 추가 |
|
||||
| `3.0` | Phase 4 | dynamic_table, 변수 시스템 |
|
||||
| `4.0` | Phase 5 | formula, conditional, image |
|
||||
|
||||
### 5.2 하위 호환
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "3.0",
|
||||
"page": { ... },
|
||||
"blocks": [ ... ],
|
||||
"variables": { ... },
|
||||
"migrations": {
|
||||
"from_1.0": "auto"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 이전 버전 스키마 자동 인식 및 업그레이드
|
||||
- 신규 블록 타입은 이전 버전에서 무시 (graceful degradation)
|
||||
|
||||
---
|
||||
|
||||
## 6. 신규 블록 타입 전체 목록
|
||||
|
||||
### Phase별 블록 추가 계획
|
||||
|
||||
| Phase | 블록 타입 | 카테고리 | 설명 |
|
||||
|-------|----------|---------|------|
|
||||
| 1 (완료) | `heading` | 기본 | 제목 |
|
||||
| 1 (완료) | `paragraph` | 기본 | 문단 |
|
||||
| 1 (완료) | `table` | 기본 | 정적 테이블 |
|
||||
| 1 (완료) | `columns` | 기본 | 다단 레이아웃 |
|
||||
| 1 (완료) | `divider` | 기본 | 구분선 |
|
||||
| 1 (완료) | `spacer` | 기본 | 여백 |
|
||||
| 1 (완료) | `text_field` | 폼 | 텍스트 입력 |
|
||||
| 1 (완료) | `number_field` | 폼 | 숫자 입력 |
|
||||
| 1 (완료) | `date_field` | 폼 | 날짜 입력 |
|
||||
| 1 (완료) | `select_field` | 폼 | 드롭다운 |
|
||||
| 1 (완료) | `checkbox_field` | 폼 | 체크박스 |
|
||||
| 1 (완료) | `textarea_field` | 폼 | 장문 텍스트 |
|
||||
| 1 (완료) | `signature_field` | 폼 | 서명 |
|
||||
| **3** | `approval_line` | 워크플로우 | 결재선 |
|
||||
| **4** | `dynamic_table` | 데이터 | 동적 행 테이블 |
|
||||
| **5** | `formula_field` | 데이터 | 수식 계산 |
|
||||
| **5** | `image` | 미디어 | 이미지 업로드/표시 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 기술 스택 정리
|
||||
|
||||
| 구성 요소 | 기술 | 비고 |
|
||||
|----------|------|------|
|
||||
| 블록 에디터 UI | Alpine.js + Blade | 기존 유지 |
|
||||
| 드래그-앤-드롭 | SortableJS | 기존 유지 |
|
||||
| 블록 렌더러 | PHP (BlockRendererService) | 신규 |
|
||||
| 변수 해석 | PHP (VariableResolver) | 신규 |
|
||||
| 수식 엔진 | PHP (FormulaEngine) | 신규 |
|
||||
| 데이터 저장 | EAV (document_data) | 기존 테이블 활용 |
|
||||
| 결재 워크플로우 | DocumentApproval | 기존 로직 활용 |
|
||||
| 인쇄 | CSS @media print | 신규 |
|
||||
| PDF | Puppeteer 또는 wkhtmltopdf | 신규 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 위험 요소 및 대응
|
||||
|
||||
| 위험 | 영향 | 대응 |
|
||||
|------|------|------|
|
||||
| EAV 매핑 복잡도 | 블록 ID ↔ section_id 매핑 불일치 | 블록 ID를 section 대용으로 사용, 매핑 테이블 추가 검토 |
|
||||
| Legacy 데이터 호환 | 기존 문서 데이터 접근 불가 | Legacy 서식 문서는 기존 방식 유지, 신규 서식만 블록 적용 |
|
||||
| 수식 엔진 보안 | 임의 코드 실행 위험 | 화이트리스트 함수만 허용, eval 사용 금지 |
|
||||
| 인쇄 레이아웃 | 브라우저별 차이 | CSS @page 규격 준수, PDF 변환 권장 |
|
||||
| 스키마 마이그레이션 | 버전 업그레이드 시 데이터 손실 | 하위 호환 보장, 자동 업그레이드 로직 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 성공 기준
|
||||
|
||||
| 기준 | 측정 방법 |
|
||||
|------|----------|
|
||||
| 블록 서식으로 문서 생성 가능 | Phase 2 완료 후 테스트 |
|
||||
| 결재 워크플로우 정상 동작 | Phase 3 완료 후 테스트 |
|
||||
| 동적 행 추가/삭제 | Phase 4 완료 후 테스트 |
|
||||
| 변수 자동 주입 | Phase 4 완료 후 테스트 |
|
||||
| Legacy 서식 자동 변환 | Phase 6 완료 후 테스트 |
|
||||
| 인쇄 품질 A4 기준 정상 | Phase 6 완료 후 테스트 |
|
||||
|
||||
---
|
||||
|
||||
## 관련 문서
|
||||
|
||||
- [문서양식관리](../features/documents/mng-document-template.md) — 현재 양식관리 기술문서
|
||||
- [문서관리 시스템](../features/documents/mng-document-system.md) — 문서 생성/결재 기술문서
|
||||
- [문서관리 API](../features/documents/README.md) — API 엔드포인트 목록
|
||||
|
||||
---
|
||||
|
||||
**최종 업데이트**: 2026-03-06
|
||||
Reference in New Issue
Block a user