From a2040a228a614d8805adc37ba0026a8b3e6000e4 Mon Sep 17 00:00:00 2001 From: kjs Date: Fri, 13 Mar 2026 16:02:02 +0900 Subject: [PATCH] docs: add document sync rule for component and DB changes - Introduced a new document sync rule to ensure that related documentation is updated whenever components are added or modified, or when there are changes to the database structure. - Specified the documents that must be updated, including the full-screen analysis and V2 component usage guide, along with detailed instructions on how to update them. - This addition aims to enforce consistency and accuracy in documentation, facilitating better collaboration and adherence to development standards. Made-with: Cursor --- .cursor/rules/document-sync-rule.mdc | 38 + .../00_analysis/full-screen-analysis.md | 485 ------- .../00_analysis/v2-component-usage-guide.md | 846 ------------ .../full-screen-analysis.md | 952 ++++++++++++++ .../v2-component-usage-guide.md | 1146 +++++++++++++++++ 5 files changed, 2136 insertions(+), 1331 deletions(-) create mode 100644 .cursor/rules/document-sync-rule.mdc delete mode 100644 docs/screen-implementation-guide/00_analysis/full-screen-analysis.md delete mode 100644 docs/screen-implementation-guide/00_analysis/v2-component-usage-guide.md create mode 100644 docs/screen-implementation-guide/01_reference_guides/full-screen-analysis.md create mode 100644 docs/screen-implementation-guide/01_reference_guides/v2-component-usage-guide.md diff --git a/.cursor/rules/document-sync-rule.mdc b/.cursor/rules/document-sync-rule.mdc new file mode 100644 index 00000000..a2b7e13a --- /dev/null +++ b/.cursor/rules/document-sync-rule.mdc @@ -0,0 +1,38 @@ +--- +description: 컴포넌트 추가/수정 또는 DB 구조 변경 시 관련 문서를 항상 최신화하도록 강제하는 규칙 +globs: + - "frontend/lib/registry/components/**/*.tsx" + - "frontend/components/v2/**/*.tsx" + - "db/migrations/**/*.sql" + - "backend-node/src/types/ddl.ts" +--- + +# 컴포넌트 및 DB 구조 변경 시 문서 동기화 규칙 + +## 🚨 핵심 원칙 (절대 준수) + +새로운 V2 컴포넌트를 생성하거나 기존 컴포넌트의 설정(overrides)을 변경할 때, 또는 DB 테이블 구조나 화면 생성 파이프라인이 변경될 때는 **반드시** 아래 두 문서를 함께 업데이트해야 합니다. + +1. `docs/screen-implementation-guide/01_reference_guides/full-screen-analysis.md` (전체 레퍼런스) +2. `docs/screen-implementation-guide/01_reference_guides/v2-component-usage-guide.md` (실행 가이드) + +## 📌 업데이트 대상 및 방법 + +### 1. V2 컴포넌트 신규 추가 또는 속성(Props/Overrides) 변경 시 +- **`full-screen-analysis.md`**: `3. 컴포넌트 전체 설정 레퍼런스` 섹션에 해당 컴포넌트의 모든 설정값(타입, 기본값, 설명)을 표 형태로 추가/수정하세요. +- **`v2-component-usage-guide.md`**: + - `7. Step 6: screen_layouts_v2 INSERT`의 컴포넌트 url 매핑표에 추가하세요. + - `16. 컴포넌트 빠른 참조표`에 추가하세요. + - 필요한 경우 `8. 패턴별 layout_data 완전 예시`에 새로운 패턴을 추가하세요. + +### 2. DB 테이블 구조 또는 화면 생성 로직 변경 시 +- **`full-screen-analysis.md`**: `2. DB 테이블 스키마` 섹션의 테이블 구조(컬럼, 타입, 설명)를 최신화하세요. +- **`v2-component-usage-guide.md`**: + - `Step 1` ~ `Step 7`의 SQL 템플릿이 변경된 구조와 일치하는지 확인하고 수정하세요. + - 특히 `INSERT` 문의 컬럼 목록과 `VALUES` 형식이 정확한지 검증하세요. + +## ⚠️ AI 에이전트 행동 지침 + +1. 사용자가 컴포넌트 코드를 수정해달라고 요청하면, 수정 완료 후 **"관련 가이드 문서도 업데이트할까요?"** 라고 반드시 물어보세요. +2. 사용자가 DB 마이그레이션 스크립트를 작성해달라고 하거나 핵심 시스템 테이블을 건드리면, 가이드 문서의 SQL 템플릿도 수정해야 하는지 확인하세요. +3. 가이드 문서 업데이트 시 JSON 예제 안에 `//` 같은 주석을 넣지 않도록 주의하세요 (DB 파싱 에러 방지). diff --git a/docs/screen-implementation-guide/00_analysis/full-screen-analysis.md b/docs/screen-implementation-guide/00_analysis/full-screen-analysis.md deleted file mode 100644 index 378b13b4..00000000 --- a/docs/screen-implementation-guide/00_analysis/full-screen-analysis.md +++ /dev/null @@ -1,485 +0,0 @@ -# WACE ERP 화면 구성 시스템 전체 분석 - -> **최종 업데이트**: 2026-03-13 -> **용도**: LLM 챗봇 / AI 에이전트가 화면 개발 요청을 받았을 때 참조하는 시스템 구조 레퍼런스 -> **핵심 규칙**: 사용자 업무 화면은 React 코드(.tsx)로 직접 만들지 않는다. DB 등록(screen_definitions + screen_layouts_v2 + menu_info)으로 구현한다. - ---- - -## 1. 시스템 아키텍처 요약 - -### 1.1 화면 렌더링 파이프라인 - -``` -[DB] screen_definitions (화면 정의) - + screen_layouts_v2 (레이아웃 JSON) - + menu_info (메뉴 등록) - │ - ▼ -[Backend API] GET /api/screens/:screenId - │ - ▼ -[Frontend] /screens/[screenId]/page.tsx - │ - ▼ -[Converter] layoutV2Converter.ts → V2 JSON을 Legacy 포맷으로 변환 - │ - ▼ -[Renderer] ResponsiveGridRenderer → RealtimePreview → DynamicComponentRenderer - │ - ▼ -[Registry] ComponentRegistry.getComponent(componentType) → 실제 React 컴포넌트 렌더링 -``` - -### 1.2 화면 유형 분류 - -| 구분 | 구현 방식 | 코드 위치 | 예시 | -|------|----------|----------|------| -| **사용자 업무 화면** | DB 등록 (SQL INSERT) | screen_definitions + screen_layouts_v2 | 수주관리, 품목정보, BOM관리 | -| **관리자 메뉴** | React 코드 직접 작성 | frontend/app/(main)/admin/*/page.tsx | 사용자관리, 권한관리, 시스템설정 | - -**절대 규칙**: 사용자 업무 화면(생산, 영업, 구매, 물류, 품질 등)은 React 하드코딩 금지. 반드시 DB 등록 방식으로 구현. - ---- - -## 2. V2 컴포넌트 전체 목록 (32개) - -> 모든 컴포넌트는 `v2-` 접두사를 사용한다. 접두사 없는 컴포넌트는 레거시이므로 사용 금지. - -### 2.1 입력 컴포넌트 (9개) - -| ID | 용도 | 핵심 설정 | -|----|------|----------| -| `v2-input` | 텍스트, 숫자, 비밀번호, textarea, 슬라이더, 컬러, 버튼 | inputType, format(email/tel/url/currency/biz_no), required, readonly, maxLength | -| `v2-select` | 드롭다운, 콤보박스, 라디오, 체크박스, 태그, 토글, 스왑 | mode, source(static/code/db/api/entity/category/distinct/select), searchable, multiple, cascading | -| `v2-date` | 날짜, 시간, 날짜시간, 날짜범위, 월, 연도 | dateType(date/time/datetime), format, range, minDate, maxDate | -| `v2-file-upload` | 파일/이미지 업로드, 다중 업로드 | accept, maxSize, multiple | -| `v2-media` | 이미지, 비디오, 오디오 표시 | mediaType | -| `v2-location-swap-selector` | 출발지/도착지 선택 및 교환 | - | -| `v2-rack-structure` | 창고 랙 위치 일괄 생성 | columns, rows | -| `v2-process-work-standard` | 품목별 공정 작업기준 관리 (Pre/In/Post-Work) | - | -| `v2-item-routing` | 품목별 라우팅 버전 및 공정 순서 관리 (3단계 계층) | - | - -### 2.2 표시/데이터 컴포넌트 (10개) - -| ID | 용도 | 핵심 설정 | -|----|------|----------| -| `v2-table-list` | 데이터 테이블 (조회/편집, 페이지네이션, 정렬, 필터) | selectedTable, columns, pagination, displayMode(table/card), checkbox, linkedFilters | -| `v2-table-grouped` | 그룹화 테이블 (접기/펼치기, 그룹별 집계) | groupConfig(groupByColumn, summary), v2-table-list 기반 확장 | -| `v2-table-search-widget` | 테이블 검색/필터/그룹 바 | autoSelectFirstTable, showTableSelector | -| `v2-pivot-grid` | 다차원 피벗 분석 (행/열/데이터/필터 영역) | fields(area, summaryType, groupInterval), dataSource | -| `v2-text-display` | 라벨, 제목, 설명 텍스트 표시 | fontSize, fontWeight, color, textAlign | -| `v2-card-display` | 테이블 데이터를 카드 형태로 표시 | cardsPerRow, columnMapping(title/subtitle/image/status), cardStyle | -| `v2-aggregation-widget` | 합계, 평균, 개수, 최대, 최소 집계 카드 | items, filters, layout | -| `v2-status-count` | 상태별 건수 카드 표시 | statusField, countField | -| `v2-numbering-rule` | 자동 코드/번호 채번 (접두사+날짜+순번) | rule, prefix, format | -| `v2-category-manager` | 트리 기반 카테고리 관리 (3단계 계층) | - | - -### 2.3 레이아웃 컴포넌트 (8개) - -| ID | 용도 | 핵심 설정 | -|----|------|----------| -| `v2-split-panel-layout` | 마스터-디테일 좌우 분할 | splitRatio, leftPanel(displayMode/tableName), rightPanel(relation/foreignKey), additionalTabs | -| `v2-tabs-widget` | 탭 전환, 탭 내 컴포넌트 배치 | tabs(id/label/components), defaultTab, orientation | -| `v2-section-card` | 제목+테두리가 있는 그룹화 컨테이너 | title, collapsible | -| `v2-section-paper` | 배경색 기반 미니멀 그룹화 컨테이너 | backgroundColor, padding | -| `v2-divider-line` | 영역 구분선 | orientation, thickness | -| `v2-split-line` | 캔버스 좌우 분할용 드래그 가능 세로선 | - | -| `v2-repeat-container` | 데이터 수만큼 내부 컴포넌트 반복 렌더링 | dataSourceType, layout, gridColumns | -| `v2-repeater` | 인라인/모달/버튼 모드 반복 데이터 관리 | mode(inline/modal/button) | - -### 2.4 특수/비즈니스 컴포넌트 (5개) - -| ID | 용도 | 핵심 설정 | -|----|------|----------| -| `v2-button-primary` | 저장/삭제/조회 등 액션 버튼 | text, actionType, variant, webTypeConfig.dataflowConfig | -| `v2-timeline-scheduler` | 간트차트형 일정/계획 시각화 (드래그/리사이즈) | selectedTable, resourceTable, fieldMapping, zoomLevel, editable | -| `v2-approval-step` | 결재 단계 스테퍼 시각화 | - | -| `v2-bom-tree` | BOM 계층 트리 표시 (정전개/역전개) | - | -| `v2-bom-item-editor` | BOM 하위품목 트리 편집 | - | - ---- - -## 3. 화면 UI 패턴 분류 (7가지) - -> AI가 화면 개발 요청을 받았을 때, 아래 패턴 중 해당하는 것을 선택하여 구현한다. - -### 패턴 A: 기본 마스터 (검색 + 테이블) - -**적용 비율**: 약 50% (가장 흔함) -**적용 화면**: 코드관리, 부서정보, 창고정보, 검사기준, 불량관리, 공급업체관리 등 - -``` -┌─────────────────────────────────────────────────┐ -│ v2-table-search-widget │ -│ [검색필드1] [검색필드2] [조회] [엑셀] │ -├─────────────────────────────────────────────────┤ -│ v2-table-list │ -│ 제목 [신규] [삭제] │ -│ ─────────────────────────────────────────────── │ -│ □ | 코드 | 이름 | 상태 | 등록일 │ -└─────────────────────────────────────────────────┘ -``` - -**필수 컴포넌트**: `v2-table-search-widget` (1개) + `v2-table-list` (1개) - -### 패턴 B: 마스터-디테일 (좌우 분할) - -**적용 비율**: 약 25% -**적용 화면**: 공정관리, 수주관리, 견적관리, 품목라우팅 등 - -``` -┌──────────────────┬──────────────────────────────┐ -│ 마스터 테이블 │ 디테일 테이블/폼 │ -│ (좌측 패널) │ (우측 패널) │ -│ □ A001 항목1 │ [상세 정보 테이블] │ -│ □ A002 항목2 ← │ │ -└──────────────────┴──────────────────────────────┘ - v2-split-panel-layout -``` - -**필수 컴포넌트**: `v2-split-panel-layout` (1개) - -### 패턴 C: 마스터-디테일 + 탭 - -**적용 비율**: 약 10% -**적용 화면**: 거래처관리, 설비정보, 품목정보 등 - -``` -┌──────────────────┬──────────────────────────────┐ -│ 마스터 테이블 │ [기본] [이력] [첨부] │ -│ │ ┌────────────────────────┐ │ -│ □ A001 거래처1 │ │ 탭별 컨텐츠 │ │ -│ □ A002 거래처2 ← │ └────────────────────────┘ │ -└──────────────────┴──────────────────────────────┘ -``` - -**필수 컴포넌트**: `v2-split-panel-layout` (1개, rightPanel에 additionalTabs 설정) - -### 패턴 D: 카드 뷰 - -**적용 비율**: 약 5% -**적용 화면**: 설비정보, 대시보드, 상품 카탈로그 등 - -``` -┌─────────────────────────────────────────────────┐ -│ v2-table-search-widget │ -├─────────────────────────────────────────────────┤ -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ -│ │ [이미지]│ │ [이미지]│ │ [이미지]│ │ -│ │ 제목 │ │ 제목 │ │ 제목 │ │ -│ │ 설명 │ │ 설명 │ │ 설명 │ │ -│ └─────────┘ └─────────┘ └─────────┘ │ -│ v2-card-display │ -└─────────────────────────────────────────────────┘ -``` - -**필수 컴포넌트**: `v2-card-display` (1개) + `v2-table-search-widget` (선택) - -### 패턴 E: 피벗 분석 - -**적용 비율**: 약 3% -**적용 화면**: 매출분석, 재고현황, 생산실적 분석 등 - -**필수 컴포넌트**: `v2-pivot-grid` (1개) - -### 패턴 F: 그룹화 테이블 - -**적용 비율**: 약 5% -**적용 화면**: 품목정보(카테고리별), 입출고관리(구분별), 작업지시(공정별) 등 - -``` -┌─────────────────────────────────────────────────┐ -│ [전체 펼치기] [전체 접기] │ -├─────────────────────────────────────────────────┤ -│ ▼ □ 그룹A 수량: 150 3건 │ -│ ├─ □ 항목1 50개 │ -│ ├─ □ 항목2 50개 │ -│ └─ □ 항목3 50개 │ -│ ► □ 그룹B 수량: 200 2건 │ -└─────────────────────────────────────────────────┘ -``` - -**필수 컴포넌트**: `v2-table-grouped` (1개) - -### 패턴 G: 타임라인/간트차트 - -**적용 비율**: 약 2% -**적용 화면**: 생산계획관리, 설비가동현황 등 - -``` -┌────────────┬─────────────────────────────────────┐ -│ │ 15(수) │ 16(목) │ 17(금) │ 18(토) │ -├────────────┼─────────────────────────────────────┤ -│ 설비A │ ████████████████ │ -│ 설비B │ █████████████████████ │ -│ 설비C │ ████████████████ │ -└────────────┴─────────────────────────────────────┘ -``` - -**필수 컴포넌트**: `v2-timeline-scheduler` (1개) - ---- - -## 4. 패턴 판단 의사결정 트리 - -> AI가 화면 요청을 받았을 때 이 트리를 따라 패턴을 결정한다. - -``` -Q1. 시간축 기반 일정/간트차트가 필요한가? -├─ YES → 패턴 G (타임라인) -└─ NO ↓ - -Q2. 다차원 집계/피벗 분석이 필요한가? -├─ YES → 패턴 E (피벗) -└─ NO ↓ - -Q3. 데이터를 그룹별로 묶어서 접기/펼치기가 필요한가? -├─ YES → 패턴 F (그룹화 테이블) -└─ NO ↓ - -Q4. 이미지+정보를 카드 형태로 표시하는가? -├─ YES → 패턴 D (카드 뷰) -└─ NO ↓ - -Q5. 마스터 테이블 선택 시 연관 디테일이 필요한가? -├─ YES → Q5-1. 디테일에 탭이 필요한가? -│ ├─ YES → 패턴 C (마스터-디테일+탭) -│ └─ NO → 패턴 B (마스터-디테일) -└─ NO → 패턴 A (기본 마스터) -``` - ---- - -## 5. 화면 구현 불가능/제한 사항 - -### 5.1 현재 불가능 (별도 React 개발 필요) - -| 기능 | 상태 | 대안 | -|------|------|------| -| 칸반 보드 (드래그앤드롭) | 미지원 | 별도 React 컴포넌트 | -| 모바일 네이티브 앱 스타일 | 미지원 | 별도 개발 | -| 복잡한 차트 (line, bar, pie) | 미지원 | 외부 라이브러리 연동 | - -### 5.2 이전에 불가능했으나 현재 지원되는 기능 - -| 기능 | 지원 컴포넌트 | 추가 시점 | -|------|-------------|----------| -| 그룹화 테이블 | `v2-table-grouped` | 2026-01 | -| 타임라인/간트차트 | `v2-timeline-scheduler` | 2026-01 | -| BOM 트리 (정전개/역전개) | `v2-bom-tree` | 2026-02 | -| BOM 하위품목 편집 | `v2-bom-item-editor` | 2026-02 | -| 결재 스테퍼 | `v2-approval-step` | 2026-02 | - -### 5.3 권장하지 않는 조합 - -| 조합 | 이유 | -|------|------| -| 3단계 이상 중첩 분할 | 화면 복잡도 증가, 성능 저하 | -| 탭 안에 탭 | 사용성 저하 | -| 한 화면에 3개 이상 테이블 | 데이터 로딩 성능 | -| 피벗 + 상세 테이블 동시 | 데이터 과부하 | - ---- - -## 6. 화면별 구현 현황 (메뉴 분류) - -### 6.1 기준정보 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 회사정보 | A | 완전 지원 | | -| 부서정보 | A | 완전 지원 | | -| 품목정보 | F 또는 A | 완전 지원 | 카테고리별 그룹화 시 F | -| BOM관리 | B + v2-bom-tree | 완전 지원 | v2-bom-tree로 트리 구현 | -| 공정정보관리 | B | 완전 지원 | | -| 공정작업기준 | A | 완전 지원 | v2-process-work-standard 활용 가능 | -| 품목라우팅 | B | 완전 지원 | v2-item-routing 활용 가능 | - -### 6.2 영업관리 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 수주관리 | B | 완전 지원 | | -| 견적관리 | B | 완전 지원 | | -| 거래처관리 | C | 완전 지원 | 탭(기본/이력/첨부) | -| 판매품목정보 | A | 완전 지원 | | -| 출하계획관리 | A | 완전 지원 | | - -### 6.3 생산관리 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 생산계획관리 | G | 완전 지원 | v2-timeline-scheduler | -| 생산관리 | A | 완전 지원 | | -| 생산실적관리 | A | 완전 지원 | | -| 작업지시 | F | 완전 지원 | 공정별 그룹화 | -| 공정관리 | B | 완전 지원 | | - -### 6.4 구매관리 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 발주관리 | A | 완전 지원 | | -| 공급업체관리 | A | 완전 지원 | | -| 구매입고 | A | 완전 지원 | | - -### 6.5 설비관리 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 설비정보 | C 또는 D | 완전 지원 | 카드뷰 또는 탭 | - -### 6.6 물류관리 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 창고정보관리 | A | 완전 지원 | | -| 입출고관리 | F | 완전 지원 | 구분별 그룹화 | -| 재고현황 | A 또는 E | 완전 지원 | 피벗 분석도 가능 | - -### 6.7 품질관리 - -| 화면명 | 패턴 | 구현 가능 | 비고 | -|--------|------|----------|------| -| 검사기준 | A | 완전 지원 | | -| 검사정보관리 | C | 완전 지원 | 탭(수입검사/공정검사/출하검사) | -| 검사장비관리 | A | 완전 지원 | | -| 불량관리 | A | 완전 지원 | | -| 클레임관리 | A | 완전 지원 | | - ---- - -## 7. 컴포넌트 커버리지 요약 - -| 항목 | 수치 | -|------|------| -| 전체 분석 대상 화면 | 26개 | -| V2 컴포넌트로 구현 가능 | **26개 (100%)** | -| 등록된 V2 컴포넌트 수 | 32개 | -| 화면 UI 패턴 수 | 7가지 (A~G) | - -**이전 대비 변경 사항**: -- BOM 트리 지원 추가 (v2-bom-tree) → BOM관리 완전 지원 -- 그룹화 테이블 지원 추가 (v2-table-grouped) → 품목정보, 입출고 등 완전 지원 -- 타임라인 지원 추가 (v2-timeline-scheduler) → 생산계획관리 완전 지원 -- 결재 스테퍼 추가 (v2-approval-step) → 결재 프로세스 시각화 가능 -- 전체 커버리지: 65% → **100%** 달성 - ---- - -## 8. UI vs 비즈니스 로직 분리 구조 - -``` -┌───────────────────────────────┬───────────────────────────────────┐ -│ UI 레이아웃 │ 제어관리 (비즈니스 로직) │ -│ screen_layouts_v2 (JSON) │ dataflow_diagrams (JSONB) │ -├───────────────────────────────┼───────────────────────────────────┤ -│ - 컴포넌트 배치/크기/위치 │ - 버튼 클릭 시 액션 (INSERT 등) │ -│ - 검색 필드 구성 │ - 조건부 실행 │ -│ - 테이블 컬럼 표시/숨김 │ - 다중 행 일괄 처리 │ -│ - 카드/탭/분할 레이아웃 │ - 테이블 간 데이터 이동 │ -│ - 페이지네이션/정렬 설정 │ - 외부 시스템 호출 │ -└───────────────────────────────┴───────────────────────────────────┘ -``` - -**핵심**: V2 컴포넌트는 UI만 담당한다. 버튼 클릭 시 INSERT/UPDATE/DELETE 같은 비즈니스 로직은 `dataflow_diagrams` 테이블에서 별도 설정한다. - -### 비즈니스 로직 실행 흐름 - -``` -v2-button-primary 클릭 - → webTypeConfig.dataflowConfig 확인 - → ImprovedButtonActionExecutor 실행 - 1. Before 제어 실행 (조건 체크) - 2. Main 액션 실행 (save/delete/update) - 3. After 제어 실행 (후처리) -``` - ---- - -## 9. 화면 개발 순서 (AI 에이전트용) - -``` -Step 1: DB 테이블 생성 - └─ 모든 컬럼 VARCHAR(500), 기본 5개 컬럼 필수 (id, created_date, updated_date, writer, company_code) - └─ table_labels, table_type_columns, column_labels 메타데이터 등록 - -Step 2: screen_definitions INSERT - └─ screen_code = '{company_code}_{순번}' - └─ table_name = 메인 테이블명 - -Step 3: screen_layouts_v2 INSERT - └─ layout_data = V2 레이아웃 JSON (version: "2.0", components 배열) - └─ 패턴(A~G)에 맞는 컴포넌트 배치 - -Step 4: dataflow_diagrams INSERT (비즈니스 로직이 필요한 경우) - └─ 버튼별 액션 정의 (INSERT/UPDATE/DELETE) - └─ 조건, 필드 매핑 설정 - -Step 5: menu_info INSERT - └─ menu_url = '/screen/{screen_code}' - └─ 적절한 parent_id로 메뉴 트리에 배치 -``` - ---- - -## 10. DB 테이블 구조 레퍼런스 - -### screen_definitions - -| 컬럼 | 타입 | 설명 | -|------|------|------| -| screen_id | integer PK (시퀀스) | 화면 고유 ID | -| screen_name | varchar(100) | 화면명 | -| screen_code | varchar(50) UNIQUE | 화면 코드 (`{company_code}_{순번}`) | -| table_name | varchar(100) | 기본 테이블명 | -| company_code | varchar(50) | 회사 코드 | -| description | text | 설명 | -| is_active | char(1) | Y/N/D | - -### screen_layouts_v2 - -| 컬럼 | 타입 | 설명 | -|------|------|------| -| layout_id | integer PK | 레이아웃 ID | -| screen_id | integer FK | 화면 ID | -| company_code | varchar(20) | 회사 코드 | -| layer_id | integer | 레이어 ID (1=기본) | -| layout_data | jsonb | 전체 레이아웃 JSON | - -### layout_data JSON 구조 - -```json -{ - "version": "2.0", - "components": [ - { - "id": "comp_xxx", - "url": "@/lib/registry/components/v2-table-list", - "position": { "x": 0, "y": 150, "z": 1 }, - "size": { "width": 1920, "height": 600 }, - "displayOrder": 0, - "overrides": { - "label": "품목 목록", - "tableName": "item_info", - "columns": [ - { "columnName": "item_code", "displayName": "품목코드", "visible": true } - ], - "pagination": { "enabled": true, "pageSize": 20 } - } - } - ], - "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, - "screenResolution": { "width": 1920, "height": 1080 } -} -``` - -### menu_info (주요 컬럼) - -| 컬럼 | 설명 | -|------|------| -| objid / menu_id | 메뉴 PK | -| screen_id | 연결된 화면 ID | -| menu_url | 메뉴 URL (`/screen/{screen_code}`) | -| company_code | 회사 코드 | diff --git a/docs/screen-implementation-guide/00_analysis/v2-component-usage-guide.md b/docs/screen-implementation-guide/00_analysis/v2-component-usage-guide.md deleted file mode 100644 index 1966a3c7..00000000 --- a/docs/screen-implementation-guide/00_analysis/v2-component-usage-guide.md +++ /dev/null @@ -1,846 +0,0 @@ -# V2 컴포넌트 사용 가이드 (LLM/챗봇용) - -> **최종 업데이트**: 2026-03-13 -> **용도**: LLM 챗봇이 화면 개발 요청을 받았을 때, 어떤 컴포넌트를 어떤 설정으로 사용해야 하는지 판단하는 레퍼런스 -> **버전**: 2.0.0 -> **대상**: AI 에이전트, 챗봇, 화면 설계자 - ---- - -## 0. 핵심 규칙 (반드시 준수) - -1. **사용자 업무 화면은 React 코드로 직접 만들지 않는다** → DB에 `screen_definitions` + `screen_layouts_v2` + `menu_info` INSERT로 구현 -2. **모든 컴포넌트는 `v2-` 접두사** → `v2-` 없는 레거시 컴포넌트 사용 금지 -3. **UI와 로직 분리** → UI는 `screen_layouts_v2`, 비즈니스 로직은 `dataflow_diagrams` -4. **멀티테넌시 필수** → 모든 테이블에 `company_code` 컬럼, 모든 쿼리에 `company_code` 필터링 - ---- - -## 1. 컴포넌트 전체 카탈로그 (32개) - -### 1.1 입력 컴포넌트 - -#### v2-input - -텍스트, 숫자, 비밀번호, textarea 등 모든 단일 값 입력을 처리하는 통합 입력 컴포넌트. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| inputType | string | 입력 유형 | `text`, `number`, `password`, `slider`, `color`, `button`, `textarea` | -| format | string | 포맷 검증 | `email`, `tel`, `url`, `currency`, `biz_no` | -| required | boolean | 필수 여부 | true/false | -| readonly | boolean | 읽기 전용 | true/false | -| maxLength | number | 최대 길이 | 숫자 | -| min / max | number | 숫자 범위 | 숫자 (inputType=number일 때) | -| step | number | 증감 단위 | 숫자 (inputType=number/slider일 때) | -| tableName | string | 바인딩 테이블 | DB 테이블명 | -| columnName | string | 바인딩 컬럼 | DB 컬럼명 | - -#### v2-select - -드롭다운, 콤보박스, 라디오, 체크박스, 태그 등 선택형 입력 통합 컴포넌트. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| mode | string | 선택 모드 | `dropdown`, `combobox`, `radio`, `check`, `tag`, `tagbox`, `toggle`, `swap` | -| source | string | 데이터 소스 | `static`, `code`, `db`, `api`, `entity`, `category`, `distinct`, `select` | -| searchable | boolean | 검색 가능 | true/false (mode=combobox일 때 기본 true) | -| multiple | boolean | 다중 선택 | true/false | -| cascading | object | 연쇄 선택 | 상위 select 값에 따라 하위 옵션 변경 | - -**source 설명**: -- `static`: 고정 옵션 목록 (options 배열 직접 지정) -- `code`: code_info 테이블의 공통 코드 -- `db`: 특정 테이블의 컬럼 값 -- `entity`: 엔티티 조인 (다른 테이블 참조) -- `category`: v2-category-manager의 카테고리 -- `distinct`: 테이블 컬럼의 DISTINCT 값 - -#### v2-date - -날짜, 시간, 날짜시간, 날짜범위, 월, 연도 입력. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| dateType | string | 날짜 유형 | `date`, `time`, `datetime`, `month`, `year` | -| format | string | 표시 형식 | `YYYY-MM-DD`, `HH:mm`, `YYYY-MM-DD HH:mm` 등 | -| range | boolean | 범위 선택 | true/false (시작~종료 날짜) | -| minDate / maxDate | string | 선택 가능 범위 | ISO 8601 날짜 | -| showToday | boolean | 오늘 버튼 | true/false | - -#### v2-file-upload - -파일/이미지 업로드. 다중 업로드, 미리보기 지원. - -#### v2-media - -이미지, 비디오, 오디오 등 미디어 표시/재생. - -#### v2-location-swap-selector - -출발지/도착지 선택 및 교환 (물류 화면용). - -#### v2-rack-structure - -창고 랙 위치 일괄 생성 (열 범위, 단 수 설정). - -#### v2-process-work-standard - -품목별 공정 작업기준(Pre-Work/In-Work/Post-Work) 관리. 라우팅과 연동. - -#### v2-item-routing - -품목별 라우팅 버전 및 공정 순서 관리 (3단계 계층: 품목 → 라우팅 버전 → 공정). - ---- - -### 1.2 표시/데이터 컴포넌트 - -#### v2-table-list - -가장 핵심 컴포넌트. DB 테이블 데이터를 조회/편집하는 테이블. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| selectedTable | string | **필수**. DB 테이블명 | 예: `"item_info"` | -| columns | array | 표시할 컬럼 설정 | `[{ columnName, displayName, visible, sortable, width, editable }]` | -| pagination | object | 페이지네이션 | `{ enabled: true, pageSize: 20, showSizeSelector: true }` | -| displayMode | string | 표시 모드 | `"table"` (기본) 또는 `"card"` | -| checkbox | object | 체크박스 | `{ enabled: true, multiple: true, position: "left", selectAll: true }` | -| horizontalScroll | object | 가로 스크롤 | `{ enabled: true, maxVisibleColumns: 8 }` | -| linkedFilters | array | 다른 컴포넌트와 연동 필터 | - | -| excludeFilter | object | 제외 필터 | - | -| autoLoad | boolean | 자동 데이터 로드 | true/false | -| stickyHeader | boolean | 헤더 고정 | true/false | -| toolbar | object | 툴바 (신규/삭제/엑셀 등) | - | -| tableStyle | object | 테이블 스타일 | `{ compact: true, striped: true }` | - -**columns 배열 항목**: - -```json -{ - "columnName": "item_code", - "displayName": "품목코드", - "visible": true, - "sortable": true, - "width": 120, - "editable": false, - "inputType": "text", - "format": null, - "align": "left" -} -``` - -#### v2-table-grouped - -v2-table-list 기반 확장. 특정 컬럼 기준으로 데이터를 그룹화하여 접기/펼치기 기능 제공. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| selectedTable | string | **필수**. DB 테이블명 | - | -| groupConfig | object | **필수**. 그룹화 설정 | 아래 참조 | -| columns | array | 컬럼 설정 (v2-table-list와 동일) | - | -| showCheckbox | boolean | 체크박스 표시 | true/false | - -**groupConfig 구조**: - -```json -{ - "groupByColumn": "category", - "groupLabelFormat": "{category_name} ({category_code})", - "summary": { - "sumColumns": ["quantity", "amount"], - "showCount": true - }, - "defaultExpanded": true, - "nestedGroup": null -} -``` - -#### v2-table-search-widget - -테이블 상단에 배치하는 검색/필터 바. 대상 테이블의 컬럼을 자동 감지하여 검색 필드 생성. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| autoSelectFirstTable | boolean | 첫 번째 테이블 자동 선택 | true/false | -| showTableSelector | boolean | 테이블 선택기 표시 | true/false | -| title | string | 검색 바 제목 | - | - -#### v2-pivot-grid - -다차원 피벗 테이블. 행/열/데이터/필터 영역으로 데이터 분석. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| fields | array | **필수**. 필드 정의 | 아래 참조 | -| dataSource | object | 데이터 소스 | `{ type: "table"/"api"/"static", joinConfigs, filterConditions }` | - -**fields 배열 항목**: - -```json -{ - "dataField": "region", - "area": "row", - "caption": "지역" -} -``` - -area 값: `"row"`, `"column"`, `"data"`, `"filter"` -summaryType (area=data일 때): `"sum"`, `"avg"`, `"count"`, `"min"`, `"max"`, `"countDistinct"` -groupInterval (날짜 필드): `"year"`, `"quarter"`, `"month"`, `"week"`, `"day"` - -#### v2-text-display - -라벨, 제목, 설명 텍스트 표시. - -| 설정 | 타입 | 설명 | -|------|------|------| -| fontSize | string | 폰트 크기 | -| fontWeight | string | 폰트 두께 | -| color | string | 텍스트 색상 | -| textAlign | string | 정렬 | - -#### v2-card-display - -테이블 데이터를 카드 형태로 표시. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| dataSource | string | 데이터 소스 | `"table"`, `"static"`, `"api"` | -| cardsPerRow | number | 행당 카드 수 | 1~6 | -| columnMapping | object | 필드 매핑 | `{ title, subtitle, description, image, status }` | -| cardStyle | object | 카드 스타일 | `{ imagePosition, imageSize }` | - -#### v2-aggregation-widget - -합계, 평균, 개수, 최대, 최소 등 집계 카드 표시. - -| 설정 | 타입 | 설명 | -|------|------|------| -| items | array | 집계 항목 배열 (테이블, 컬럼, 집계함수) | -| filters | array | 필터 조건 | -| layout | string | 레이아웃 (horizontal/vertical) | - -#### v2-status-count - -상태별 건수를 카드 형태로 표시. 대시보드/현황 화면에 적합. - -#### v2-numbering-rule - -자동 코드/번호 채번. 접두사 + 날짜 + 순번 조합. - -| 설정 | 타입 | 설명 | -|------|------|------| -| rule | string | 채번 규칙 | -| prefix | string | 접두사 | -| format | string | 포맷 | - -#### v2-category-manager - -트리 기반 카테고리 관리 (3단계 계층). 카테고리 생성/수정/삭제/이동. - ---- - -### 1.3 레이아웃 컴포넌트 - -#### v2-split-panel-layout - -**가장 복잡하고 중요한 컴포넌트**. 마스터-디테일 좌우 분할 레이아웃. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| splitRatio | number | 좌측 비율 (0-100) | 기본 30 | -| resizable | boolean | 리사이즈 가능 | true/false | -| minLeftWidth | number | 좌측 최소 너비 | 기본 200 | -| minRightWidth | number | 우측 최소 너비 | 기본 300 | -| syncSelection | boolean | 선택 동기화 | true/false | -| autoLoad | boolean | 자동 로드 | true/false | - -**leftPanel / rightPanel 구조**: - -```json -{ - "leftPanel": { - "displayMode": "table", - "tableName": "master_table", - "columns": [], - "showSearch": true, - "showAdd": true, - "showEdit": true, - "showDelete": true, - "addButton": { - "enabled": true, - "mode": "auto", - "modalScreenId": "" - }, - "editButton": { - "enabled": true, - "mode": "auto", - "modalScreenId": "" - }, - "deleteButton": { - "enabled": true, - "buttonLabel": "삭제", - "confirmMessage": "삭제하시겠습니까?" - }, - "addModalColumns": [], - "additionalTabs": [], - "dataFilter": {} - }, - "rightPanel": { - "displayMode": "table", - "tableName": "detail_table", - "relation": { - "type": "detail", - "foreignKey": "master_id", - "leftColumn": "id", - "rightColumn": "master_id", - "keys": [] - }, - "addButton": { "enabled": true }, - "editButton": { "enabled": true }, - "deleteButton": { "enabled": true }, - "additionalTabs": [] - } -} -``` - -**displayMode 종류**: -- `"list"`: 리스트 형태 (제목, 부제목) -- `"table"`: 테이블 형태 (컬럼, 행) -- `"custom"`: 자유 배치 (components 배열로 내부 컴포넌트 배치) - -**relation.type 종류**: -- `"join"`: 두 테이블 JOIN -- `"detail"`: 마스터 PK → 디테일 FK 관계 -- `"custom"`: 커스텀 관계 (leftColumn, rightColumn 직접 지정) - -**additionalTabs** (우측 패널에 멀티 탭 배치): - -```json -{ - "additionalTabs": [ - { - "id": "tab1", - "label": "기본정보", - "tableName": "detail_basic", - "relation": { "type": "detail", "foreignKey": "master_id" } - }, - { - "id": "tab2", - "label": "이력", - "tableName": "detail_history", - "relation": { "type": "detail", "foreignKey": "master_id" } - } - ] -} -``` - -#### v2-tabs-widget - -탭 전환 레이아웃. 탭 내부에 컴포넌트를 배치할 수 있음. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| tabs | array | **필수**. 탭 배열 | `[{ id, label, order, disabled, components }]` | -| defaultTab | string | 기본 활성 탭 ID | - | -| orientation | string | 탭 방향 | `"horizontal"`, `"vertical"` | -| persistSelection | boolean | 선택 유지 | true/false | - -**tabs 항목 구조**: - -```json -{ - "id": "tab-basic", - "label": "기본정보", - "order": 0, - "disabled": false, - "components": [ - { - "id": "tbl-basic", - "componentType": "v2-table-list", - "label": "기본정보", - "position": { "x": 0, "y": 0 }, - "size": { "width": 800, "height": 400 }, - "componentConfig": { "selectedTable": "basic_info" } - } - ] -} -``` - -#### v2-section-card - -제목+테두리가 있는 그룹화 컨테이너. 폼 필드를 묶을 때 사용. - -| 설정 | 타입 | 설명 | -|------|------|------| -| title | string | 섹션 제목 | -| collapsible | boolean | 접기/펼치기 가능 | -| padding | string | 내부 여백 | - -#### v2-section-paper - -배경색 기반 미니멀 그룹화 컨테이너. - -#### v2-divider-line - -영역 구분선 (가로/세로). - -#### v2-split-line - -캔버스 화면에서 좌우 영역을 분할하는 드래그 가능한 세로선. 디자이너 모드에서 사용. - -#### v2-repeat-container - -데이터 수만큼 내부 컴포넌트를 반복 렌더링. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| dataSourceType | string | 데이터 소스 | `"table"`, `"api"`, `"static"` | -| layout | string | 레이아웃 | `"vertical"`, `"horizontal"`, `"grid"` | -| gridColumns | number | 그리드 열 수 | 1~12 | - -#### v2-repeater - -인라인/모달/버튼 모드로 반복 데이터를 관리하는 컴포넌트. 주문 상세, 항목 리스트 등. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| mode | string | 동작 모드 | `"inline"` (인라인 편집), `"modal"` (모달 팝업), `"button"` (버튼 트리거) | - ---- - -### 1.4 특수/비즈니스 컴포넌트 - -#### v2-button-primary - -저장, 삭제, 조회 등 액션 버튼. 제어관리(dataflow)와 연결 가능. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| text | string | 버튼 텍스트 | 예: `"저장"`, `"삭제"` | -| actionType | string | 액션 유형 | `"save"`, `"delete"`, `"search"`, `"custom"` | -| variant | string | 스타일 | `"default"`, `"outline"`, `"destructive"`, `"ghost"` | - -**제어관리 연결** (webTypeConfig.dataflowConfig): - -```json -{ - "webTypeConfig": { - "enableDataflowControl": true, - "dataflowConfig": { - "controlMode": "relationship", - "relationshipConfig": { - "relationshipId": "rel_001", - "relationshipName": "수주확정", - "executionTiming": "after" - } - } - } -} -``` - -executionTiming 값: -- `"before"`: 메인 액션 전에 제어 실행 (조건 체크용) -- `"after"`: 메인 액션 후에 제어 실행 (후처리용) -- `"replace"`: 메인 액션 대신 제어만 실행 - -#### v2-timeline-scheduler - -간트차트형 일정/계획 시각화. 드래그/리사이즈로 일정 변경 가능. - -| 설정 | 타입 | 설명 | 값 | -|------|------|------|----| -| selectedTable | string | 스케줄 테이블명 | - | -| resourceTable | string | 리소스 테이블명 (설비/작업자) | - | -| fieldMapping | object | 필드 매핑 | `{ id, resourceId, title, startDate, endDate, status, progress }` | -| resourceFieldMapping | object | 리소스 필드 매핑 | `{ id, name, group }` | -| defaultZoomLevel | string | 초기 줌 | `"day"`, `"week"`, `"month"` | -| editable | boolean | 편집 가능 | true/false | -| allowDrag | boolean | 드래그 이동 허용 | true/false | -| allowResize | boolean | 리사이즈 허용 | true/false | - -#### v2-approval-step - -결재 단계 스테퍼 시각화. 결재 프로세스의 진행 상태를 단계별로 표시. - -#### v2-bom-tree - -BOM(Bill of Materials) 계층 트리 표시. 정전개(상위→하위)/역전개(하위→상위) 전환 가능. - -#### v2-bom-item-editor - -BOM 하위품목 트리 편집. 하위 부품 추가/수정/삭제/수량 변경 등. - ---- - -## 2. 화면 패턴별 상세 구현 가이드 - -### 2.1 패턴 A: 기본 마스터 (검색 + 테이블) - -**적용 조건**: 단일 테이블 CRUD. 마스터-디테일 관계 없음. - -**layout_data 예시**: - -```json -{ - "version": "2.0", - "components": [ - { - "id": "search_widget", - "url": "@/lib/registry/components/v2-table-search-widget", - "position": { "x": 0, "y": 0, "z": 1 }, - "size": { "width": 1920, "height": 100 }, - "displayOrder": 0, - "overrides": { - "label": "검색", - "autoSelectFirstTable": true - } - }, - { - "id": "table_list", - "url": "@/lib/registry/components/v2-table-list", - "position": { "x": 0, "y": 120, "z": 1 }, - "size": { "width": 1920, "height": 700 }, - "displayOrder": 1, - "overrides": { - "label": "품목 목록", - "tableName": "item_info", - "columns": [ - { "columnName": "item_code", "displayName": "품목코드", "visible": true, "sortable": true, "width": 120 }, - { "columnName": "item_name", "displayName": "품목명", "visible": true, "sortable": true, "width": 200 }, - { "columnName": "item_type", "displayName": "품목유형", "visible": true, "width": 100 }, - { "columnName": "unit", "displayName": "단위", "visible": true, "width": 80 } - ], - "pagination": { "enabled": true, "pageSize": 20 }, - "checkbox": { "enabled": true, "multiple": true } - } - } - ], - "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, - "screenResolution": { "width": 1920, "height": 1080 } -} -``` - -### 2.2 패턴 B: 마스터-디테일 (좌우 분할) - -**적용 조건**: 마스터 테이블 선택 시 연관 디테일 테이블 표시. 두 테이블 간 FK 관계 존재. - -**layout_data 예시**: - -```json -{ - "version": "2.0", - "components": [ - { - "id": "split_panel", - "url": "@/lib/registry/components/v2-split-panel-layout", - "position": { "x": 0, "y": 0, "z": 1 }, - "size": { "width": 1920, "height": 800 }, - "displayOrder": 0, - "overrides": { - "label": "수주관리", - "splitRatio": 35, - "resizable": true, - "leftPanel": { - "displayMode": "table", - "tableName": "order_master", - "columns": [ - { "columnName": "order_no", "displayName": "수주번호", "visible": true, "width": 120 }, - { "columnName": "customer_name", "displayName": "거래처", "visible": true, "width": 150 }, - { "columnName": "order_date", "displayName": "수주일자", "visible": true, "width": 100 }, - { "columnName": "status", "displayName": "상태", "visible": true, "width": 80 } - ], - "showSearch": true, - "showAdd": true, - "showDelete": true, - "addButton": { "enabled": true, "mode": "auto" }, - "deleteButton": { "enabled": true, "confirmMessage": "삭제하시겠습니까?" } - }, - "rightPanel": { - "displayMode": "table", - "tableName": "order_detail", - "relation": { - "type": "detail", - "foreignKey": "order_master_id", - "leftColumn": "id", - "rightColumn": "order_master_id" - }, - "columns": [ - { "columnName": "item_code", "displayName": "품목코드", "visible": true, "width": 120 }, - { "columnName": "item_name", "displayName": "품목명", "visible": true, "width": 150 }, - { "columnName": "quantity", "displayName": "수량", "visible": true, "width": 80, "editable": true }, - { "columnName": "unit_price", "displayName": "단가", "visible": true, "width": 100, "editable": true } - ], - "addButton": { "enabled": true }, - "deleteButton": { "enabled": true } - } - } - } - ], - "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, - "screenResolution": { "width": 1920, "height": 1080 } -} -``` - -### 2.3 패턴 C: 마스터-디테일 + 탭 - -**적용 조건**: 마스터-디테일에서 우측에 여러 종류의 상세 정보를 탭으로 분리. - -**차이점**: rightPanel에 `additionalTabs` 배열 추가. - -```json -{ - "rightPanel": { - "displayMode": "table", - "tableName": "customer_basic", - "relation": { - "type": "detail", - "foreignKey": "customer_id" - }, - "additionalTabs": [ - { - "id": "tab-basic", - "label": "기본정보", - "tableName": "customer_basic", - "relation": { "type": "detail", "foreignKey": "customer_id" } - }, - { - "id": "tab-history", - "label": "거래이력", - "tableName": "customer_history", - "relation": { "type": "detail", "foreignKey": "customer_id" } - }, - { - "id": "tab-files", - "label": "첨부파일", - "tableName": "customer_files", - "relation": { "type": "detail", "foreignKey": "customer_id" } - } - ] - } -} -``` - ---- - -## 3. 제어관리 (비즈니스 로직) 설정 - -### 3.1 개요 - -V2 컴포넌트는 **UI만 담당**한다. 버튼 클릭 시 실행되는 비즈니스 로직(INSERT/UPDATE/DELETE, 조건 체크, 외부 호출 등)은 `dataflow_diagrams` 테이블에서 별도 설정한다. - -### 3.2 설정 위치 - -| 구분 | 저장 위치 | 용도 | -|------|----------|------| -| 버튼-제어 연결 | `screen_layouts_v2` → 컴포넌트 `webTypeConfig.dataflowConfig` | 어떤 버튼이 어떤 제어를 실행하는지 | -| 제어 정의 | `dataflow_diagrams` → `control`, `plan` JSONB | 조건, 액션, 필드 매핑 | -| 노드 플로우 | `node_flows` → `flow_data` JSONB | 복잡한 다단계 플로우 | - -### 3.3 실행 흐름 - -``` -[버튼 클릭] v2-button-primary - │ - ▼ -[설정 확인] webTypeConfig.dataflowConfig - │ - ├─ controlMode: "relationship" → dataflow_diagrams 실행 - ├─ controlMode: "flow" → node_flows 실행 - └─ controlMode: "none" → 기본 액션만 실행 - │ - ▼ -[실행기] ImprovedButtonActionExecutor - │ - ├─ 1. Before 제어 (executionTiming = "before") - │ └─ 조건 체크 → 실패 시 중단 - │ - ├─ 2. Main 액션 (executionTiming ≠ "replace"일 때) - │ └─ save / delete / update 실행 - │ - └─ 3. After 제어 (executionTiming = "after") - └─ 후처리 (이력 저장, 상태 변경, 외부 호출 등) -``` - -### 3.4 dataflow_diagrams 설정 예시 - -**시나리오**: 수주 목록에서 [확정] 버튼 클릭 → 상태를 '확정'으로 변경 + 이력 테이블에 기록 - -```json -{ - "control": [ - { - "conditions": [ - { "field": "status", "operator": "=", "value": "대기", "dataType": "string" } - ], - "triggerType": "update" - } - ], - "plan": [ - { - "actions": [ - { - "actionType": "update", - "targetTable": "order_master", - "conditions": [ - { "field": "status", "operator": "=", "value": "대기" } - ], - "fieldMappings": [ - { "targetField": "status", "defaultValue": "확정" } - ] - }, - { - "actionType": "insert", - "targetTable": "order_history", - "fieldMappings": [ - { "sourceField": "order_no", "targetField": "order_no" }, - { "sourceField": "customer_name", "targetField": "customer_name" }, - { "targetField": "action", "defaultValue": "확정" } - ] - } - ] - } - ] -} -``` - ---- - -## 4. 화면 개발 체크리스트 (AI 에이전트용) - -화면 개발 요청을 받았을 때 이 순서대로 확인한다. - -### Step 1: 요구사항 분석 - -``` -□ 어떤 테이블을 사용하는가? (메인 테이블, 디테일 테이블) -□ 테이블 간 관계는? (FK, 1:N, M:N) -□ 어떤 패턴인가? (A~G 중 선택, 의사결정 트리 참조) -□ 어떤 버튼이 필요한가? (저장, 삭제, 확정 등) -□ 비즈니스 로직이 있는가? (상태 변경, 이력 기록, 외부 호출 등) -``` - -### Step 2: DB 테이블 생성 - -``` -□ 모든 컬럼 VARCHAR(500) -□ 기본 5개 컬럼 포함 (id, created_date, updated_date, writer, company_code) -□ table_labels, table_type_columns, column_labels 메타데이터 등록 -□ company_code 인덱스 생성 -``` - -### Step 3: screen_definitions INSERT - -```sql -INSERT INTO screen_definitions (screen_name, screen_code, table_name, company_code, is_active) -VALUES ('화면명', '{company_code}_순번', '메인_테이블', '{company_code}', 'Y'); -``` - -### Step 4: screen_layouts_v2 INSERT - -```sql -INSERT INTO screen_layouts_v2 (screen_id, company_code, layer_id, layout_data) -VALUES ({screen_id}, '{company_code}', 1, '{패턴별 JSON}'); -``` - -### Step 5: dataflow_diagrams INSERT (비즈니스 로직 필요 시) - -``` -□ 버튼별 액션 정의 -□ 조건 설정 (어떤 상태일 때만 실행) -□ 필드 매핑 (소스→타겟) -□ 실행 타이밍 (before/after/replace) -``` - -### Step 6: menu_info INSERT - -```sql -INSERT INTO menu_info (screen_id, menu_url, company_code, ...) -VALUES ({screen_id}, '/screen/{screen_code}', '{company_code}', ...); -``` - -### Step 7: 멀티테넌시 검증 - -``` -□ 테이블에 company_code 컬럼 존재 -□ 모든 SELECT에 company_code 필터링 -□ INSERT에 company_code 포함 -□ UPDATE/DELETE WHERE절에 company_code 포함 -□ JOIN에 company_code 매칭 -``` - ---- - -## 5. 자주 묻는 질문 (FAQ) - -### Q: v2-table-list와 v2-table-grouped 중 어떤 걸 써야 하나? - -**A**: 데이터를 특정 컬럼 기준으로 묶어서 접기/펼치기가 필요하면 `v2-table-grouped`, 아니면 `v2-table-list`. 예를 들어 "카테고리별 품목 목록"은 `v2-table-grouped`, "품목 전체 목록"은 `v2-table-list`. - -### Q: 마스터-디테일에서 탭을 사용하고 싶은데? - -**A**: `v2-split-panel-layout`의 rightPanel에 `additionalTabs` 배열을 설정한다. 별도의 `v2-tabs-widget`을 배치할 필요 없다. - -### Q: BOM 화면은 어떻게 만드나? - -**A**: `v2-split-panel-layout`의 좌측에 `v2-bom-tree` (또는 `v2-table-list`), 우측에 `v2-bom-item-editor`를 배치한다. 트리 컴포넌트가 자체적으로 정전개/역전개를 지원한다. - -### Q: 버튼 클릭 시 특정 로직을 실행하고 싶은데? - -**A**: `v2-button-primary`의 `webTypeConfig.dataflowConfig`에 제어관리를 연결한다. `dataflow_diagrams` 테이블에 실행할 액션(INSERT/UPDATE/DELETE)과 조건을 정의한다. - -### Q: 검색 기능은 어떻게 추가하나? - -**A**: `v2-table-search-widget`을 `v2-table-list` 위에 배치한다. `autoSelectFirstTable: true`로 설정하면 자동으로 화면의 첫 번째 테이블과 연동된다. - -### Q: 파일 업로드는? - -**A**: `v2-file-upload` 컴포넌트를 사용한다. 폼 내 배치하여 파일/이미지 업로드를 처리한다. - -### Q: 결재 프로세스를 표시하고 싶은데? - -**A**: `v2-approval-step` 컴포넌트를 사용한다. 결재 단계를 스테퍼 형태로 시각화한다. - -### Q: 인라인 편집이 가능한가? - -**A**: `v2-table-list`의 columns에서 `editable: true`로 설정하면 해당 컬럼의 셀을 직접 편집할 수 있다. 단, 복잡한 편집은 모달 편집(`v2-split-panel-layout`의 editButton.mode = "modal")을 권장한다. - -### Q: 화면에 집계(합계, 평균 등)를 표시하고 싶은데? - -**A**: `v2-aggregation-widget`을 사용하여 테이블 데이터의 합계, 평균, 개수 등을 카드 형태로 표시한다. 또는 `v2-status-count`로 상태별 건수를 표시한다. - ---- - -## 6. 컴포넌트 선택 빠른 참조표 - -| 요구사항 | 컴포넌트 | -|----------|----------| -| 데이터 목록 보기 | `v2-table-list` | -| 데이터 검색/필터 | `v2-table-search-widget` | -| 좌우 분할 (마스터-디테일) | `v2-split-panel-layout` | -| 그룹별 묶기 (접기/펼치기) | `v2-table-grouped` | -| 탭 전환 | `v2-tabs-widget` 또는 `v2-split-panel-layout`의 additionalTabs | -| 카드 형태 보기 | `v2-card-display` | -| 피벗/집계 분석 | `v2-pivot-grid` | -| 간트차트/일정 | `v2-timeline-scheduler` | -| BOM 트리 | `v2-bom-tree` + `v2-bom-item-editor` | -| 텍스트 입력 | `v2-input` | -| 선택 (드롭다운 등) | `v2-select` | -| 날짜 입력 | `v2-date` | -| 파일 업로드 | `v2-file-upload` | -| 액션 버튼 | `v2-button-primary` | -| 자동 채번 | `v2-numbering-rule` | -| 카테고리 관리 | `v2-category-manager` | -| 결재 단계 표시 | `v2-approval-step` | -| 상태별 건수 | `v2-status-count` | -| 합계/평균 카드 | `v2-aggregation-widget` | -| 구분선 | `v2-divider-line` | -| 그룹 컨테이너 | `v2-section-card` 또는 `v2-section-paper` | -| 데이터 반복 | `v2-repeat-container` 또는 `v2-repeater` | -| 공정 작업기준 | `v2-process-work-standard` | -| 품목 라우팅 | `v2-item-routing` | -| 미디어 표시 | `v2-media` | -| 랙 구조 | `v2-rack-structure` | -| 위치 교환 | `v2-location-swap-selector` | diff --git a/docs/screen-implementation-guide/01_reference_guides/full-screen-analysis.md b/docs/screen-implementation-guide/01_reference_guides/full-screen-analysis.md new file mode 100644 index 00000000..1ba0da01 --- /dev/null +++ b/docs/screen-implementation-guide/01_reference_guides/full-screen-analysis.md @@ -0,0 +1,952 @@ +# WACE 화면 시스템 - DB 스키마 & 컴포넌트 설정 전체 레퍼런스 + +> **최종 업데이트**: 2026-03-13 +> **용도**: AI 챗봇이 화면 생성 시 참조하는 DB 스키마, 컴포넌트 전체 설정 사전 +> **관련 문서**: `v2-component-usage-guide.md` (SQL 템플릿, 실행 예시) + +--- + +## 1. 시스템 아키텍처 + +### 렌더링 파이프라인 + +``` +[DB] screen_definitions + screen_layouts_v2 + → [Backend API] GET /api/screens/:screenId + → [layoutV2Converter] V2 JSON → Legacy 변환 (기본값 + overrides 병합) + → [ResponsiveGridRenderer] → DynamicComponentRenderer + → [ComponentRegistry] → 실제 React 컴포넌트 +``` + +### 테이블 관계도 + +``` +비즈니스 테이블 ←── table_labels (라벨) + ←── table_type_columns (컬럼 타입, company_code='*') + ←── column_labels (한글 라벨) + +screen_definitions ←── screen_layouts_v2 (layout_data JSON) +menu_info (메뉴 트리, menu_url → /screen/{screen_code}) + +[선택] dataflow_diagrams (비즈니스 로직) +[선택] numbering_rules + numbering_rule_parts (채번) +[선택] table_column_category_values (카테고리) +``` + +--- + +## 2. DB 테이블 스키마 + +### 2.1 비즈니스 테이블 필수 구조 + +> **[최우선 규칙] 비즈니스 테이블에 NOT NULL / UNIQUE 제약조건 절대 금지!** +> +> 멀티테넌시 환경에서 회사별로 필수값/유니크 규칙이 다를 수 있으므로, +> 제약조건은 DB 레벨이 아닌 **`table_type_columns`의 메타데이터(`is_nullable`, `is_unique`)로 논리적 제어**한다. +> DB에 직접 NOT NULL/UNIQUE/CHECK/FOREIGN KEY를 걸면 멀티테넌시가 깨진다. +> +> **허용**: `id` PRIMARY KEY, `DEFAULT` 값만 DB 레벨 설정 +> **금지**: 비즈니스 컬럼에 `NOT NULL`, `UNIQUE`, `CHECK`, `FOREIGN KEY` + +```sql +CREATE TABLE "{테이블명}" ( + "id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text, + "created_date" timestamp DEFAULT now(), + "updated_date" timestamp DEFAULT now(), + "writer" varchar(500) DEFAULT NULL, + "company_code" varchar(500), + -- 모든 비즈니스 컬럼은 varchar(500), NOT NULL/UNIQUE 제약조건 금지 +); +``` + +### 2.2 table_labels + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| table_name | varchar PK | 테이블명 | +| table_label | varchar | 한글 라벨 | +| description | text | 설명 | +| use_log_table | varchar(1) | 'Y'/'N' | + +### 2.3 table_type_columns + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| id | serial PK | 자동 증가 | +| table_name | varchar | UNIQUE(+column_name+company_code) | +| column_name | varchar | 컬럼명 | +| company_code | varchar | `'*'` = 전체 공통 | +| input_type | varchar | text/number/date/code/entity/select/checkbox/radio/textarea/category/numbering | +| detail_settings | text | JSON (code/entity/select 상세) | +| is_nullable | varchar | `'Y'`/`'N'` (논리적 필수값 제어) | +| display_order | integer | -5~-1: 기본, 0~: 비즈니스 | +| column_label | varchar | 컬럼 한글 라벨 | +| description | text | 컬럼 설명 | +| is_visible | boolean | 화면 표시 여부 (기본 true) | +| code_category | varchar | input_type=code일 때 코드 카테고리 | +| code_value | varchar | 코드 값 | +| reference_table | varchar | input_type=entity일 때 참조 테이블 | +| reference_column | varchar | 참조 컬럼 | +| display_column | varchar | 참조 표시 컬럼 | +| is_unique | varchar | `'Y'`/`'N'` (논리적 유니크 제어) | +| category_ref | varchar | 카테고리 참조 | + +### 2.4 screen_definitions + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| screen_id | serial PK | 자동 증가 | +| screen_name | varchar NOT NULL | 화면명 | +| screen_code | varchar | **조건부 UNIQUE** (`WHERE is_active <> 'D'`) | +| table_name | varchar | 메인 테이블명 | +| company_code | varchar NOT NULL | 회사 코드 | +| description | text | 화면 설명 | +| is_active | char(1) | `'Y'`/`'N'`/`'D'` (D=삭제) | +| layout_metadata | jsonb | 레이아웃 메타데이터 | +| created_date | timestamp | 생성일시 | +| created_by | varchar | 생성자 | +| updated_date | timestamp | 수정일시 | +| updated_by | varchar | 수정자 | +| deleted_date | timestamp | 삭제일시 | +| deleted_by | varchar | 삭제자 | +| delete_reason | text | 삭제 사유 | +| db_source_type | varchar | `'internal'` (기본) / `'external'` | +| db_connection_id | integer | 외부 DB 연결 ID | +| data_source_type | varchar | `'database'` (기본) / `'rest_api'` | +| rest_api_connection_id | integer | REST API 연결 ID | +| rest_api_endpoint | varchar | REST API 엔드포인트 | +| rest_api_json_path | varchar | JSON 응답 경로 (기본 `'data'`) | +| source_screen_id | integer | 원본 화면 ID (복사본일 때) | + +> **screen_code UNIQUE 주의**: `is_active = 'D'`(삭제)인 화면은 UNIQUE 대상에서 제외된다. 삭제된 화면과 같은 코드로 새 화면을 만들 수 있지만, 활성 상태(`'Y'`/`'N'`)에서는 중복 불가. + +### 2.5 screen_layouts_v2 + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| layout_id | serial PK | 자동 증가 | +| screen_id | integer FK | UNIQUE(+company_code+layer_id) | +| company_code | varchar NOT NULL | 회사 코드 | +| layout_data | jsonb NOT NULL | 전체 레이아웃 JSON (기본 `'{}'`) | +| created_at | timestamptz | 생성일시 | +| updated_at | timestamptz | 수정일시 | +| layer_id | integer | 1=기본 레이어 (기본값 1) | +| layer_name | varchar | 레이어명 (기본 `'기본 레이어'`) | +| condition_config | jsonb | 레이어 조건부 표시 설정 | + +### 2.6 menu_info + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| objid | numeric PK | BIGINT 고유값 | +| menu_type | numeric | 0=화면, 1=폴더 | +| parent_obj_id | numeric | 부모 메뉴 objid | +| menu_name_kor | varchar | 메뉴명 (한글) | +| menu_name_eng | varchar | 메뉴명 (영문) | +| seq | numeric | 정렬 순서 | +| menu_url | varchar | `/screen/{screen_code}` | +| menu_desc | varchar | 메뉴 설명 | +| writer | varchar | 작성자 | +| regdate | timestamp | 등록일시 | +| status | varchar | 상태 (`'active'` 등) | +| company_code | varchar | 회사 코드 (기본 `'*'`) | +| screen_code | varchar | 연결 화면 코드 | +| system_name | varchar | 시스템명 | +| lang_key | varchar | 다국어 키 | +| lang_key_desc | varchar | 다국어 설명 키 | +| menu_code | varchar | 메뉴 코드 | +| source_menu_objid | bigint | 원본 메뉴 objid (복사본일 때) | +| screen_group_id | integer | 화면 그룹 ID | +| menu_icon | varchar | 메뉴 아이콘 | + +--- + +## 3. 컴포넌트 전체 설정 레퍼런스 (32개) + +> 아래 설정은 layout_data JSON의 각 컴포넌트 `overrides` 안에 들어가는 값이다. +> 기본값과 다른 부분만 overrides에 지정하면 된다. + +--- + +### 3.1 v2-table-list (데이터 테이블) + +**용도**: DB 테이블 데이터를 테이블/카드 형태로 조회/편집. 가장 핵심적인 컴포넌트. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| tableName | string | - | 조회할 DB 테이블명 | +| selectedTable | string | - | tableName 별칭 | +| displayMode | `"table"\|"card"` | `"table"` | 테이블 모드 또는 카드 모드 | +| autoLoad | boolean | `true` | 화면 로드 시 자동으로 데이터 조회 | +| isReadOnly | boolean | false | 읽기 전용 (편집 불가) | +| columns | ColumnConfig[] | `[]` | 표시할 컬럼 설정 배열 | +| title | string | - | 테이블 상단 제목 | +| showHeader | boolean | `true` | 테이블 헤더 행 표시 | +| showFooter | boolean | `true` | 테이블 푸터 표시 | +| height | string | `"auto"` | 높이 모드 (`"auto"`, `"fixed"`, `"viewport"`) | +| fixedHeight | number | - | height="fixed"일 때 고정 높이(px) | +| autoWidth | boolean | `true` | 컬럼 너비 자동 계산 | +| stickyHeader | boolean | `false` | 스크롤 시 헤더 고정 | + +**checkbox (체크박스 설정)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| enabled | boolean | `true` | 체크박스 사용 여부 | +| multiple | boolean | `true` | 다중 선택 허용 | +| position | `"left"\|"right"` | `"left"` | 체크박스 위치 | +| selectAll | boolean | `true` | 전체 선택 버튼 표시 | + +**pagination (페이지네이션)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| enabled | boolean | `true` | 페이지네이션 사용 | +| pageSize | number | `20` | 한 페이지당 행 수 | +| showSizeSelector | boolean | `true` | 페이지 크기 변경 드롭다운 | +| showPageInfo | boolean | `true` | "1-20 / 100건" 같은 정보 표시 | +| pageSizeOptions | number[] | `[10,20,50,100]` | 선택 가능한 페이지 크기 | + +**horizontalScroll (가로 스크롤)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| enabled | boolean | `true` | 가로 스크롤 사용 | +| maxVisibleColumns | number | `8` | 스크롤 없이 보이는 최대 컬럼 수 | +| minColumnWidth | number | `100` | 컬럼 최소 너비(px) | +| maxColumnWidth | number | `300` | 컬럼 최대 너비(px) | + +**tableStyle (스타일)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| theme | string | `"default"` | 테마 (`default`/`striped`/`bordered`/`minimal`) | +| headerStyle | string | `"default"` | 헤더 스타일 (`default`/`dark`/`light`) | +| rowHeight | string | `"normal"` | 행 높이 (`compact`/`normal`/`comfortable`) | +| alternateRows | boolean | `true` | 짝수/홀수 행 색상 교차 | +| hoverEffect | boolean | `true` | 마우스 호버 시 행 강조 | +| borderStyle | string | `"light"` | 테두리 (`none`/`light`/`heavy`) | + +**toolbar (툴바 버튼)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| showEditMode | boolean | `false` | 즉시저장/배치저장 모드 전환 버튼 | +| showExcel | boolean | `false` | Excel 내보내기 버튼 | +| showPdf | boolean | `false` | PDF 내보내기 버튼 | +| showSearch | boolean | `false` | 테이블 내 검색 | +| showRefresh | boolean | `false` | 상단 새로고침 버튼 | +| showPaginationRefresh | boolean | `true` | 하단 새로고침 버튼 | + +**filter (필터)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| enabled | boolean | `true` | 필터 기능 사용 | +| filters | array | `[]` | 사전 정의 필터 목록 | + +**ColumnConfig (columns 배열 요소)**: + +| 설정 | 타입 | 설명 | +|------|------|------| +| columnName | string | DB 컬럼명 | +| displayName | string | 화면 표시명 | +| visible | boolean | 표시 여부 | +| sortable | boolean | 정렬 가능 여부 | +| searchable | boolean | 검색 가능 여부 | +| editable | boolean | 인라인 편집 가능 여부 | +| width | number | 컬럼 너비(px) | +| align | `"left"\|"center"\|"right"` | 텍스트 정렬 | +| format | string | 포맷 (`text`/`number`/`date`/`currency`/`boolean`) | +| hidden | boolean | 숨김 (데이터는 로드하되 표시 안 함) | +| fixed | `"left"\|"right"\|false` | 컬럼 고정 위치 | +| thousandSeparator | boolean | 숫자 천 단위 콤마 | +| isEntityJoin | boolean | 엔티티 조인 사용 여부 | +| entityJoinInfo | object | 조인 정보 (`sourceTable`, `sourceColumn`, `referenceTable`, `joinAlias`) | + +**cardConfig (displayMode="card"일 때)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| idColumn | string | `"id"` | ID 컬럼 | +| titleColumn | string | `"name"` | 카드 제목 컬럼 | +| subtitleColumn | string | - | 부제목 컬럼 | +| descriptionColumn | string | - | 설명 컬럼 | +| imageColumn | string | - | 이미지 URL 컬럼 | +| cardsPerRow | number | `3` | 행당 카드 수 | +| cardSpacing | number | `16` | 카드 간격(px) | +| showActions | boolean | `true` | 카드 액션 버튼 표시 | + +--- + +### 3.2 v2-split-panel-layout (마스터-디테일 분할) + +**용도**: 좌측 마스터 테이블 선택 → 우측 디테일 테이블 연동. 가장 복잡한 컴포넌트. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| splitRatio | number | `30` | 좌측 패널 비율(0~100) | +| resizable | boolean | `true` | 사용자가 분할선 드래그로 비율 변경 가능 | +| minLeftWidth | number | `200` | 좌측 최소 너비(px) | +| minRightWidth | number | `300` | 우측 최소 너비(px) | +| autoLoad | boolean | `true` | 화면 로드 시 자동 데이터 조회 | +| syncSelection | boolean | `true` | 좌측 선택 시 우측 자동 갱신 | + +**leftPanel / rightPanel 공통 설정**: + +| 설정 | 타입 | 설명 | +|------|------|------| +| title | string | 패널 제목 | +| tableName | string | DB 테이블명 | +| displayMode | `"list"\|"table"\|"custom"` | `list`: 리스트, `table`: 테이블, `custom`: 자유 배치 | +| columns | array | 컬럼 설정 (`name`, `label`, `width`, `sortable`, `align`, `isEntityJoin`, `joinInfo`) | +| showSearch | boolean | 패널 내 검색 바 표시 | +| showAdd | boolean | 추가 버튼 표시 | +| showEdit | boolean | 수정 버튼 표시 | +| showDelete | boolean | 삭제 버튼 표시 | +| addButton | object | `{ enabled, mode("auto"/"modal"), modalScreenId }` | +| editButton | object | `{ enabled, mode("auto"/"modal"), modalScreenId, buttonLabel }` | +| deleteButton | object | `{ enabled, buttonLabel, confirmMessage }` | +| addModalColumns | array | 추가 모달 전용 컬럼 (`name`, `label`, `required`) | +| dataFilter | object | `{ enabled, filters, matchType("all"/"any") }` | +| tableConfig | object | `{ showCheckbox, showRowNumber, rowHeight, headerHeight, striped, bordered, hoverable, stickyHeader }` | +| components | array | displayMode="custom"일 때 내부 컴포넌트 배열 | + +**rightPanel 전용 설정**: + +| 설정 | 타입 | 설명 | +|------|------|------| +| relation | object | 마스터-디테일 연결 관계 | +| relation.type | `"detail"\|"join"` | detail: FK 관계, join: 테이블 JOIN | +| relation.leftColumn | string | 좌측(마스터) 연결 컬럼 (보통 `"id"`) | +| relation.rightColumn | string | 우측(디테일) 연결 컬럼 (FK) | +| relation.foreignKey | string | FK 컬럼명 (rightColumn과 동일) | +| relation.keys | array | 복합키 `[{ leftColumn, rightColumn }]` | +| additionalTabs | array | 우측 패널에 탭 추가 (각 탭은 rightPanel과 동일 구조 + `tabId`, `label`) | +| addConfig | object | `{ targetTable, autoFillColumns, leftPanelColumn, targetColumn }` | +| deduplication | object | `{ enabled, groupByColumn, keepStrategy, sortColumn }` | +| summaryColumnCount | number | 요약 표시 컬럼 수 | + +--- + +### 3.3 v2-table-search-widget (검색 바) + +**용도**: 테이블 상단에 배치하여 검색/필터 기능 제공. 대상 테이블 컬럼을 자동 감지. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| autoSelectFirstTable | boolean | `true` | 화면 내 첫 번째 테이블 자동 연결 | +| showTableSelector | boolean | `true` | 테이블 선택 드롭다운 표시 | +| title | string | `"테이블 검색"` | 검색 바 제목 | +| filterMode | `"dynamic"\|"preset"` | `"dynamic"` | dynamic: 자동 필터, preset: 고정 필터 | +| presetFilters | array | `[]` | 고정 필터 목록 (`{ columnName, columnLabel, filterType, width }`) | +| targetPanelPosition | `"left"\|"right"\|"auto"` | `"left"` | split-panel에서 대상 패널 위치 | + +--- + +### 3.4 v2-input (텍스트/숫자 입력) + +**용도**: 텍스트, 숫자, 비밀번호, textarea, 슬라이더, 컬러, 버튼 등 단일 값 입력. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| inputType | string | `"text"` | 입력 유형: `text`/`number`/`password`/`slider`/`color`/`button`/`textarea` | +| format | string | `"none"` | 포맷 검증: `none`/`email`/`tel`/`url`/`currency`/`biz_no` | +| placeholder | string | `""` | 입력 힌트 텍스트 | +| required | boolean | `false` | 필수 입력 표시 | +| readonly | boolean | `false` | 읽기 전용 | +| disabled | boolean | `false` | 비활성화 | +| maxLength | number | - | 최대 입력 글자 수 | +| minLength | number | - | 최소 입력 글자 수 | +| pattern | string | - | 정규식 패턴 검증 | +| showCounter | boolean | `false` | 글자 수 카운터 표시 | +| min | number | - | 최소값 (number/slider) | +| max | number | - | 최대값 (number/slider) | +| step | number | - | 증감 단위 (number/slider) | +| buttonText | string | - | 버튼 텍스트 (inputType=button) | +| tableName | string | - | 바인딩 테이블명 | +| columnName | string | - | 바인딩 컬럼명 | + +--- + +### 3.5 v2-select (선택) + +**용도**: 드롭다운, 콤보박스, 라디오, 체크박스, 태그, 토글 등 선택형 입력. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| mode | string | `"dropdown"` | 선택 모드: `dropdown`/`combobox`/`radio`/`check`/`tag`/`tagbox`/`toggle`/`swap` | +| source | string | `"distinct"` | 데이터 소스: `static`/`code`/`db`/`api`/`entity`/`category`/`distinct`/`select` | +| options | array | `[]` | source=static일 때 옵션 목록 `[{ label, value }]` | +| codeGroup | string | - | source=code일 때 코드 그룹 | +| codeCategory | string | - | source=code일 때 코드 카테고리 | +| table | string | - | source=db일 때 테이블명 | +| valueColumn | string | - | source=db일 때 값 컬럼 | +| labelColumn | string | - | source=db일 때 표시 컬럼 | +| entityTable | string | - | source=entity일 때 엔티티 테이블 | +| entityValueField | string | - | source=entity일 때 값 필드 | +| entityLabelField | string | - | source=entity일 때 표시 필드 | +| searchable | boolean | `true` | 검색 가능 (combobox에서 기본 활성) | +| multiple | boolean | `false` | 다중 선택 허용 | +| maxSelect | number | - | 최대 선택 수 | +| allowClear | boolean | - | 선택 해제 허용 | +| placeholder | string | `"선택하세요"` | 힌트 텍스트 | +| required | boolean | `false` | 필수 선택 | +| readonly | boolean | `false` | 읽기 전용 | +| disabled | boolean | `false` | 비활성화 | +| cascading | object | - | 연쇄 선택 (상위 select 값에 따라 하위 옵션 변경) | +| hierarchical | boolean | - | 계층 구조 (부모-자식 관계) | +| parentField | string | - | 부모 필드명 | + +--- + +### 3.6 v2-date (날짜) + +**용도**: 날짜, 시간, 날짜시간, 날짜범위, 월, 연도 입력. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dateType | string | `"date"` | 날짜 유형: `date`/`datetime`/`time`/`daterange`/`month`/`year` | +| format | string | `"YYYY-MM-DD"` | 표시/저장 형식 | +| placeholder | string | `"날짜 선택"` | 힌트 텍스트 | +| required | boolean | `false` | 필수 입력 | +| readonly | boolean | `false` | 읽기 전용 | +| disabled | boolean | `false` | 비활성화 | +| showTime | boolean | `false` | 시간 선택 표시 (datetime) | +| use24Hours | boolean | `true` | 24시간 형식 | +| range | boolean | - | 범위 선택 (시작~종료) | +| minDate | string | - | 선택 가능 최소 날짜 (ISO 8601) | +| maxDate | string | - | 선택 가능 최대 날짜 | +| showToday | boolean | - | 오늘 버튼 표시 | + +--- + +### 3.7 v2-button-primary (액션 버튼) + +**용도**: 저장, 삭제, 조회, 커스텀 등 액션 버튼. 제어관리(dataflow)와 연결 가능. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| text | string | `"저장"` | 버튼 텍스트 | +| actionType | string | `"button"` | 버튼 타입: `button`/`submit`/`reset` | +| variant | string | `"primary"` | 스타일: `primary`/`secondary`/`danger` | +| size | string | `"md"` | 크기: `sm`/`md`/`lg` | +| disabled | boolean | `false` | 비활성화 | +| action | object | - | 액션 설정 | +| action.type | string | `"save"` | 액션 유형: `save`/`delete`/`edit`/`copy`/`navigate`/`modal`/`control`/`custom` | +| action.successMessage | string | `"저장되었습니다."` | 성공 시 토스트 메시지 | +| action.errorMessage | string | `"오류가 발생했습니다."` | 실패 시 토스트 메시지 | +| webTypeConfig | object | - | 제어관리 연결 설정 | +| webTypeConfig.enableDataflowControl | boolean | - | 제어관리 활성화 | +| webTypeConfig.dataflowConfig | object | - | 제어관리 설정 | +| webTypeConfig.dataflowConfig.controlMode | string | - | `"relationship"`/`"flow"`/`"none"` | +| webTypeConfig.dataflowConfig.relationshipConfig | object | - | `{ relationshipId, executionTiming("before"/"after"/"replace") }` | +| webTypeConfig.dataflowConfig.flowConfig | object | - | `{ flowId, executionTiming }` | + +--- + +### 3.8 v2-table-grouped (그룹화 테이블) + +**용도**: 특정 컬럼 기준으로 데이터를 그룹화. 그룹별 접기/펼치기, 집계 표시. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| selectedTable | string | `""` | DB 테이블명 | +| columns | array | `[]` | 컬럼 설정 (v2-table-list와 동일) | +| showCheckbox | boolean | `false` | 체크박스 표시 | +| checkboxMode | `"single"\|"multi"` | `"multi"` | 체크박스 모드 | +| isReadOnly | boolean | `false` | 읽기 전용 | +| rowClickable | boolean | `true` | 행 클릭 가능 | +| showExpandAllButton | boolean | `true` | 전체 펼치기/접기 버튼 | +| groupHeaderStyle | string | `"default"` | 그룹 헤더 스타일 (`default`/`compact`/`card`) | +| emptyMessage | string | `"데이터가 없습니다."` | 빈 데이터 메시지 | +| height | string\|number | `"auto"` | 높이 | +| maxHeight | number | `600` | 최대 높이(px) | +| pagination.enabled | boolean | `false` | 페이지네이션 사용 | +| pagination.pageSize | number | `10` | 페이지 크기 | + +**groupConfig (그룹화 설정)**: + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| groupByColumn | string | `""` | **필수**. 그룹화 기준 컬럼 | +| groupLabelFormat | string | `"{value}"` | 그룹 라벨 포맷 | +| defaultExpanded | boolean | `true` | 초기 펼침 여부 | +| sortDirection | `"asc"\|"desc"` | `"asc"` | 그룹 정렬 방향 | +| summary.showCount | boolean | `true` | 그룹별 건수 표시 | +| summary.sumColumns | string[] | `[]` | 합계 표시할 컬럼 목록 | +| summary.avgColumns | string[] | - | 평균 표시 컬럼 | +| summary.maxColumns | string[] | - | 최대값 표시 컬럼 | +| summary.minColumns | string[] | - | 최소값 표시 컬럼 | + +--- + +### 3.9 v2-pivot-grid (피벗 분석) + +**용도**: 다차원 데이터 분석. 행/열/데이터/필터 영역에 필드를 배치하여 집계. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| fields | array | `[]` | **필수**. 피벗 필드 배열 | +| dataSource | object | - | 데이터 소스 (`type`, `tableName`, `joinConfigs`, `filterConditions`) | +| allowSortingBySummary | boolean | - | 집계값 기준 정렬 허용 | +| allowFiltering | boolean | - | 필터링 허용 | +| allowExpandAll | boolean | - | 전체 확장/축소 허용 | +| wordWrapEnabled | boolean | - | 텍스트 줄바꿈 | +| height | string\|number | - | 높이 | +| totals.showRowGrandTotals | boolean | - | 행 총합계 표시 | +| totals.showColumnGrandTotals | boolean | - | 열 총합계 표시 | +| chart.enabled | boolean | - | 차트 연동 표시 | +| chart.type | string | - | 차트 타입 (`bar`/`line`/`area`/`pie`/`stackedBar`) | + +**fields 배열 요소**: + +| 설정 | 타입 | 설명 | +|------|------|------| +| field | string | DB 컬럼명 | +| caption | string | 표시 라벨 | +| area | `"row"\|"column"\|"data"\|"filter"` | **필수**. 배치 영역 | +| summaryType | string | area=data일 때: `sum`/`count`/`avg`/`min`/`max`/`countDistinct` | +| groupInterval | string | 날짜 그룹화: `year`/`quarter`/`month`/`week`/`day` | +| sortBy | string | 정렬 기준: `value`/`caption` | +| sortOrder | string | 정렬 방향: `asc`/`desc`/`none` | + +--- + +### 3.10 v2-card-display (카드 뷰) + +**용도**: 테이블 데이터를 카드 형태로 표시. 이미지+제목+설명 구조. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dataSource | string | `"table"` | 데이터 소스: `table`/`static` | +| tableName | string | - | DB 테이블명 | +| cardsPerRow | number | `3` | 행당 카드 수 (1~6) | +| cardSpacing | number | `16` | 카드 간격(px) | +| columnMapping | object | `{}` | 필드 매핑 (`title`, `subtitle`, `description`, `image`, `status`) | +| cardStyle.showTitle | boolean | `true` | 제목 표시 | +| cardStyle.showSubtitle | boolean | `true` | 부제목 표시 | +| cardStyle.showDescription | boolean | `true` | 설명 표시 | +| cardStyle.showImage | boolean | `false` | 이미지 표시 | +| cardStyle.showActions | boolean | `true` | 액션 버튼 표시 | + +--- + +### 3.11 v2-timeline-scheduler (간트차트) + +**용도**: 시간축 기반 일정/계획 시각화. 드래그/리사이즈로 일정 편집. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| selectedTable | string | - | 스케줄 데이터 테이블 | +| resourceTable | string | `"equipment_mng"` | 리소스(설비/작업자) 테이블 | +| scheduleType | string | `"PRODUCTION"` | 스케줄 유형: `PRODUCTION`/`MAINTENANCE`/`SHIPPING`/`WORK_ASSIGN` | +| defaultZoomLevel | string | `"day"` | 초기 줌: `day`/`week`/`month` | +| editable | boolean | `true` | 편집 가능 | +| draggable | boolean | `true` | 드래그 이동 허용 | +| resizable | boolean | `true` | 기간 리사이즈 허용 | +| rowHeight | number | `50` | 행 높이(px) | +| headerHeight | number | `60` | 헤더 높이(px) | +| resourceColumnWidth | number | `150` | 리소스 컬럼 너비(px) | +| cellWidth.day | number | `60` | 일 단위 셀 너비 | +| cellWidth.week | number | `120` | 주 단위 셀 너비 | +| cellWidth.month | number | `40` | 월 단위 셀 너비 | +| showConflicts | boolean | `true` | 시간 겹침 충돌 표시 | +| showProgress | boolean | `true` | 진행률 바 표시 | +| showTodayLine | boolean | `true` | 오늘 날짜 표시선 | +| showToolbar | boolean | `true` | 상단 툴바 표시 | +| showAddButton | boolean | `true` | 추가 버튼 | +| height | number | `500` | 높이(px) | + +**fieldMapping (필수)**: + +| 설정 | 기본값 | 설명 | +|------|--------|------| +| id | `"schedule_id"` | 스케줄 PK 필드 | +| resourceId | `"resource_id"` | 리소스 FK 필드 | +| title | `"schedule_name"` | 제목 필드 | +| startDate | `"start_date"` | 시작일 필드 | +| endDate | `"end_date"` | 종료일 필드 | +| status | - | 상태 필드 | +| progress | - | 진행률 필드 (0~100) | + +**resourceFieldMapping**: + +| 설정 | 기본값 | 설명 | +|------|--------|------| +| id | `"equipment_code"` | 리소스 PK | +| name | `"equipment_name"` | 리소스 표시명 | +| group | - | 리소스 그룹 | + +**statusColors (상태별 색상)**: + +| 상태 | 기본 색상 | +|------|----------| +| planned | `"#3b82f6"` (파랑) | +| in_progress | `"#f59e0b"` (주황) | +| completed | `"#10b981"` (초록) | +| delayed | `"#ef4444"` (빨강) | +| cancelled | `"#6b7280"` (회색) | + +--- + +### 3.12 v2-tabs-widget (탭) + +**용도**: 탭 전환. 각 탭 내부에 컴포넌트 배치 가능. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| tabs | array | `[{id:"tab-1",label:"탭1",...}]` | 탭 배열 | +| defaultTab | string | `"tab-1"` | 기본 활성 탭 ID | +| orientation | string | `"horizontal"` | 탭 방향: `horizontal`/`vertical` | +| variant | string | `"default"` | 스타일: `default`/`pills`/`underline` | +| allowCloseable | boolean | `false` | 탭 닫기 버튼 표시 | +| persistSelection | boolean | `false` | 탭 선택 상태 localStorage 저장 | + +**tabs 배열 요소**: `{ id, label, order, disabled, icon, components[] }` +**components 요소**: `{ id, componentType, label, position, size, componentConfig }` + +--- + +### 3.13 v2-aggregation-widget (집계 카드) + +**용도**: 합계, 평균, 개수 등 집계값을 카드 형태로 표시. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dataSourceType | string | `"table"` | 데이터 소스: `table`/`component`/`selection` | +| tableName | string | - | 테이블명 | +| items | array | `[]` | 집계 항목 배열 | +| layout | string | `"horizontal"` | 배치: `horizontal`/`vertical` | +| showLabels | boolean | `true` | 라벨 표시 | +| showIcons | boolean | `true` | 아이콘 표시 | +| gap | string | `"16px"` | 항목 간격 | +| autoRefresh | boolean | `false` | 자동 새로고침 | +| refreshOnFormChange | boolean | `true` | 폼 변경 시 새로고침 | + +**items 요소**: `{ id, columnName, columnLabel, type("sum"/"avg"/"count"/"max"/"min"), format, decimalPlaces, prefix, suffix }` + +--- + +### 3.14 v2-status-count (상태별 건수) + +**용도**: 상태별 건수를 카드 형태로 표시. 대시보드/현황 화면용. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| title | string | `"상태 현황"` | 제목 | +| tableName | string | `""` | 대상 테이블 | +| statusColumn | string | `"status"` | 상태 컬럼명 | +| relationColumn | string | `""` | 관계 컬럼 (필터용) | +| items | array | - | 상태 항목 `[{ value, label, color }]` | +| showTotal | boolean | - | 합계 표시 | +| cardSize | string | `"md"` | 카드 크기: `sm`/`md`/`lg` | + +--- + +### 3.15 v2-text-display (텍스트 표시) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| text | string | `"텍스트를 입력하세요"` | 표시 텍스트 | +| fontSize | string | `"14px"` | 폰트 크기 | +| fontWeight | string | `"normal"` | 폰트 굵기 | +| color | string | `"#212121"` | 텍스트 색상 | +| textAlign | string | `"left"` | 정렬: `left`/`center`/`right` | +| backgroundColor | string | - | 배경색 | +| padding | string | - | 패딩 | + +--- + +### 3.16 v2-numbering-rule (자동 채번) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| ruleConfig | object | - | 채번 규칙 설정 | +| maxRules | number | `6` | 최대 파트 수 | +| readonly | boolean | `false` | 읽기 전용 | +| showPreview | boolean | `true` | 미리보기 표시 | +| showRuleList | boolean | `true` | 규칙 목록 표시 | +| cardLayout | string | `"vertical"` | 레이아웃: `vertical`/`horizontal` | + +--- + +### 3.17 v2-file-upload (파일 업로드) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| placeholder | string | `"파일을 선택하세요"` | 힌트 텍스트 | +| multiple | boolean | `true` | 다중 업로드 | +| accept | string | `"*/*"` | 허용 파일 형식 (예: `"image/*"`, `".pdf,.xlsx"`) | +| maxSize | number | `10485760` | 최대 파일 크기(bytes, 기본 10MB) | +| maxFiles | number | - | 최대 파일 수 | +| showPreview | boolean | - | 미리보기 표시 | +| showFileList | boolean | - | 파일 목록 표시 | +| allowDelete | boolean | - | 삭제 허용 | +| allowDownload | boolean | - | 다운로드 허용 | + +--- + +### 3.18 v2-section-card (그룹 컨테이너 - 테두리) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| title | string | `"섹션 제목"` | 제목 | +| description | string | `""` | 설명 | +| showHeader | boolean | `true` | 헤더 표시 | +| padding | string | `"md"` | 패딩: `none`/`sm`/`md`/`lg` | +| backgroundColor | string | `"default"` | 배경: `default`/`muted`/`transparent` | +| borderStyle | string | `"solid"` | 테두리: `solid`/`dashed`/`none` | +| collapsible | boolean | `false` | 접기/펼치기 가능 | +| defaultOpen | boolean | `true` | 기본 펼침 | + +--- + +### 3.19 v2-section-paper (그룹 컨테이너 - 배경색) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| backgroundColor | string | `"default"` | 배경: `default`/`muted`/`accent`/`primary`/`custom` | +| customColor | string | - | custom일 때 색상 | +| showBorder | boolean | `false` | 테두리 표시 | +| padding | string | `"md"` | 패딩: `none`/`sm`/`md`/`lg` | +| roundedCorners | string | `"md"` | 모서리: `none`/`sm`/`md`/`lg` | +| shadow | string | `"none"` | 그림자: `none`/`sm`/`md` | + +--- + +### 3.20 v2-divider-line (구분선) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| orientation | string | - | 방향 (가로/세로) | +| thickness | number | - | 두께 | + +--- + +### 3.21 v2-split-line (캔버스 분할선) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| resizable | boolean | `true` | 드래그 리사이즈 허용 | +| lineColor | string | `"#e2e8f0"` | 분할선 색상 | +| lineWidth | number | `4` | 분할선 두께(px) | + +--- + +### 3.22 v2-repeat-container (반복 렌더링) + +**용도**: 데이터 수만큼 내부 컴포넌트를 반복 렌더링. 카드 리스트 등에 사용. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dataSourceType | string | `"manual"` | 소스: `table-list`/`v2-repeater`/`externalData`/`manual` | +| dataSourceComponentId | string | - | 연결할 컴포넌트 ID | +| tableName | string | - | 테이블명 | +| layout | string | `"vertical"` | 배치: `vertical`/`horizontal`/`grid` | +| gridColumns | number | `2` | grid일 때 컬럼 수 | +| gap | string | `"16px"` | 아이템 간격 | +| showBorder | boolean | `true` | 카드 테두리 | +| showShadow | boolean | `false` | 카드 그림자 | +| borderRadius | string | `"8px"` | 모서리 둥글기 | +| backgroundColor | string | `"#ffffff"` | 배경색 | +| padding | string | `"16px"` | 패딩 | +| showItemTitle | boolean | `false` | 아이템 제목 표시 | +| itemTitleTemplate | string | `""` | 제목 템플릿 (예: `"{order_no} - {item}"`) | +| emptyMessage | string | `"데이터가 없습니다"` | 빈 상태 메시지 | +| clickable | boolean | `false` | 클릭 가능 | +| selectionMode | string | `"single"` | 선택 모드: `single`/`multiple` | +| usePaging | boolean | `false` | 페이징 사용 | +| pageSize | number | `10` | 페이지 크기 | + +--- + +### 3.23 v2-repeater (반복 데이터 관리) + +**용도**: 인라인/모달 모드로 반복 데이터(주문 상세 등) 관리. 행 추가/삭제/편집. + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| renderMode | string | `"inline"` | 모드: `inline` (인라인 편집) / `modal` (모달로 선택 추가) | +| mainTableName | string | - | 저장 대상 테이블 | +| foreignKeyColumn | string | - | 마스터 연결 FK 컬럼 | +| foreignKeySourceColumn | string | - | 마스터 PK 컬럼 | +| columns | array | `[]` | 컬럼 설정 | +| dataSource.tableName | string | - | 데이터 테이블 | +| dataSource.foreignKey | string | - | FK 컬럼 | +| dataSource.sourceTable | string | - | 모달용 소스 테이블 | +| modal.size | string | `"md"` | 모달 크기: `sm`/`md`/`lg`/`xl`/`full` | +| modal.title | string | - | 모달 제목 | +| modal.searchFields | string[] | - | 검색 필드 | +| features.showAddButton | boolean | `true` | 추가 버튼 | +| features.showDeleteButton | boolean | `true` | 삭제 버튼 | +| features.inlineEdit | boolean | `false` | 인라인 편집 | +| features.showRowNumber | boolean | `false` | 행 번호 표시 | +| calculationRules | array | - | 자동 계산 규칙 (예: 수량*단가=금액) | + +--- + +### 3.24 v2-approval-step (결재 스테퍼) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| targetTable | string | `""` | 결재 대상 테이블 | +| targetRecordIdField | string | `""` | 레코드 ID 필드 | +| displayMode | string | `"horizontal"` | 표시 방향: `horizontal`/`vertical` | +| showComment | boolean | `true` | 결재 코멘트 표시 | +| showTimestamp | boolean | `true` | 결재 시간 표시 | +| showDept | boolean | `true` | 부서 표시 | +| compact | boolean | `false` | 컴팩트 모드 | + +--- + +### 3.25 v2-bom-tree (BOM 트리) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| detailTable | string | `"bom_detail"` | BOM 디테일 테이블 | +| foreignKey | string | `"bom_id"` | BOM 마스터 FK | +| parentKey | string | `"parent_detail_id"` | 트리 부모 키 (자기참조) | + +--- + +### 3.26 v2-bom-item-editor (BOM 편집) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| detailTable | string | `"bom_detail"` | BOM 디테일 테이블 | +| sourceTable | string | `"item_info"` | 품목 소스 테이블 | +| foreignKey | string | `"bom_id"` | BOM 마스터 FK | +| parentKey | string | `"parent_detail_id"` | 트리 부모 키 | +| itemCodeField | string | `"item_number"` | 품목 코드 필드 | +| itemNameField | string | `"item_name"` | 품목명 필드 | +| itemTypeField | string | `"type"` | 품목 유형 필드 | +| itemUnitField | string | `"unit"` | 품목 단위 필드 | + +--- + +### 3.27 v2-category-manager (카테고리 관리) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| tableName | string | - | 대상 테이블 | +| columnName | string | - | 카테고리 컬럼 | +| menuObjid | number | - | 연결 메뉴 OBJID | +| viewMode | string | `"tree"` | 뷰 모드: `tree`/`list` | +| showViewModeToggle | boolean | `true` | 뷰 모드 토글 표시 | +| defaultExpandLevel | number | `1` | 기본 트리 펼침 레벨 | +| showInactiveItems | boolean | `false` | 비활성 항목 표시 | +| leftPanelWidth | number | `15` | 좌측 패널 너비 | + +--- + +### 3.28 v2-media (미디어) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| mediaType | string | `"file"` | 미디어 타입: `file`/`image`/`video`/`audio` | +| multiple | boolean | `false` | 다중 업로드 | +| preview | boolean | `true` | 미리보기 | +| maxSize | number | `10` | 최대 크기(MB) | +| accept | string | `"*/*"` | 허용 형식 | +| showFileList | boolean | `true` | 파일 목록 | +| dragDrop | boolean | `true` | 드래그앤드롭 | + +--- + +### 3.29 v2-location-swap-selector (위치 교환) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dataSource.type | string | `"static"` | 소스: `static`/`table`/`code` | +| dataSource.tableName | string | - | 장소 테이블 | +| dataSource.valueField | string | `"location_code"` | 값 필드 | +| dataSource.labelField | string | `"location_name"` | 표시 필드 | +| dataSource.staticOptions | array | - | 정적 옵션 `[{value, label}]` | +| departureField | string | `"departure"` | 출발지 저장 필드 | +| destinationField | string | `"destination"` | 도착지 저장 필드 | +| departureLabel | string | `"출발지"` | 출발지 라벨 | +| destinationLabel | string | `"도착지"` | 도착지 라벨 | +| showSwapButton | boolean | `true` | 교환 버튼 표시 | +| variant | string | `"card"` | UI: `card`/`inline`/`minimal` | + +--- + +### 3.30 v2-rack-structure (창고 랙) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| maxConditions | number | `10` | 최대 조건 수 | +| maxRows | number | `99` | 최대 열 수 | +| maxLevels | number | `20` | 최대 단 수 | +| codePattern | string | `"{warehouseCode}-{floor}{zone}-{row:02d}-{level}"` | 위치 코드 패턴 | +| namePattern | string | `"{zone}구역-{row:02d}열-{level}단"` | 위치 이름 패턴 | +| showTemplates | boolean | `true` | 템플릿 표시 | +| showPreview | boolean | `true` | 미리보기 | +| showStatistics | boolean | `true` | 통계 카드 | +| readonly | boolean | `false` | 읽기 전용 | + +--- + +### 3.31 v2-process-work-standard (공정 작업기준) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dataSource.itemTable | string | `"item_info"` | 품목 테이블 | +| dataSource.routingVersionTable | string | `"item_routing_version"` | 라우팅 버전 테이블 | +| dataSource.routingDetailTable | string | `"item_routing_detail"` | 라우팅 디테일 테이블 | +| dataSource.processTable | string | `"process_mng"` | 공정 테이블 | +| splitRatio | number | `30` | 좌우 분할 비율 | +| leftPanelTitle | string | `"품목 및 공정 선택"` | 좌측 패널 제목 | +| readonly | boolean | `false` | 읽기 전용 | +| itemListMode | string | `"all"` | 품목 모드: `all`/`registered` | + +--- + +### 3.32 v2-item-routing (품목 라우팅) + +| 설정 | 타입 | 기본값 | 설명 | +|------|------|--------|------| +| dataSource.itemTable | string | `"item_info"` | 품목 테이블 | +| dataSource.routingVersionTable | string | `"item_routing_version"` | 라우팅 버전 테이블 | +| dataSource.routingDetailTable | string | `"item_routing_detail"` | 라우팅 디테일 테이블 | +| dataSource.processTable | string | `"process_mng"` | 공정 테이블 | +| splitRatio | number | `40` | 좌우 분할 비율 | +| leftPanelTitle | string | `"품목 목록"` | 좌측 제목 | +| rightPanelTitle | string | `"공정 순서"` | 우측 제목 | +| readonly | boolean | `false` | 읽기 전용 | +| autoSelectFirstVersion | boolean | `true` | 첫 버전 자동 선택 | +| itemListMode | string | `"all"` | 품목 모드: `all`/`registered` | + +--- + +## 4. 패턴 의사결정 트리 + +``` +Q1. 시간축 기반 일정/간트차트? → v2-timeline-scheduler +Q2. 다차원 피벗 분석? → v2-pivot-grid +Q3. 그룹별 접기/펼치기? → v2-table-grouped +Q4. 카드 형태 표시? → v2-card-display +Q5. 마스터-디테일? + ├ 우측 멀티 탭? → v2-split-panel-layout + additionalTabs + └ 단일 디테일? → v2-split-panel-layout +Q6. 단일 테이블? → v2-table-search-widget + v2-table-list +``` + +--- + +## 5. 관계(relation) 레퍼런스 + +| 관계 유형 | 설정 | +|----------|------| +| 단순 FK | `{ type:"detail", leftColumn:"id", rightColumn:"{FK}", foreignKey:"{FK}" }` | +| 복합 키 | `{ type:"detail", keys:[{ leftColumn:"a", rightColumn:"b" }] }` | +| JOIN | `{ type:"join", leftColumn:"{col}", rightColumn:"{col}" }` | + +## 6. 엔티티 조인 + +FK 컬럼에 참조 테이블의 이름을 표시: + +**table_type_columns**: `input_type='entity'`, `detail_settings='{"referenceTable":"X","referenceColumn":"id","displayColumn":"name"}'` + +**layout_data columns**: `{ name:"fk_col", isEntityJoin:true, joinInfo:{ sourceTable:"A", sourceColumn:"fk_col", referenceTable:"X", joinAlias:"name" } }` diff --git a/docs/screen-implementation-guide/01_reference_guides/v2-component-usage-guide.md b/docs/screen-implementation-guide/01_reference_guides/v2-component-usage-guide.md new file mode 100644 index 00000000..14182a91 --- /dev/null +++ b/docs/screen-implementation-guide/01_reference_guides/v2-component-usage-guide.md @@ -0,0 +1,1146 @@ +# WACE 화면 구현 실행 가이드 (챗봇/AI 에이전트 전용) + +> **최종 업데이트**: 2026-03-13 +> **용도**: 사용자가 "수주관리 화면 만들어줘"라고 요청하면, 이 문서를 참조하여 SQL을 직접 생성하고 화면을 구현하는 AI 챗봇용 실행 가이드 +> **핵심**: 이 문서의 SQL 템플릿을 따라 INSERT하면 화면이 자동으로 생성된다 + +--- + +## 0. 절대 규칙 + +1. 사용자 업무 화면(수주, 생산, 품질 등)은 **React 코드(.tsx) 작성 금지** → DB INSERT로만 구현 +2. 모든 DB 컬럼은 **VARCHAR(500)** (날짜 컬럼만 TIMESTAMP) +3. 모든 테이블에 **기본 5개 컬럼** 필수: id, created_date, updated_date, writer, company_code +4. 모든 INSERT에 **ON CONFLICT** 절 필수 (중복 방지) +5. 컴포넌트는 반드시 **v2-** 접두사 사용 +6. **[최우선] 비즈니스 테이블 CREATE TABLE 시 NOT NULL / UNIQUE 제약조건 절대 금지!** + +> **왜 DB 레벨 제약조건을 걸면 안 되는가?** +> +> 이 시스템은 **멀티테넌시(Multi-Tenancy)** 환경이다. +> 각 회사(tenant)마다 같은 테이블을 공유하되, **필수값/유니크 규칙이 회사별로 다를 수 있다.** +> +> 따라서 제약조건은 DB에 직접 거는 것이 아니라, **관리자 메뉴에서 회사별 메타데이터**로 논리적으로 제어한다: +> - **필수값**: `table_type_columns.is_nullable = 'N'` → 애플리케이션 레벨에서 검증 +> - **유니크**: `table_type_columns.is_unique = 'Y'` → 애플리케이션 레벨에서 검증 +> +> DB 레벨에서 NOT NULL이나 UNIQUE를 걸면, **특정 회사에만 적용해야 할 규칙이 모든 회사에 강제되어** 멀티테넌시가 깨진다. +> +> **허용**: 기본 5개 컬럼의 `id` PRIMARY KEY, `DEFAULT` 값만 DB 레벨에서 설정 +> **금지**: 비즈니스 컬럼에 `NOT NULL`, `UNIQUE`, `CHECK`, `FOREIGN KEY` 등 DB 제약조건 직접 적용 + +--- + +## 1. 화면 생성 전체 파이프라인 + +사용자가 화면을 요청하면 아래 7단계를 순서대로 실행한다. + +``` +Step 1: 비즈니스 테이블 CREATE TABLE +Step 2: table_labels INSERT (테이블 라벨) +Step 3: table_type_columns INSERT (컬럼 타입 정의, company_code='*') +Step 4: column_labels INSERT (컬럼 한글 라벨) +Step 5: screen_definitions INSERT → screen_id 획득 +Step 6: screen_layouts_v2 INSERT (레이아웃 JSON) +Step 7: menu_info INSERT (메뉴 등록) +``` + +**선택적 추가 단계**: +- 채번 규칙이 필요하면: numbering_rules + numbering_rule_parts INSERT +- 카테고리가 필요하면: table_column_category_values INSERT +- 비즈니스 로직(버튼 액션)이 필요하면: dataflow_diagrams INSERT + +--- + +## 2. Step 1: 비즈니스 테이블 생성 (CREATE TABLE) + +### 템플릿 + +> **[최우선] 비즈니스 컬럼에 NOT NULL / UNIQUE / CHECK / FOREIGN KEY 제약조건 절대 금지!** +> 멀티테넌시 환경에서 회사별로 규칙이 다르므로, `table_type_columns`의 `is_nullable`, `is_unique` 메타데이터로 논리적 제어한다. + +```sql +CREATE TABLE "{테이블명}" ( + "id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text, + "created_date" timestamp DEFAULT now(), + "updated_date" timestamp DEFAULT now(), + "writer" varchar(500) DEFAULT NULL, + "company_code" varchar(500), + + "{비즈니스_컬럼1}" varchar(500), + "{비즈니스_컬럼2}" varchar(500), + "{비즈니스_컬럼3}" varchar(500) + -- NOT NULL, UNIQUE, CHECK, FOREIGN KEY 금지! +); +``` + +### 마스터-디테일인 경우 (2개 테이블) + +```sql +-- 마스터 테이블 +CREATE TABLE "{마스터_테이블}" ( + "id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text, + "created_date" timestamp DEFAULT now(), + "updated_date" timestamp DEFAULT now(), + "writer" varchar(500) DEFAULT NULL, + "company_code" varchar(500), + "{컬럼1}" varchar(500), + "{컬럼2}" varchar(500) + -- NOT NULL, UNIQUE, FOREIGN KEY 금지! +); + +-- 디테일 테이블 +CREATE TABLE "{디테일_테이블}" ( + "id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text, + "created_date" timestamp DEFAULT now(), + "updated_date" timestamp DEFAULT now(), + "writer" varchar(500) DEFAULT NULL, + "company_code" varchar(500), + "{마스터_FK}" varchar(500), -- 마스터 테이블 id 참조 (FOREIGN KEY 제약조건은 걸지 않는다!) + "{컬럼1}" varchar(500), + "{컬럼2}" varchar(500) + -- NOT NULL, UNIQUE, FOREIGN KEY 금지! +); +``` + +**금지 사항**: +- INTEGER, NUMERIC, BOOLEAN, TEXT, DATE 등 DB 타입 직접 사용 금지. 반드시 VARCHAR(500). +- 비즈니스 컬럼에 NOT NULL, UNIQUE, CHECK, FOREIGN KEY 등 DB 레벨 제약조건 금지. + +--- + +## 3. Step 2: table_labels INSERT + +```sql +INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date) +VALUES ('{테이블명}', '{한글_라벨}', '{설명}', now(), now()) +ON CONFLICT (table_name) +DO UPDATE SET table_label = EXCLUDED.table_label, description = EXCLUDED.description, updated_date = now(); +``` + +**예시**: +```sql +INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date) +VALUES ('order_master', '수주 마스터', '수주 헤더 정보 관리', now(), now()) +ON CONFLICT (table_name) +DO UPDATE SET table_label = EXCLUDED.table_label, description = EXCLUDED.description, updated_date = now(); + +INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date) +VALUES ('order_detail', '수주 상세', '수주 품목별 상세 정보', now(), now()) +ON CONFLICT (table_name) +DO UPDATE SET table_label = EXCLUDED.table_label, description = EXCLUDED.description, updated_date = now(); +``` + +--- + +## 4. Step 3: table_type_columns INSERT + +> `company_code = '*'` 로 등록한다 (전체 공통 설정). + +### 기본 5개 컬럼 (모든 테이블 공통) + +```sql +INSERT INTO table_type_columns ( + table_name, column_name, company_code, input_type, detail_settings, + is_nullable, is_unique, display_order, column_label, description, is_visible, + created_date, updated_date +) +VALUES + ('{테이블명}', 'id', '*', 'text', '{}', 'N', 'Y', -5, 'ID', '기본키 (자동생성)', false, now(), now()), + ('{테이블명}', 'created_date', '*', 'date', '{}', 'Y', 'N', -4, '생성일시', '레코드 생성일시', false, now(), now()), + ('{테이블명}', 'updated_date', '*', 'date', '{}', 'Y', 'N', -3, '수정일시', '레코드 수정일시', false, now(), now()), + ('{테이블명}', 'writer', '*', 'text', '{}', 'Y', 'N', -2, '작성자', '레코드 작성자', false, now(), now()), + ('{테이블명}', 'company_code', '*', 'text', '{}', 'Y', 'N', -1, '회사코드', '회사 구분 코드', false, now(), now()) +ON CONFLICT (table_name, column_name, company_code) +DO UPDATE SET input_type = EXCLUDED.input_type, detail_settings = EXCLUDED.detail_settings, + is_nullable = EXCLUDED.is_nullable, is_unique = EXCLUDED.is_unique, + display_order = EXCLUDED.display_order, column_label = EXCLUDED.column_label, + description = EXCLUDED.description, is_visible = EXCLUDED.is_visible, updated_date = now(); +``` + +### 비즈니스 컬럼 (display_order 0부터) + +```sql +INSERT INTO table_type_columns ( + table_name, column_name, company_code, input_type, detail_settings, + is_nullable, is_unique, display_order, column_label, description, is_visible, + created_date, updated_date +) +VALUES + ('{테이블명}', '{컬럼명}', '*', '{input_type}', '{detail_settings_json}', 'Y', 'N', {순서}, '{한글라벨}', '{설명}', true, now(), now()) +ON CONFLICT (table_name, column_name, company_code) +DO UPDATE SET input_type = EXCLUDED.input_type, detail_settings = EXCLUDED.detail_settings, + is_nullable = EXCLUDED.is_nullable, is_unique = EXCLUDED.is_unique, + display_order = EXCLUDED.display_order, column_label = EXCLUDED.column_label, + description = EXCLUDED.description, is_visible = EXCLUDED.is_visible, updated_date = now(); +``` + +### input_type 선택 기준 + +| 데이터 성격 | input_type | detail_settings 예시 | +|------------|-----------|---------------------| +| 일반 텍스트 | `text` | `'{}'` | +| 숫자 (수량, 금액) | `number` | `'{}'` | +| 날짜 | `date` | `'{}'` | +| 여러 줄 텍스트 (비고) | `textarea` | `'{}'` | +| 공통코드 선택 (상태 등) | `code` | `'{"codeCategory":"STATUS_CODE"}'` | +| 다른 테이블 참조 (거래처 등) | `entity` | `'{"referenceTable":"customer_info","referenceColumn":"id","displayColumn":"customer_name"}'` | +| 정적 옵션 선택 | `select` | `'{"options":[{"label":"옵션1","value":"v1"},{"label":"옵션2","value":"v2"}]}'` | +| 체크박스 | `checkbox` | `'{}'` | +| 라디오 | `radio` | `'{}'` | +| 카테고리 | `category` | `'{"categoryRef":"CAT_ID"}'` | +| 자동 채번 | `numbering` | `'{"numberingRuleId":"rule_id"}'` | + +--- + +## 5. Step 4: column_labels INSERT + +> 레거시 호환용이지만 **필수 등록**이다. table_type_columns와 동일한 값을 넣되, `column_label`(한글명)을 추가. +> +> **주의**: `column_labels` 테이블의 UNIQUE 제약조건은 `(table_name, column_name, company_code)` 3개 컬럼이다. 반드시 `company_code`를 포함해야 한다. + +```sql +-- 기본 5개 컬럼 +INSERT INTO column_labels (table_name, column_name, column_label, input_type, detail_settings, description, display_order, is_visible, company_code, created_date, updated_date) +VALUES + ('{테이블명}', 'id', 'ID', 'text', '{}', '기본키 (자동생성)', -5, true, '*', now(), now()), + ('{테이블명}', 'created_date', '생성일시', 'date', '{}', '레코드 생성일시', -4, true, '*', now(), now()), + ('{테이블명}', 'updated_date', '수정일시', 'date', '{}', '레코드 수정일시', -3, true, '*', now(), now()), + ('{테이블명}', 'writer', '작성자', 'text', '{}', '레코드 작성자', -2, true, '*', now(), now()), + ('{테이블명}', 'company_code', '회사코드', 'text', '{}', '회사 구분 코드', -1, true, '*', now(), now()) +ON CONFLICT (table_name, column_name, company_code) +DO UPDATE SET column_label = EXCLUDED.column_label, input_type = EXCLUDED.input_type, + detail_settings = EXCLUDED.detail_settings, description = EXCLUDED.description, + display_order = EXCLUDED.display_order, is_visible = EXCLUDED.is_visible, updated_date = now(); + +-- 비즈니스 컬럼 +INSERT INTO column_labels (table_name, column_name, column_label, input_type, detail_settings, description, display_order, is_visible, company_code, created_date, updated_date) +VALUES + ('{테이블명}', '{컬럼명}', '{한글라벨}', '{input_type}', '{detail_settings}', '{설명}', {순서}, true, '*', now(), now()) +ON CONFLICT (table_name, column_name, company_code) +DO UPDATE SET column_label = EXCLUDED.column_label, input_type = EXCLUDED.input_type, + detail_settings = EXCLUDED.detail_settings, description = EXCLUDED.description, + display_order = EXCLUDED.display_order, is_visible = EXCLUDED.is_visible, updated_date = now(); +``` + +--- + +## 6. Step 5: screen_definitions INSERT + +```sql +INSERT INTO screen_definitions ( + screen_name, screen_code, table_name, company_code, description, is_active, + db_source_type, data_source_type, created_date +) +VALUES ( + '{화면명}', -- 예: '수주관리' + '{screen_code}', -- 예: 'COMPANY_A_ORDER_MNG' (회사코드_식별자) + '{메인_테이블명}', -- 예: 'order_master' + '{company_code}', -- 예: 'COMPANY_A' + '{설명}', + 'Y', + 'internal', + 'database', + now() +) +RETURNING screen_id; +``` + +**screen_code 규칙**: `{company_code}_{영문식별자}` (예: `ILSHIN_ORDER_MNG`, `COMPANY_19_ITEM_INFO`) + +**중요**: Step 6, 7에서 `screen_id`가 필요하다. 서브쿼리로 참조하면 하드코딩 실수를 방지할 수 있다: +```sql +(SELECT screen_id FROM screen_definitions WHERE screen_code = '{screen_code}') +``` + +> **screen_code 조건부 UNIQUE 규칙**: +> `screen_code`는 단순 UNIQUE가 아니라 **`WHERE is_active <> 'D'`** 조건부 UNIQUE이다. +> - 삭제된 화면(`is_active = 'D'`)과 동일한 코드로 새 화면을 만들 수 있다. +> - 활성 상태(`'Y'` 또는 `'N'`)에서는 같은 `screen_code`가 중복되면 에러가 발생한다. +> - 화면 삭제 시 `DELETE`가 아닌 `UPDATE SET is_active = 'D'`로 소프트 삭제하므로, 이전 코드의 재사용이 가능하다. + +--- + +## 7. Step 6: screen_layouts_v2 INSERT (핵심) + +### 기본 구조 + +```sql +INSERT INTO screen_layouts_v2 (screen_id, company_code, layer_id, layer_name, layout_data, created_at, updated_at) +VALUES ( + (SELECT screen_id FROM screen_definitions WHERE screen_code = '{screen_code}'), + '{company_code}', + 1, -- 기본 레이어 + '기본 레이어', + '{layout_data_json}'::jsonb, + now(), + now() +) +ON CONFLICT (screen_id, company_code, layer_id) +DO UPDATE SET layout_data = EXCLUDED.layout_data, updated_at = now(); +``` + +### layout_data JSON 뼈대 + +```json +{ + "version": "2.0", + "components": [ + { + "id": "{고유ID}", + "url": "@/lib/registry/components/{컴포넌트타입}", + "position": { "x": 0, "y": 0 }, + "size": { "width": 1920, "height": 800 }, + "displayOrder": 0, + "overrides": { /* 컴포넌트별 설정 */ } + } + ], + "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, + "screenResolution": { "width": 1920, "height": 1080 } +} +``` + +### 컴포넌트 url 매핑표 + +| 컴포넌트 | url 값 | +|----------|--------| +| v2-table-list | `@/lib/registry/components/v2-table-list` | +| v2-table-search-widget | `@/lib/registry/components/v2-table-search-widget` | +| v2-split-panel-layout | `@/lib/registry/components/v2-split-panel-layout` | +| v2-table-grouped | `@/lib/registry/components/v2-table-grouped` | +| v2-tabs-widget | `@/lib/registry/components/v2-tabs-widget` | +| v2-button-primary | `@/lib/registry/components/v2-button-primary` | +| v2-input | `@/lib/registry/components/v2-input` | +| v2-select | `@/lib/registry/components/v2-select` | +| v2-date | `@/lib/registry/components/v2-date` | +| v2-card-display | `@/lib/registry/components/v2-card-display` | +| v2-pivot-grid | `@/lib/registry/components/v2-pivot-grid` | +| v2-timeline-scheduler | `@/lib/registry/components/v2-timeline-scheduler` | +| v2-text-display | `@/lib/registry/components/v2-text-display` | +| v2-aggregation-widget | `@/lib/registry/components/v2-aggregation-widget` | +| v2-numbering-rule | `@/lib/registry/components/v2-numbering-rule` | +| v2-file-upload | `@/lib/registry/components/v2-file-upload` | +| v2-section-card | `@/lib/registry/components/v2-section-card` | +| v2-divider-line | `@/lib/registry/components/v2-divider-line` | +| v2-bom-tree | `@/lib/registry/components/v2-bom-tree` | +| v2-approval-step | `@/lib/registry/components/v2-approval-step` | +| v2-status-count | `@/lib/registry/components/v2-status-count` | +| v2-section-paper | `@/lib/registry/components/v2-section-paper` | +| v2-split-line | `@/lib/registry/components/v2-split-line` | +| v2-repeat-container | `@/lib/registry/components/v2-repeat-container` | +| v2-repeater | `@/lib/registry/components/v2-repeater` | +| v2-category-manager | `@/lib/registry/components/v2-category-manager` | +| v2-media | `@/lib/registry/components/v2-media` | +| v2-location-swap-selector | `@/lib/registry/components/v2-location-swap-selector` | +| v2-rack-structure | `@/lib/registry/components/v2-rack-structure` | +| v2-process-work-standard | `@/lib/registry/components/v2-process-work-standard` | +| v2-item-routing | `@/lib/registry/components/v2-item-routing` | +| v2-bom-item-editor | `@/lib/registry/components/v2-bom-item-editor` | + +--- + +## 8. 패턴별 layout_data 완전 예시 + +### 8.1 패턴 A: 기본 마스터 (검색 + 테이블) + +**사용 조건**: 단일 테이블 CRUD, 마스터-디테일 관계 없음 + +```json +{ + "version": "2.0", + "components": [ + { + "id": "search_1", + "url": "@/lib/registry/components/v2-table-search-widget", + "position": { "x": 0, "y": 0 }, + "size": { "width": 1920, "height": 100 }, + "displayOrder": 0, + "overrides": { + "label": "검색", + "autoSelectFirstTable": true, + "showTableSelector": false + } + }, + { + "id": "table_1", + "url": "@/lib/registry/components/v2-table-list", + "position": { "x": 0, "y": 120 }, + "size": { "width": 1920, "height": 700 }, + "displayOrder": 1, + "overrides": { + "label": "{화면제목}", + "tableName": "{테이블명}", + "autoLoad": true, + "displayMode": "table", + "checkbox": { "enabled": true, "multiple": true, "position": "left", "selectAll": true }, + "pagination": { "enabled": true, "pageSize": 20, "showSizeSelector": true, "showPageInfo": true }, + "horizontalScroll": { "enabled": true, "maxVisibleColumns": 8 }, + "toolbar": { "showEditMode": true, "showExcel": true, "showRefresh": true } + } + } + ], + "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, + "screenResolution": { "width": 1920, "height": 1080 } +} +``` + +### 8.2 패턴 B: 마스터-디테일 (좌우 분할) + +**사용 조건**: 좌측 마스터 테이블 선택 → 우측 디테일 테이블 연동. 두 테이블 간 FK 관계. + +```json +{ + "version": "2.0", + "components": [ + { + "id": "split_1", + "url": "@/lib/registry/components/v2-split-panel-layout", + "position": { "x": 0, "y": 0 }, + "size": { "width": 1920, "height": 850 }, + "displayOrder": 0, + "overrides": { + "label": "{화면제목}", + "splitRatio": 35, + "resizable": true, + "autoLoad": true, + "syncSelection": true, + "leftPanel": { + "title": "{마스터_제목}", + "displayMode": "table", + "tableName": "{마스터_테이블명}", + "showSearch": true, + "showAdd": true, + "showEdit": false, + "showDelete": true, + "columns": [ + { "name": "{컬럼1}", "label": "{라벨1}", "width": 120, "sortable": true }, + { "name": "{컬럼2}", "label": "{라벨2}", "width": 150 }, + { "name": "{컬럼3}", "label": "{라벨3}", "width": 100 } + ], + "addButton": { "enabled": true, "mode": "auto" }, + "deleteButton": { "enabled": true, "confirmMessage": "선택한 항목을 삭제하시겠습니까?" } + }, + "rightPanel": { + "title": "{디테일_제목}", + "displayMode": "table", + "tableName": "{디테일_테이블명}", + "relation": { + "type": "detail", + "leftColumn": "id", + "rightColumn": "{마스터FK_컬럼}", + "foreignKey": "{마스터FK_컬럼}" + }, + "columns": [ + { "name": "{컬럼1}", "label": "{라벨1}", "width": 120 }, + { "name": "{컬럼2}", "label": "{라벨2}", "width": 150 }, + { "name": "{컬럼3}", "label": "{라벨3}", "width": 100, "editable": true } + ], + "addButton": { "enabled": true, "mode": "auto" }, + "editButton": { "enabled": true, "mode": "auto" }, + "deleteButton": { "enabled": true, "confirmMessage": "삭제하시겠습니까?" } + } + } + } + ], + "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, + "screenResolution": { "width": 1920, "height": 1080 } +} +``` + +### 8.3 패턴 C: 마스터-디테일 + 탭 + +**사용 조건**: 패턴 B에서 우측에 여러 종류의 상세를 탭으로 구분 + +패턴 B의 rightPanel에 **additionalTabs** 추가: + +```json +{ + "rightPanel": { + "title": "{디테일_제목}", + "displayMode": "table", + "tableName": "{기본탭_테이블}", + "relation": { + "type": "detail", + "leftColumn": "id", + "rightColumn": "{FK_컬럼}", + "foreignKey": "{FK_컬럼}" + }, + "additionalTabs": [ + { + "tabId": "tab_basic", + "label": "기본정보", + "tableName": "{기본정보_테이블}", + "displayMode": "table", + "relation": { "type": "detail", "leftColumn": "id", "rightColumn": "{FK}", "foreignKey": "{FK}" }, + "columns": [ /* 컬럼 배열 */ ], + "addButton": { "enabled": true }, + "deleteButton": { "enabled": true } + }, + { + "tabId": "tab_history", + "label": "이력", + "tableName": "{이력_테이블}", + "displayMode": "table", + "relation": { "type": "detail", "leftColumn": "id", "rightColumn": "{FK}", "foreignKey": "{FK}" }, + "columns": [ /* 컬럼 배열 */ ] + }, + { + "tabId": "tab_files", + "label": "첨부파일", + "tableName": "{파일_테이블}", + "displayMode": "table", + "relation": { "type": "detail", "leftColumn": "id", "rightColumn": "{FK}", "foreignKey": "{FK}" }, + "columns": [ /* 컬럼 배열 */ ] + } + ] + } +} +``` + +### 8.4 패턴 D: 그룹화 테이블 + +```json +{ + "version": "2.0", + "components": [ + { + "id": "grouped_1", + "url": "@/lib/registry/components/v2-table-grouped", + "position": { "x": 0, "y": 0 }, + "size": { "width": 1920, "height": 800 }, + "displayOrder": 0, + "overrides": { + "label": "{화면제목}", + "selectedTable": "{테이블명}", + "groupConfig": { + "groupByColumn": "{그룹기준_컬럼}", + "groupLabelFormat": "{value}", + "defaultExpanded": true, + "sortDirection": "asc", + "summary": { "showCount": true, "sumColumns": ["{합계컬럼1}", "{합계컬럼2}"] } + }, + "columns": [ + { "columnName": "{컬럼1}", "displayName": "{라벨1}", "visible": true, "width": 120 }, + { "columnName": "{컬럼2}", "displayName": "{라벨2}", "visible": true, "width": 150 } + ], + "showCheckbox": true, + "showExpandAllButton": true + } + } + ], + "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, + "screenResolution": { "width": 1920, "height": 1080 } +} +``` + +### 8.5 패턴 E: 타임라인/간트차트 + +```json +{ + "version": "2.0", + "components": [ + { + "id": "timeline_1", + "url": "@/lib/registry/components/v2-timeline-scheduler", + "position": { "x": 0, "y": 0 }, + "size": { "width": 1920, "height": 800 }, + "displayOrder": 0, + "overrides": { + "label": "{화면제목}", + "selectedTable": "{스케줄_테이블}", + "resourceTable": "{리소스_테이블}", + "fieldMapping": { + "id": "id", + "resourceId": "{리소스FK_컬럼}", + "title": "{제목_컬럼}", + "startDate": "{시작일_컬럼}", + "endDate": "{종료일_컬럼}", + "status": "{상태_컬럼}", + "progress": "{진행률_컬럼}" + }, + "resourceFieldMapping": { + "id": "id", + "name": "{리소스명_컬럼}", + "group": "{그룹_컬럼}" + }, + "defaultZoomLevel": "day", + "editable": true, + "allowDrag": true, + "allowResize": true + } + } + ], + "gridSettings": { "columns": 12, "gap": 16, "padding": 16 }, + "screenResolution": { "width": 1920, "height": 1080 } +} +``` + +--- + +## 9. Step 7: menu_info INSERT + +```sql +INSERT INTO menu_info ( + objid, menu_type, parent_obj_id, + menu_name_kor, menu_name_eng, seq, + menu_url, menu_desc, writer, regdate, status, + company_code, screen_code +) +VALUES ( + {고유_objid}, + 0, + {부모_메뉴_objid}, + '{메뉴명_한글}', + '{메뉴명_영문}', + {정렬순서}, + '/screen/{screen_code}', + '{메뉴_설명}', + 'admin', + now(), + 'active', + '{company_code}', + '{screen_code}' +); +``` + +- `objid`: BIGINT 고유값. `extract(epoch from now())::bigint * 1000` 으로 생성 +- `menu_type`: `0` = 말단 메뉴(화면), `1` = 폴더 +- `parent_obj_id`: 상위 폴더 메뉴의 objid + +**objid 생성 규칙 및 주의사항**: + +기본 생성: `extract(epoch from now())::bigint * 1000` + +> **여러 메뉴를 한 트랜잭션에서 동시에 INSERT할 때 PK 중복 위험!** +> `now()`는 같은 트랜잭션 안에서 동일한 값을 반환하므로, 복수 INSERT 시 objid가 충돌한다. +> 반드시 순서값을 더해서 고유성을 보장할 것: +> +> ```sql +> -- 폴더 메뉴 +> extract(epoch from now())::bigint * 1000 + 1 +> -- 화면 메뉴 1 +> extract(epoch from now())::bigint * 1000 + 2 +> -- 화면 메뉴 2 +> extract(epoch from now())::bigint * 1000 + 3 +> ``` + +--- + +## 10. 선택적 단계: 채번 규칙 설정 + +자동으로 코드/번호를 생성해야 하는 컬럼이 있을 때 사용. + +### numbering_rules INSERT + +```sql +INSERT INTO numbering_rules ( + rule_id, rule_name, description, separator, reset_period, + current_sequence, table_name, column_name, company_code, + created_at, updated_at, created_by +) +VALUES ( + '{rule_id}', -- 예: 'ORDER_NO_RULE' + '{규칙명}', -- 예: '수주번호 채번' + '{설명}', + '-', -- 구분자 + 'year', -- 'none', 'year', 'month', 'day' + 1, -- 시작 순번 + '{테이블명}', -- 예: 'order_master' + '{컬럼명}', -- 예: 'order_no' + '{company_code}', + now(), now(), 'admin' +); +``` + +### numbering_rule_parts INSERT (채번 구성 파트) + +```sql +-- 파트 1: 접두사 +INSERT INTO numbering_rule_parts (rule_id, part_order, part_type, generation_method, auto_config, manual_config, company_code, created_at) +VALUES ('{rule_id}', 1, 'prefix', 'auto', '{"prefix": "SO", "separatorAfter": "-"}'::jsonb, '{}'::jsonb, '{company_code}', now()); + +-- 파트 2: 날짜 +INSERT INTO numbering_rule_parts (rule_id, part_order, part_type, generation_method, auto_config, manual_config, company_code, created_at) +VALUES ('{rule_id}', 2, 'date', 'auto', '{"format": "YYYYMM", "separatorAfter": "-"}'::jsonb, '{}'::jsonb, '{company_code}', now()); + +-- 파트 3: 순번 +INSERT INTO numbering_rule_parts (rule_id, part_order, part_type, generation_method, auto_config, manual_config, company_code, created_at) +VALUES ('{rule_id}', 3, 'sequence', 'auto', '{"digits": 4, "startFrom": 1}'::jsonb, '{}'::jsonb, '{company_code}', now()); +``` + +**결과**: `SO-202603-0001`, `SO-202603-0002`, ... + +--- + +## 11. 선택적 단계: 카테고리 값 설정 + +상태, 유형 등을 카테고리로 관리할 때 사용. + +### table_column_category_values INSERT + +```sql +INSERT INTO table_column_category_values ( + table_name, column_name, value_code, value_label, value_order, + parent_value_id, depth, description, color, company_code, created_by +) +VALUES + ('{테이블명}', '{컬럼명}', 'ACTIVE', '활성', 1, NULL, 1, '활성 상태', '#22c55e', '{company_code}', 'admin'), + ('{테이블명}', '{컬럼명}', 'INACTIVE', '비활성', 2, NULL, 1, '비활성 상태', '#ef4444', '{company_code}', 'admin'), + ('{테이블명}', '{컬럼명}', 'PENDING', '대기', 3, NULL, 1, '승인 대기', '#f59e0b', '{company_code}', 'admin'); +``` + +--- + +## 12. 패턴 판단 의사결정 트리 + +사용자가 화면을 요청하면 이 트리로 패턴을 결정한다. + +``` +Q1. 시간축 기반 일정/간트차트가 필요한가? +├─ YES → 패턴 E (타임라인) → v2-timeline-scheduler +└─ NO ↓ + +Q2. 다차원 집계/피벗 분석이 필요한가? +├─ YES → 피벗 → v2-pivot-grid +└─ NO ↓ + +Q3. 데이터를 그룹별로 접기/펼치기가 필요한가? +├─ YES → 패턴 D (그룹화) → v2-table-grouped +└─ NO ↓ + +Q4. 이미지+정보를 카드 형태로 표시하는가? +├─ YES → 카드뷰 → v2-card-display +└─ NO ↓ + +Q5. 마스터 테이블 선택 시 연관 디테일이 필요한가? +├─ YES → Q5-1. 디테일에 여러 탭이 필요한가? +│ ├─ YES → 패턴 C (마스터-디테일+탭) → v2-split-panel-layout + additionalTabs +│ └─ NO → 패턴 B (마스터-디테일) → v2-split-panel-layout +└─ NO → 패턴 A (기본 마스터) → v2-table-search-widget + v2-table-list +``` + +--- + +## 13. 화면 간 연결 관계 정의 + +### 13.1 마스터-디테일 관계 (v2-split-panel-layout) + +좌측 마스터 테이블의 행을 선택하면, 우측 디테일 테이블이 해당 FK로 필터링된다. + +**relation 설정**: + +> **JSON 안에 주석(`//`, `/* */`) 절대 금지!** PostgreSQL `::jsonb` 캐스팅 시 파싱 에러 발생. 설명은 반드시 JSON 바깥에 작성한다. + +- `type`: `"detail"` (FK 관계) +- `leftColumn`: 마스터 테이블의 PK 컬럼 (보통 `"id"`) +- `rightColumn`: 디테일 테이블의 FK 컬럼 +- `foreignKey`: `rightColumn`과 동일한 값 + +```json +{ + "relation": { + "type": "detail", + "leftColumn": "id", + "rightColumn": "master_id", + "foreignKey": "master_id" + } +} +``` + +**복합 키인 경우**: + +```json +{ + "relation": { + "type": "detail", + "keys": [ + { "leftColumn": "order_no", "rightColumn": "order_no" }, + { "leftColumn": "company_code", "rightColumn": "company_code" } + ] + } +} +``` + +### 13.2 엔티티 조인 (테이블 참조 표시) + +디테일 테이블의 FK 컬럼에 다른 테이블의 이름을 표시하고 싶을 때. + +**table_type_columns에서 설정**: + +```sql +INSERT INTO table_type_columns (table_name, column_name, company_code, input_type, detail_settings, ...) +VALUES ('order_detail', 'item_id', '*', 'entity', + '{"referenceTable":"item_info","referenceColumn":"id","displayColumn":"item_name"}', ...); +``` + +**v2-table-list columns에서 설정**: + +```json +{ + "columns": [ + { + "name": "item_id", + "label": "품목", + "isEntityJoin": true, + "joinInfo": { + "sourceTable": "order_detail", + "sourceColumn": "item_id", + "referenceTable": "item_info", + "joinAlias": "item_name" + } + } + ] +} +``` + +### 13.3 모달 화면 연결 + +추가/편집 버튼 클릭 시 별도 모달 화면을 띄우는 경우. + +1. **모달용 screen_definitions INSERT** (별도 화면 생성) +2. split-panel의 addButton/editButton에서 연결: + +```json +{ + "addButton": { + "enabled": true, + "mode": "modal", + "modalScreenId": "{모달_screen_id}" + }, + "editButton": { + "enabled": true, + "mode": "modal", + "modalScreenId": "{모달_screen_id}" + } +} +``` + +--- + +## 14. 비즈니스 로직 설정 (제어관리) + +버튼 클릭 시 INSERT/UPDATE/DELETE, 상태 변경, 이력 기록 등이 필요한 경우. + +### 14.1 v2-button-primary overrides + +```json +{ + "id": "btn_confirm", + "url": "@/lib/registry/components/v2-button-primary", + "position": { "x": 1700, "y": 10 }, + "size": { "width": 100, "height": 40 }, + "overrides": { + "text": "확정", + "variant": "primary", + "actionType": "button", + "action": { "type": "custom" }, + "webTypeConfig": { + "enableDataflowControl": true, + "dataflowConfig": { + "controlMode": "relationship", + "relationshipConfig": { + "relationshipId": "{관계_ID}", + "relationshipName": "{관계명}", + "executionTiming": "after" + } + } + } + } +} +``` + +### 14.2 dataflow_diagrams INSERT + +```sql +INSERT INTO dataflow_diagrams ( + diagram_name, company_code, + relationships, control, plan, node_positions +) +VALUES ( + '{관계도명}', + '{company_code}', + '[{"fromTable":"{소스_테이블}","toTable":"{타겟_테이블}","relationType":"data_save"}]'::jsonb, + '[{ + "conditions": [{"field":"status","operator":"=","value":"대기","dataType":"string"}], + "triggerType": "update" + }]'::jsonb, + '[{ + "actions": [ + { + "actionType": "update", + "targetTable": "{타겟_테이블}", + "conditions": [{"field":"status","operator":"=","value":"대기"}], + "fieldMappings": [{"targetField":"status","defaultValue":"확정"}] + }, + { + "actionType": "insert", + "targetTable": "{이력_테이블}", + "fieldMappings": [ + {"sourceField":"order_no","targetField":"order_no"}, + {"targetField":"action","defaultValue":"확정"} + ] + } + ] + }]'::jsonb, + '[]'::jsonb +) +RETURNING diagram_id; +``` + +**executionTiming 선택**: +- `before`: 메인 액션 전 → 조건 체크 (조건 불충족 시 메인 액션 중단) +- `after`: 메인 액션 후 → 후처리 (이력 기록, 상태 변경 등) +- `replace`: 메인 액션 대체 → 제어만 실행 + +--- + +## 15. 전체 예시: "수주관리 화면 만들어줘" + +### 요구사항 해석 +- 마스터: order_master (수주번호, 거래처, 수주일자, 상태) +- 디테일: order_detail (품목, 수량, 단가, 금액) +- 패턴: B (마스터-디테일) + +### 실행 SQL + +```sql +-- ===== Step 1: 테이블 생성 ===== +CREATE TABLE "order_master" ( + "id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text, + "created_date" timestamp DEFAULT now(), + "updated_date" timestamp DEFAULT now(), + "writer" varchar(500) DEFAULT NULL, + "company_code" varchar(500), + "order_no" varchar(500), + "customer_id" varchar(500), + "order_date" varchar(500), + "delivery_date" varchar(500), + "status" varchar(500), + "total_amount" varchar(500), + "notes" varchar(500) +); + +CREATE TABLE "order_detail" ( + "id" varchar(500) PRIMARY KEY DEFAULT gen_random_uuid()::text, + "created_date" timestamp DEFAULT now(), + "updated_date" timestamp DEFAULT now(), + "writer" varchar(500) DEFAULT NULL, + "company_code" varchar(500), + "order_master_id" varchar(500), + "item_id" varchar(500), + "quantity" varchar(500), + "unit_price" varchar(500), + "amount" varchar(500), + "notes" varchar(500) +); + +-- ===== Step 2: table_labels ===== +INSERT INTO table_labels (table_name, table_label, description, created_date, updated_date) VALUES + ('order_master', '수주 마스터', '수주 헤더 정보', now(), now()), + ('order_detail', '수주 상세', '수주 품목별 상세', now(), now()) +ON CONFLICT (table_name) DO UPDATE SET table_label = EXCLUDED.table_label, description = EXCLUDED.description, updated_date = now(); + +-- ===== Step 3: table_type_columns (확장 컬럼 포함) ===== +-- order_master 기본 + 비즈니스 컬럼 +INSERT INTO table_type_columns ( + table_name, column_name, company_code, input_type, detail_settings, + is_nullable, is_unique, display_order, column_label, description, is_visible, + created_date, updated_date +) VALUES + ('order_master', 'id', '*', 'text', '{}', 'N', 'Y', -5, 'ID', '기본키', false, now(), now()), + ('order_master', 'created_date', '*', 'date', '{}', 'Y', 'N', -4, '생성일시', '레코드 생성일시', false, now(), now()), + ('order_master', 'updated_date', '*', 'date', '{}', 'Y', 'N', -3, '수정일시', '레코드 수정일시', false, now(), now()), + ('order_master', 'writer', '*', 'text', '{}', 'Y', 'N', -2, '작성자', '레코드 작성자', false, now(), now()), + ('order_master', 'company_code', '*', 'text', '{}', 'Y', 'N', -1, '회사코드', '회사 구분 코드', false, now(), now()), + ('order_master', 'order_no', '*', 'text', '{}', 'N', 'Y', 0, '수주번호', '수주 식별번호', true, now(), now()), + ('order_master', 'customer_id', '*', 'entity', '{"referenceTable":"customer_info","referenceColumn":"id","displayColumn":"customer_name"}', 'N', 'N', 1, '거래처', '거래처 참조', true, now(), now()), + ('order_master', 'order_date', '*', 'date', '{}', 'N', 'N', 2, '수주일자', '', true, now(), now()), + ('order_master', 'delivery_date', '*', 'date', '{}', 'Y', 'N', 3, '납기일', '', true, now(), now()), + ('order_master', 'status', '*', 'code', '{"codeCategory":"ORDER_STATUS"}', 'Y', 'N', 4, '상태', '수주 상태', true, now(), now()), + ('order_master', 'total_amount', '*', 'number', '{}', 'Y', 'N', 5, '총금액', '', true, now(), now()), + ('order_master', 'notes', '*', 'textarea', '{}', 'Y', 'N', 6, '비고', '', true, now(), now()) +ON CONFLICT (table_name, column_name, company_code) DO UPDATE SET + input_type = EXCLUDED.input_type, detail_settings = EXCLUDED.detail_settings, + is_nullable = EXCLUDED.is_nullable, is_unique = EXCLUDED.is_unique, + display_order = EXCLUDED.display_order, column_label = EXCLUDED.column_label, + description = EXCLUDED.description, is_visible = EXCLUDED.is_visible, updated_date = now(); + +-- order_detail 기본 + 비즈니스 컬럼 +INSERT INTO table_type_columns ( + table_name, column_name, company_code, input_type, detail_settings, + is_nullable, is_unique, display_order, column_label, description, is_visible, + created_date, updated_date +) VALUES + ('order_detail', 'id', '*', 'text', '{}', 'N', 'Y', -5, 'ID', '기본키', false, now(), now()), + ('order_detail', 'created_date', '*', 'date', '{}', 'Y', 'N', -4, '생성일시', '레코드 생성일시', false, now(), now()), + ('order_detail', 'updated_date', '*', 'date', '{}', 'Y', 'N', -3, '수정일시', '레코드 수정일시', false, now(), now()), + ('order_detail', 'writer', '*', 'text', '{}', 'Y', 'N', -2, '작성자', '레코드 작성자', false, now(), now()), + ('order_detail', 'company_code', '*', 'text', '{}', 'Y', 'N', -1, '회사코드', '회사 구분 코드', false, now(), now()), + ('order_detail', 'order_master_id', '*', 'text', '{}', 'N', 'N', 0, '수주마스터ID', 'FK', false, now(), now()), + ('order_detail', 'item_id', '*', 'entity', '{"referenceTable":"item_info","referenceColumn":"id","displayColumn":"item_name"}', 'N', 'N', 1, '품목', '품목 참조', true, now(), now()), + ('order_detail', 'quantity', '*', 'number', '{}', 'N', 'N', 2, '수량', '', true, now(), now()), + ('order_detail', 'unit_price', '*', 'number', '{}', 'Y', 'N', 3, '단가', '', true, now(), now()), + ('order_detail', 'amount', '*', 'number', '{}', 'Y', 'N', 4, '금액', '', true, now(), now()), + ('order_detail', 'notes', '*', 'textarea', '{}', 'Y', 'N', 5, '비고', '', true, now(), now()) +ON CONFLICT (table_name, column_name, company_code) DO UPDATE SET + input_type = EXCLUDED.input_type, detail_settings = EXCLUDED.detail_settings, + is_nullable = EXCLUDED.is_nullable, is_unique = EXCLUDED.is_unique, + display_order = EXCLUDED.display_order, column_label = EXCLUDED.column_label, + description = EXCLUDED.description, is_visible = EXCLUDED.is_visible, updated_date = now(); + +-- ===== Step 4: column_labels (company_code 필수!) ===== +INSERT INTO column_labels (table_name, column_name, column_label, input_type, detail_settings, description, display_order, is_visible, company_code, created_date, updated_date) VALUES + ('order_master', 'id', 'ID', 'text', '{}', '기본키', -5, true, '*', now(), now()), + ('order_master', 'created_date', '생성일시', 'date', '{}', '', -4, true, '*', now(), now()), + ('order_master', 'updated_date', '수정일시', 'date', '{}', '', -3, true, '*', now(), now()), + ('order_master', 'writer', '작성자', 'text', '{}', '', -2, true, '*', now(), now()), + ('order_master', 'company_code', '회사코드', 'text', '{}', '', -1, true, '*', now(), now()), + ('order_master', 'order_no', '수주번호', 'text', '{}', '수주 식별번호', 0, true, '*', now(), now()), + ('order_master', 'customer_id', '거래처', 'entity', '{"referenceTable":"customer_info","referenceColumn":"id","displayColumn":"customer_name"}', '거래처 참조', 1, true, '*', now(), now()), + ('order_master', 'order_date', '수주일자', 'date', '{}', '', 2, true, '*', now(), now()), + ('order_master', 'delivery_date', '납기일', 'date', '{}', '', 3, true, '*', now(), now()), + ('order_master', 'status', '상태', 'code', '{"codeCategory":"ORDER_STATUS"}', '수주 상태', 4, true, '*', now(), now()), + ('order_master', 'total_amount', '총금액', 'number', '{}', '', 5, true, '*', now(), now()), + ('order_master', 'notes', '비고', 'textarea', '{}', '', 6, true, '*', now(), now()) +ON CONFLICT (table_name, column_name, company_code) DO UPDATE SET + column_label = EXCLUDED.column_label, input_type = EXCLUDED.input_type, + detail_settings = EXCLUDED.detail_settings, description = EXCLUDED.description, + display_order = EXCLUDED.display_order, is_visible = EXCLUDED.is_visible, updated_date = now(); + +INSERT INTO column_labels (table_name, column_name, column_label, input_type, detail_settings, description, display_order, is_visible, company_code, created_date, updated_date) VALUES + ('order_detail', 'id', 'ID', 'text', '{}', '기본키', -5, true, '*', now(), now()), + ('order_detail', 'created_date', '생성일시', 'date', '{}', '', -4, true, '*', now(), now()), + ('order_detail', 'updated_date', '수정일시', 'date', '{}', '', -3, true, '*', now(), now()), + ('order_detail', 'writer', '작성자', 'text', '{}', '', -2, true, '*', now(), now()), + ('order_detail', 'company_code', '회사코드', 'text', '{}', '', -1, true, '*', now(), now()), + ('order_detail', 'order_master_id', '수주마스터ID', 'text', '{}', 'FK', 0, true, '*', now(), now()), + ('order_detail', 'item_id', '품목', 'entity', '{"referenceTable":"item_info","referenceColumn":"id","displayColumn":"item_name"}', '품목 참조', 1, true, '*', now(), now()), + ('order_detail', 'quantity', '수량', 'number', '{}', '', 2, true, '*', now(), now()), + ('order_detail', 'unit_price', '단가', 'number', '{}', '', 3, true, '*', now(), now()), + ('order_detail', 'amount', '금액', 'number', '{}', '', 4, true, '*', now(), now()), + ('order_detail', 'notes', '비고', 'textarea', '{}', '', 5, true, '*', now(), now()) +ON CONFLICT (table_name, column_name, company_code) DO UPDATE SET + column_label = EXCLUDED.column_label, input_type = EXCLUDED.input_type, + detail_settings = EXCLUDED.detail_settings, description = EXCLUDED.description, + display_order = EXCLUDED.display_order, is_visible = EXCLUDED.is_visible, updated_date = now(); + +-- ===== Step 5: screen_definitions ===== +INSERT INTO screen_definitions (screen_name, screen_code, table_name, company_code, description, is_active, db_source_type, data_source_type, created_date) +VALUES ('수주관리', 'ILSHIN_ORDER_MNG', 'order_master', 'ILSHIN', '수주 마스터-디테일 관리', 'Y', 'internal', 'database', now()); + +-- ===== Step 6: screen_layouts_v2 (서브쿼리로 screen_id 참조) ===== +INSERT INTO screen_layouts_v2 (screen_id, company_code, layer_id, layer_name, layout_data, created_at, updated_at) +VALUES ( + (SELECT screen_id FROM screen_definitions WHERE screen_code = 'ILSHIN_ORDER_MNG'), + 'ILSHIN', 1, '기본 레이어', + '{ + "version": "2.0", + "components": [ + { + "id": "split_order", + "url": "@/lib/registry/components/v2-split-panel-layout", + "position": {"x": 0, "y": 0}, + "size": {"width": 1920, "height": 850}, + "displayOrder": 0, + "overrides": { + "label": "수주관리", + "splitRatio": 35, + "resizable": true, + "autoLoad": true, + "syncSelection": true, + "leftPanel": { + "title": "수주 목록", + "displayMode": "table", + "tableName": "order_master", + "showSearch": true, + "showAdd": true, + "showDelete": true, + "columns": [ + {"name": "order_no", "label": "수주번호", "width": 120, "sortable": true}, + {"name": "customer_id", "label": "거래처", "width": 150, "isEntityJoin": true, "joinInfo": {"sourceTable": "order_master", "sourceColumn": "customer_id", "referenceTable": "customer_info", "joinAlias": "customer_name"}}, + {"name": "order_date", "label": "수주일자", "width": 100}, + {"name": "status", "label": "상태", "width": 80}, + {"name": "total_amount", "label": "총금액", "width": 120} + ], + "addButton": {"enabled": true, "mode": "auto"}, + "deleteButton": {"enabled": true, "confirmMessage": "선택한 수주를 삭제하시겠습니까?"} + }, + "rightPanel": { + "title": "수주 상세", + "displayMode": "table", + "tableName": "order_detail", + "relation": { + "type": "detail", + "leftColumn": "id", + "rightColumn": "order_master_id", + "foreignKey": "order_master_id" + }, + "columns": [ + {"name": "item_id", "label": "품목", "width": 150, "isEntityJoin": true, "joinInfo": {"sourceTable": "order_detail", "sourceColumn": "item_id", "referenceTable": "item_info", "joinAlias": "item_name"}}, + {"name": "quantity", "label": "수량", "width": 80, "editable": true}, + {"name": "unit_price", "label": "단가", "width": 100, "editable": true}, + {"name": "amount", "label": "금액", "width": 100}, + {"name": "notes", "label": "비고", "width": 200, "editable": true} + ], + "addButton": {"enabled": true, "mode": "auto"}, + "editButton": {"enabled": true, "mode": "auto"}, + "deleteButton": {"enabled": true, "confirmMessage": "삭제하시겠습니까?"} + } + } + } + ], + "gridSettings": {"columns": 12, "gap": 16, "padding": 16}, + "screenResolution": {"width": 1920, "height": 1080} + }'::jsonb, + now(), now() +) +ON CONFLICT (screen_id, company_code, layer_id) DO UPDATE SET layout_data = EXCLUDED.layout_data, updated_at = now(); + +-- ===== Step 7: menu_info (objid에 순서값 더해서 PK 충돌 방지) ===== +INSERT INTO menu_info ( + objid, menu_type, parent_obj_id, menu_name_kor, menu_name_eng, + seq, menu_url, menu_desc, writer, regdate, status, company_code, screen_code +) +VALUES ( + extract(epoch from now())::bigint * 1000 + 1, 0, {부모_메뉴_objid}, + '수주관리', 'Order Management', + 1, '/screen/ILSHIN_ORDER_MNG', '수주 마스터-디테일 관리', + 'admin', now(), 'active', 'ILSHIN', 'ILSHIN_ORDER_MNG' +); +``` + +--- + +## 16. 컴포넌트 빠른 참조표 + +| 요구사항 | 컴포넌트 url | 핵심 overrides | +|----------|-------------|---------------| +| 데이터 테이블 | v2-table-list | `tableName`, `columns`, `pagination` | +| 검색 바 | v2-table-search-widget | `autoSelectFirstTable` | +| 좌우 분할 | v2-split-panel-layout | `leftPanel`, `rightPanel`, `relation`, `splitRatio` | +| 그룹화 테이블 | v2-table-grouped | `groupConfig.groupByColumn`, `summary` | +| 간트차트 | v2-timeline-scheduler | `fieldMapping`, `resourceTable` | +| 피벗 분석 | v2-pivot-grid | `fields(area, summaryType)` | +| 카드 뷰 | v2-card-display | `columnMapping`, `cardsPerRow` | +| 액션 버튼 | v2-button-primary | `text`, `actionType`, `webTypeConfig.dataflowConfig` | +| 텍스트 입력 | v2-input | `inputType`, `tableName`, `columnName` | +| 선택 | v2-select | `mode`, `source` | +| 날짜 | v2-date | `dateType` | +| 자동 채번 | v2-numbering-rule | `rule` | +| BOM 트리 | v2-bom-tree | `detailTable`, `foreignKey`, `parentKey` | +| BOM 편집 | v2-bom-item-editor | `detailTable`, `sourceTable`, `itemCodeField` | +| 결재 스테퍼 | v2-approval-step | `targetTable`, `displayMode` | +| 파일 업로드 | v2-file-upload | `multiple`, `accept`, `maxSize` | +| 상태별 건수 | v2-status-count | `tableName`, `statusColumn`, `items` | +| 집계 카드 | v2-aggregation-widget | `tableName`, `items` | +| 반복 데이터 관리 | v2-repeater | `renderMode`, `mainTableName`, `foreignKeyColumn` | +| 반복 렌더링 | v2-repeat-container | `dataSourceType`, `layout`, `gridColumns` | +| 그룹 컨테이너 (테두리) | v2-section-card | `title`, `collapsible`, `borderStyle` | +| 그룹 컨테이너 (배경색) | v2-section-paper | `backgroundColor`, `shadow`, `padding` | +| 캔버스 분할선 | v2-split-line | `resizable`, `lineColor`, `lineWidth` | +| 카테고리 관리 | v2-category-manager | `tableName`, `columnName`, `menuObjid` | +| 미디어 | v2-media | `mediaType`, `multiple`, `maxSize` | +| 위치 교환 | v2-location-swap-selector | `dataSource`, `departureField`, `destinationField` | +| 창고 랙 | v2-rack-structure | `codePattern`, `namePattern`, `maxRows` | +| 공정 작업기준 | v2-process-work-standard | `dataSource.itemTable`, `dataSource.routingDetailTable` | +| 품목 라우팅 | v2-item-routing | `dataSource.itemTable`, `dataSource.routingDetailTable` |