697 lines
31 KiB
Markdown
697 lines
31 KiB
Markdown
# POP 컴포넌트 정의서 v8.0
|
|
|
|
## POP 헌법 (공통 규칙)
|
|
|
|
### 제1조. 컴포넌트의 정의
|
|
|
|
- 컴포넌트란 디자이너가 그리드에 배치하는 것이다
|
|
- 그리드에 배치하지 않는 것은 컴포넌트가 아니다
|
|
|
|
### 제2조. 컴포넌트의 독립성
|
|
|
|
- 모든 컴포넌트는 독립적으로 동작한다
|
|
- 컴포넌트는 다른 컴포넌트의 존재를 직접 알지 못한다 (이벤트 버스로만 통신)
|
|
|
|
### 제3조. 데이터의 자유
|
|
|
|
- 모든 컴포넌트는 자신의 테이블 + 외부 테이블을 자유롭게 조인할 수 있다
|
|
- 컬럼별로 read/write/readwrite/hidden을 개별 설정할 수 있다
|
|
- 보유 데이터 중 원하는 컬럼만 골라서 저장할 수 있다
|
|
|
|
### 제4조. 통신의 규칙
|
|
|
|
- 컴포넌트 간 통신은 반드시 이벤트 버스(usePopEvent)를 통한다
|
|
- 컴포넌트가 다른 컴포넌트를 직접 참조하거나 호출하지 않는다
|
|
- 이벤트는 화면 단위로 격리된다 (다른 POP 화면의 이벤트를 받지 않는다)
|
|
- 같은 화면 안에서는 이벤트를 통해 자유롭게 데이터를 주고받을 수 있다
|
|
|
|
### 제5조. 역할의 분리
|
|
|
|
- 조회용 입력(pop-search)과 저장용 입력(pop-field)은 다른 컴포넌트다
|
|
- 이동/실행(pop-icon)과 값 선택 후 반환(pop-lookup)은 다른 컴포넌트다
|
|
- 자주 쓰는 패턴은 하나로 합치되, 흐름 자체는 강제하고 보이는 방식만 옵션으로 제공한다
|
|
|
|
### 제6조. 시스템 설정도 컴포넌트다
|
|
|
|
- 프로필, 테마, 대시보드 보이기/숨기기 같은 시스템 설정도 컴포넌트(pop-system)로 만든다
|
|
- 디자이너가 pop-system을 배치하지 않으면 해당 화면에 설정 기능이 없다
|
|
- 이를 통해 디자이너가 "이 화면에 설정 기능을 넣을지 말지"를 직접 결정한다
|
|
|
|
### 제7조. 디자이너의 권한
|
|
|
|
- 디자이너는 컴포넌트를 배치하고 설정한다
|
|
- 디자이너는 사용자에게 커스텀을 허용할지 말지 결정한다 (userConfigurable)
|
|
- 디자이너가 "사용자 커스텀 허용 = OFF"로 설정하면, 사용자는 변경할 수 없다
|
|
- 컴포넌트의 옵션 설정(어떻게 저장하고 어떻게 조회하는지 등)은 디자이너가 결정한다
|
|
|
|
### 제8조. 컴포넌트의 구성
|
|
|
|
- 모든 컴포넌트는 3개 파일로 구성된다: 실제 컴포넌트, 디자인 미리보기, 설정 패널
|
|
- 모든 컴포넌트는 레지스트리에 등록해야 디자이너에 나타난다
|
|
- 모든 컴포넌트 인스턴스는 userConfigurable, displayName 공통 속성을 가진다
|
|
|
|
### 제9조. 모달 화면의 설계
|
|
|
|
- 모달은 인라인(컴포넌트 설정만으로 구성)과 외부 참조(별도 POP 화면 연결) 두 가지 방식이 있다
|
|
- 단순한 목록 선택은 인라인 모달을 사용한다 (설정만으로 완결)
|
|
- 복잡한 검색/필터가 필요하거나 여러 곳에서 재사용하는 모달은 별도 POP 화면을 만들어 참조한다
|
|
- 모달 안의 화면도 동일한 POP 컴포넌트 시스템으로 구성된다 (같은 그리드, 같은 컴포넌트)
|
|
- 모달 화면의 layout_data는 기존 screen_layouts_pop 테이블에 저장한다 (DB 변경 불필요)
|
|
|
|
---
|
|
|
|
## 현재 상태
|
|
|
|
- 그리드 시스템 (v5.2): 완성
|
|
- 컴포넌트 레지스트리: 완성 (PopComponentRegistry.ts)
|
|
- 구현 완료: `pop-text` 1개 (pop-text.tsx)
|
|
- 기존 `components-spec.md`는 v4 기준이라 갱신 필요
|
|
|
|
## 아키텍처 개요
|
|
|
|
```mermaid
|
|
graph TB
|
|
subgraph designer [디자이너]
|
|
Palette[컴포넌트 팔레트]
|
|
Grid[CSS Grid 캔버스]
|
|
ConfigPanel[속성 설정 패널]
|
|
end
|
|
|
|
subgraph registry [레지스트리]
|
|
Registry[PopComponentRegistry]
|
|
end
|
|
|
|
subgraph infra [공통 인프라]
|
|
DataSource[useDataSource 훅]
|
|
EventBus[usePopEvent 훅]
|
|
ActionRunner[usePopAction 훅]
|
|
end
|
|
|
|
subgraph components [9개 컴포넌트]
|
|
Text[pop-text - 완성]
|
|
Dashboard[pop-dashboard]
|
|
Table[pop-table]
|
|
Button[pop-button]
|
|
Icon[pop-icon]
|
|
Search[pop-search]
|
|
Field[pop-field]
|
|
Lookup[pop-lookup]
|
|
System[pop-system]
|
|
end
|
|
|
|
subgraph backend [기존 백엔드 API]
|
|
DataAPI[dataApi - 동적 CRUD]
|
|
DashAPI[dashboardApi - 통계 쿼리]
|
|
CodeAPI[commonCodeApi - 공통코드]
|
|
NumberAPI[numberingRuleApi - 채번]
|
|
end
|
|
|
|
Palette --> Grid
|
|
Grid --> ConfigPanel
|
|
ConfigPanel --> Registry
|
|
|
|
Registry --> components
|
|
components --> infra
|
|
infra --> backend
|
|
EventBus -.->|컴포넌트 간 통신| components
|
|
System -.->|보이기/숨기기 제어| components
|
|
```
|
|
|
|
---
|
|
|
|
## 공통 인프라 (모든 컴포넌트가 공유)
|
|
|
|
### 핵심 원칙: 모든 컴포넌트는 데이터를 자유롭게 다룬다
|
|
|
|
1. **데이터 전달**: 모든 컴포넌트는 자신이 보유한 데이터를 다른 컴포넌트에 전달/수신 가능
|
|
2. **테이블 조인**: 자신의 테이블 + 외부 테이블 자유롭게 조인하여 데이터 구성
|
|
3. **컬럼별 CRUD 제어**: 컬럼 단위로 "조회만" / "저장 대상" / "숨김"을 개별 설정 가능
|
|
4. **선택적 저장**: 보유 데이터 중 원하는 컬럼만 골라서 저장/수정/삭제 가능
|
|
|
|
### 공통 인스턴스 속성 (모든 컴포넌트 배치 시 설정 가능)
|
|
|
|
디자이너가 컴포넌트를 그리드에 배치할 때 설정하는 공통 속성:
|
|
|
|
- `userConfigurable`: boolean - 사용자가 이 컴포넌트를 숨길 수 있는지 (개인 설정 패널에 노출)
|
|
- `displayName`: string - 개인 설정 패널에 보여줄 이름 (예: "금일 생산실적")
|
|
|
|
### 1. DataSourceConfig (데이터 소스 설정 타입)
|
|
|
|
모든 데이터 연동 컴포넌트가 사용하는 표준 설정 구조:
|
|
|
|
- `tableName`: 대상 테이블
|
|
- `columns`: 컬럼 바인딩 목록 (ColumnBinding 배열)
|
|
- `filters`: 필터 조건 배열
|
|
- `sort`: 정렬 설정
|
|
- `aggregation`: 집계 함수 (count, sum, avg, min, max)
|
|
- `joins`: 테이블 조인 설정 (JoinConfig 배열)
|
|
- `refreshInterval`: 자동 새로고침 주기 (초)
|
|
- `limit`: 조회 건수 제한
|
|
|
|
### 1-1. ColumnBinding (컬럼별 읽기/쓰기 제어)
|
|
|
|
각 컬럼이 컴포넌트에서 어떤 역할을 하는지 개별 설정:
|
|
|
|
- `columnName`: 컬럼명
|
|
- `sourceTable`: 소속 테이블 (조인된 외부 테이블 포함)
|
|
- `mode`: "read" | "write" | "readwrite" | "hidden"
|
|
- read: 조회만 (화면에 표시하되 저장 안 함)
|
|
- write: 저장 대상 (사용자 입력 -> DB 저장)
|
|
- readwrite: 조회 + 저장 모두
|
|
- hidden: 내부 참조용 (화면에 안 보이지만 다른 컴포넌트에 전달 가능)
|
|
- `label`: 화면 표시 라벨
|
|
- `defaultValue`: 기본값
|
|
|
|
예시: 발주 품목 카드에서 5개 컬럼 중 3개만 저장
|
|
|
|
```
|
|
columns: [
|
|
{ columnName: "item_code", sourceTable: "order_items", mode: "read" },
|
|
{ columnName: "item_name", sourceTable: "item_info", mode: "read" },
|
|
{ columnName: "inbound_qty", sourceTable: "order_items", mode: "readwrite" },
|
|
{ columnName: "warehouse", sourceTable: "order_items", mode: "write" },
|
|
{ columnName: "memo", sourceTable: "order_items", mode: "write" },
|
|
]
|
|
```
|
|
|
|
### 1-2. JoinConfig (테이블 조인 설정)
|
|
|
|
외부 테이블과 자유롭게 조인:
|
|
|
|
- `targetTable`: 조인할 외부 테이블명
|
|
- `joinType`: "inner" | "left" | "right"
|
|
- `on`: 조인 조건 { sourceColumn, targetColumn }
|
|
- `columns`: 가져올 컬럼 목록
|
|
|
|
### 2. useDataSource 훅
|
|
|
|
DataSourceConfig를 받아서 기존 API를 호출하고 결과를 반환:
|
|
|
|
- 로딩/에러/데이터 상태 관리
|
|
- 자동 새로고침 타이머
|
|
- 필터 변경 시 자동 재조회
|
|
- 기존 `dataApi`, `dashboardApi` 활용
|
|
- **CRUD 함수 제공**: save(data), update(id, data), delete(id)
|
|
- ColumnBinding의 mode가 "write" 또는 "readwrite"인 컬럼만 저장 대상에 포함
|
|
- "read" 컬럼은 저장 시 자동 제외
|
|
|
|
### 3. usePopEvent 훅 (이벤트 버스 - 데이터 전달 포함)
|
|
|
|
컴포넌트 간 통신 (단순 이벤트 + 데이터 페이로드):
|
|
|
|
- `publish(eventName, payload)`: 이벤트 발행
|
|
- `subscribe(eventName, callback)`: 이벤트 구독
|
|
- `getSharedData(key)`: 공유 데이터 직접 읽기
|
|
- `setSharedData(key, value)`: 공유 데이터 직접 쓰기
|
|
- 화면 단위 스코프 (다른 POP 화면과 격리)
|
|
|
|
### 4. PopActionConfig (액션 설정 타입)
|
|
|
|
모든 컴포넌트가 사용할 수 있는 액션 표준 구조:
|
|
|
|
- `type`: "navigate" | "modal" | "save" | "delete" | "api" | "event" | "refresh"
|
|
- `navigate`: { screenId, url }
|
|
- `modal`: { mode, title, screenId, inlineConfig, modalSize }
|
|
- mode: "inline" (설정만으로 구성) | "screen-ref" (별도 화면 참조)
|
|
- title: 모달 제목
|
|
- screenId: mode가 "screen-ref"일 때 참조할 POP 화면 ID
|
|
- inlineConfig: mode가 "inline"일 때 사용할 DataSourceConfig + 표시 설정
|
|
- modalSize: { width, height } 모달 크기
|
|
- `save`: { targetColumns }
|
|
- `delete`: { confirmMessage }
|
|
- `api`: { method, endpoint, body }
|
|
- `event`: { eventName, payload }
|
|
- `refresh`: { targetComponents }
|
|
|
|
---
|
|
|
|
## 컴포넌트 정의 (9개)
|
|
|
|
### 1. pop-text (완성)
|
|
|
|
- **한 줄 정의**: 보여주기만 함
|
|
- **카테고리**: display
|
|
- **역할**: 정적 표시 전용 (이벤트 없음)
|
|
- **서브타입**: text, datetime, image, title
|
|
- **데이터**: 없음 (정적 콘텐츠)
|
|
- **이벤트**: 발행 없음, 수신 없음
|
|
- **설정**: 내용, 폰트 크기/굵기, 좌우/상하 정렬, 이미지 URL/맞춤/크기, 날짜 포맷 빌더
|
|
|
|
### 2. pop-dashboard (신규 - 2026-02-09 토의 결과 반영)
|
|
|
|
- **한 줄 정의**: 여러 집계 아이템을 묶어서 다양한 방식으로 보여줌
|
|
- **카테고리**: display
|
|
- **역할**: 숫자 데이터를 집계/계산하여 시각화. 하나의 컴포넌트 안에 여러 집계 아이템을 담는 컨테이너
|
|
- **구조**: 1개 pop-dashboard = 여러 DashboardItem의 묶음. 각 아이템은 독립적으로 데이터 소스/서브타입/보이기숨기기 설정 가능
|
|
- **서브타입** (아이템별로 선택, 한 묶음에 혼합 가능):
|
|
- kpi-card: 숫자 + 단위 + 라벨 + 증감 표시
|
|
- chart: 막대/원형/라인 차트
|
|
- gauge: 게이지 (목표 대비 달성률)
|
|
- stat-card: 통계 카드 (건수 + 대기 + 링크)
|
|
- **표시 모드** (디자이너가 선택):
|
|
- arrows: 좌우 버튼으로 아이템 넘기기
|
|
- auto-slide: 전광판처럼 자동 전환 (터치 시 멈춤, 일정 시간 후 재개)
|
|
- grid: 컴포넌트 영역 내부를 행/열로 쪼개서 여러 아이템 동시 표시 (디자이너가 각 아이템 위치 직접 지정)
|
|
- scroll: 좌우 또는 상하 스와이프
|
|
- **데이터**: 각 아이템별 독립 DataSourceConfig (조인/집계 자유)
|
|
- **계산식 지원**: "생산량/총재고량", "출고량/현재고량" 같은 복합 표현 가능
|
|
- 값 A, B를 각각 다른 테이블/집계로 설정
|
|
- 표시 형태: 분수(1,234/5,678), 퍼센트(21.7%), 비율(1,234:5,678)
|
|
- **CRUD**: 주로 읽기. 목표값 수정 등 필요 시 write 컬럼으로 저장 가능
|
|
- **이벤트**:
|
|
- 수신: filter_changed, data_ready
|
|
- 발행: kpi_clicked (아이템 클릭 시 상세 데이터 전달)
|
|
- **설정**: 데이터 소스(드롭다운 기반 쉬운 집계), 집계 함수, 계산식, 라벨, 단위, 색상 구간, 차트 타입, 새로고침 주기, 목표값, 표시 모드, 아이템별 보이기/숨기기
|
|
- **보이기/숨기기**: 각 아이템별로 pop-system에서 개별 on/off 가능 (userConfigurable)
|
|
- **기존 POP 대시보드 폐기**: `frontend/components/pop/dashboard/` 폴더 전체를 이 컴포넌트로 대체 예정 (Phase 1~3 완료 후)
|
|
|
|
#### pop-dashboard 데이터 구조
|
|
|
|
```
|
|
PopDashboardConfig {
|
|
items: DashboardItem[] // 아이템 목록 (각각 독립 설정)
|
|
displayMode: "arrows" | "auto-slide" | "grid" | "scroll"
|
|
autoSlideInterval: number // 자동 슬라이드 간격(초)
|
|
gridLayout: { columns: number, rows: number } // 행열 그리드 설정
|
|
showIndicator: boolean // 페이지 인디케이터 표시
|
|
gap: number // 아이템 간 간격
|
|
}
|
|
|
|
DashboardItem {
|
|
id: string
|
|
label: string // pop-system에서 보이기/숨기기용 이름
|
|
visible: boolean // 보이기/숨기기
|
|
subType: "kpi-card" | "chart" | "gauge" | "stat-card"
|
|
dataSource: DataSourceConfig // 각 아이템별 독립 데이터 소스
|
|
|
|
// 행열 그리드 모드에서의 위치 (디자이너가 직접 지정)
|
|
gridPosition: { col: number, row: number, colSpan: number, rowSpan: number }
|
|
|
|
// 계산식 (선택사항)
|
|
formula?: {
|
|
enabled: boolean
|
|
values: [
|
|
{ id: "A", dataSource: DataSourceConfig, label: "생산량" },
|
|
{ id: "B", dataSource: DataSourceConfig, label: "총재고량" },
|
|
]
|
|
expression: string // "A / B", "A + B", "A / B * 100"
|
|
displayFormat: "value" | "fraction" | "percent" | "ratio"
|
|
}
|
|
|
|
// 서브타입별 설정
|
|
kpiConfig?: { unit, colorRanges, showTrend, trendPeriod }
|
|
chartConfig?: { chartType, xAxis, yAxis, colors }
|
|
gaugeConfig?: { min, max, target, colorRanges }
|
|
statConfig?: { categories, showLink }
|
|
}
|
|
```
|
|
|
|
#### 설정 패널 흐름 (드롭다운 기반 쉬운 집계)
|
|
|
|
```
|
|
1. [+ 아이템 추가] 버튼 클릭
|
|
2. 서브타입 선택: kpi-card / chart / gauge / stat-card
|
|
3. 데이터 모드 선택: [단일 집계] 또는 [계산식]
|
|
|
|
[단일 집계]
|
|
- 테이블 선택 (table-schema API로 목록)
|
|
- 조인할 테이블 추가 (선택사항)
|
|
- 컬럼 선택 → 집계 함수 선택 (합계/건수/평균/최소/최대)
|
|
- 필터 조건 추가
|
|
|
|
[계산식] (예: 생산량/총재고량)
|
|
- 값 A: 테이블 -> 컬럼 -> 집계함수
|
|
- 값 B: 테이블 -> 컬럼 -> 집계함수 (다른 테이블도 가능)
|
|
- 계산식: A / B
|
|
- 표시 형태: 분수 / 퍼센트 / 비율
|
|
|
|
4. 라벨, 단위, 색상 등 외형 설정
|
|
5. 행열 그리드 위치 설정 (grid 모드일 때)
|
|
```
|
|
|
|
### 3. pop-table (신규 - 가장 복잡)
|
|
|
|
- **한 줄 정의**: 데이터 목록을 보여주고 편집함
|
|
- **카테고리**: display
|
|
- **역할**: 데이터 목록 표시 + 편집 (카드형/테이블형)
|
|
- **서브타입**:
|
|
- card-list: 카드 형태
|
|
- table-list: 테이블 형태 (행/열 장부)
|
|
- **데이터**: DataSourceConfig (조인/컬럼별 읽기쓰기 자유)
|
|
- **CRUD**: useDataSource의 save/update/delete 사용. write/readwrite 컬럼만 자동 추출
|
|
- **카드 템플릿** (card-list 전용): 카드 내부 미니 그리드로 요소 배치, 요소별 데이터 바인딩
|
|
- **이벤트**:
|
|
- 수신: filter_changed, refresh, data_ready
|
|
- 발행: row_selected, row_action, save_complete, delete_complete
|
|
- **설정**: 데이터 소스, 표시 모드, 카드 템플릿, 컬럼 정의, 행 선택 방식, 페이징, 정렬, 인라인 편집 여부
|
|
|
|
### 4. pop-button (신규)
|
|
|
|
- **한 줄 정의**: 누르면 액션 실행 (저장, 삭제 등)
|
|
- **카테고리**: action
|
|
- **역할**: 액션 실행 (저장, 삭제, API 호출, 모달 열기 등)
|
|
- **데이터**: 이벤트로 수신한 데이터를 액션에 활용
|
|
- **CRUD**: 버튼 클릭 시 수신 데이터 기반으로 save/update/delete 실행
|
|
- **이벤트**:
|
|
- 수신: data_ready, row_selected
|
|
- 발행: save_complete, delete_complete 등
|
|
- **설정**: 라벨, 아이콘, 크기, 스타일, 액션 설정(PopActionConfig), 확인 다이얼로그, 로딩 상태
|
|
|
|
### 5. pop-icon (신규)
|
|
|
|
- **한 줄 정의**: 누르면 어딘가로 이동 (돌아오는 값 없음)
|
|
- **카테고리**: action
|
|
- **역할**: 네비게이션 (화면 이동, URL 이동)
|
|
- **데이터**: 없음
|
|
- **이벤트**: 없음 (네비게이션은 이벤트가 아닌 직접 실행)
|
|
- **설정**: 아이콘 종류(lucide-icon), 라벨, 배경색/그라디언트, 크기, 클릭 액션(PopActionConfig), 뱃지 표시
|
|
- **pop-lookup과의 차이**: pop-icon은 이동/실행만 함. 값을 선택해서 돌려주지 않음
|
|
|
|
### 6. pop-search (신규)
|
|
|
|
- **한 줄 정의**: 조건을 입력해서 다른 컴포넌트를 조회/필터링
|
|
- **카테고리**: input
|
|
- **역할**: 다른 컴포넌트에 필터 조건 전달 + 자체 데이터 조회
|
|
- **서브타입**:
|
|
- text-search: 텍스트 검색
|
|
- date-range: 날짜 범위
|
|
- select-filter: 드롭다운 선택 (공통코드 연동)
|
|
- combo-filter: 복합 필터 (여러 조건 조합)
|
|
- **실행 방식**: auto(값 변경 즉시) 또는 button(검색 버튼 클릭 시)
|
|
- **데이터**: 공통코드/카테고리 API로 선택 항목 조회
|
|
- **이벤트**:
|
|
- 수신: 없음
|
|
- 발행: filter_changed (필터 값 변경 시)
|
|
- **설정**: 필터 타입, 대상 컬럼, 공통코드 연결, 플레이스홀더, 실행 방식(auto/button), 발행할 이벤트 이름
|
|
- **pop-field와의 차이**: pop-search 입력값은 조회용(DB에 안 들어감). pop-field 입력값은 저장용(DB에 들어감)
|
|
|
|
### 7. pop-field (신규)
|
|
|
|
- **한 줄 정의**: 저장할 값을 입력
|
|
- **카테고리**: input
|
|
- **역할**: 단일 데이터 입력 (폼 필드) - 입력한 값이 DB에 저장되는 것이 목적
|
|
- **서브타입**:
|
|
- text: 텍스트 입력
|
|
- number: 숫자 입력 (수량, 금액)
|
|
- date: 날짜 선택
|
|
- select: 드롭다운 선택
|
|
- numpad: 큰 숫자패드 (현장용)
|
|
- **데이터**: DataSourceConfig (선택적)
|
|
- select 옵션을 DB에서 조회 가능
|
|
- ColumnBinding으로 입력값의 저장 대상 테이블/컬럼 지정
|
|
- **CRUD**: 자체 저장은 보통 하지 않음. value_changed 이벤트로 pop-button 등에 전달
|
|
- **이벤트**:
|
|
- 수신: set_value (외부에서 값 설정)
|
|
- 발행: value_changed (값 + 컬럼명 + 모드 정보)
|
|
- **설정**: 입력 타입, 라벨, 플레이스홀더, 필수 여부, 유효성 검증, 최소/최대값, 단위 표시, 바인딩 컬럼
|
|
|
|
### 8. pop-lookup (신규)
|
|
|
|
- **한 줄 정의**: 모달에서 값을 골라서 반환
|
|
- **카테고리**: input
|
|
- **역할**: 필드를 클릭하면 모달이 열리고, 목록에서 선택하면 값이 반환되는 컴포넌트
|
|
- **서브타입 (모달 안 표시 방식)**:
|
|
- card: 카드형 목록
|
|
- table: 테이블형 목록
|
|
- icon-grid: 아이콘 그리드 (참조 화면의 거래처 선택처럼)
|
|
- **동작 흐름**: 필드 클릭 -> 모달 열림 -> 목록에서 선택 -> 모달 닫힘 -> 필드에 값 표시 + 이벤트 발행
|
|
- **데이터**: DataSourceConfig (모달 안 목록의 데이터 소스)
|
|
- **이벤트**:
|
|
- 수신: set_value (외부에서 값 초기화)
|
|
- 발행: value_selected (선택한 레코드 전체 데이터 전달), filter_changed (선택 값을 필터로 전달)
|
|
- **설정**: 라벨, 플레이스홀더, 데이터 소스, 모달 표시 방식(card/table/icon-grid), 표시 컬럼(모달 목록에 보여줄 컬럼), 반환 컬럼(선택 시 돌려줄 값), 발행할 이벤트 이름
|
|
- **pop-icon과의 차이**: pop-icon은 이동/실행만 하고 값이 안 돌아옴. pop-lookup은 값을 골라서 돌려줌
|
|
- **pop-search와의 차이**: pop-search는 텍스트/날짜/드롭다운으로 필터링. pop-lookup은 모달을 열어서 목록에서 선택
|
|
|
|
#### pop-lookup 모달 화면 설계 방식
|
|
|
|
pop-lookup이 열리는 모달의 내부 화면은 **두 가지 방식** 중 선택할 수 있다:
|
|
|
|
**방식 A: 인라인 모달 (기본)**
|
|
- pop-lookup 컴포넌트의 설정 패널에서 직접 모달 내부 화면을 구성
|
|
- DataSourceConfig + 표시 컬럼 + 검색 필터 설정만으로 동작
|
|
- 별도 화면 생성 없이 컴포넌트 설정만으로 완결
|
|
- 적합한 경우: 단순 목록 선택 (거래처 목록, 품목 목록 등)
|
|
|
|
**방식 B: 외부 화면 참조 (고급)**
|
|
- 별도의 POP 화면(screen_id)을 모달로 연결
|
|
- 모달 안에서 검색/필터/테이블 등 복잡한 화면을 디자이너로 자유롭게 구성
|
|
- 여러 pop-lookup에서 같은 모달 화면을 재사용 가능
|
|
- 적합한 경우: 복잡한 검색/필터가 필요한 선택 화면, 여러 화면에서 공유하는 모달
|
|
|
|
**설정 구조:**
|
|
|
|
```
|
|
modalConfig: {
|
|
mode: "inline" | "screen-ref"
|
|
|
|
// mode = "inline"일 때 사용
|
|
dataSource: DataSourceConfig
|
|
displayColumns: ColumnBinding[]
|
|
searchFilter: { enabled: boolean, targetColumns: string[] }
|
|
modalSize: { width: number, height: number }
|
|
|
|
// mode = "screen-ref"일 때 사용
|
|
screenId: number // 참조할 POP 화면 ID
|
|
returnMapping: { // 모달 화면에서 선택된 값을 어떻게 매핑할지
|
|
sourceColumn: string // 모달 화면에서 반환하는 컬럼
|
|
targetField: string // pop-lookup 필드에 표시할 값
|
|
}[]
|
|
modalSize: { width: number, height: number }
|
|
}
|
|
```
|
|
|
|
**기존 시스템과의 호환성 (검증 완료):**
|
|
|
|
| 항목 | 현재 상태 | pop-lookup 지원 여부 |
|
|
|------|-----------|---------------------|
|
|
| DB: layout_data JSONB | 유연한 JSON 구조 | modalConfig를 layout_data에 저장 가능 (스키마 변경 불필요) |
|
|
| DB: screen_layouts_pop 테이블 | screen_id + company_code 기반 | 모달 화면도 별도 screen_id로 저장 가능 |
|
|
| 프론트: TabsWidget | screenId로 외부 화면 참조 지원 | 같은 패턴으로 모달에서 외부 화면 로드 가능 |
|
|
| 프론트: detectLinkedModals API | 연결된 모달 화면 감지 기능 있음 | 화면 간 참조 관계 추적에 활용 가능 |
|
|
| 백엔드: saveLayoutPop/getLayoutPop | POP 전용 저장/조회 API 있음 | 모달 화면도 동일 API로 저장/조회 가능 |
|
|
| 레이어 시스템 | layer_id 기반 다중 레이어 지원 | 모달 내부 레이아웃을 레이어로 관리 가능 |
|
|
|
|
**DB 마이그레이션 불필요**: layout_data가 JSONB이므로 modalConfig를 컴포넌트 overrides에 포함하면 됨.
|
|
**백엔드 변경 불필요**: 기존 saveLayoutPop/getLayoutPop API가 그대로 사용 가능.
|
|
**프론트엔드 참고 패턴**: TabsWidget의 screenId 참조 방식을 그대로 차용.
|
|
|
|
### 9. pop-system (신규)
|
|
|
|
- **한 줄 정의**: 시스템 설정을 하나로 통합한 컴포넌트 (프로필, 테마, 보이기/숨기기)
|
|
- **카테고리**: system
|
|
- **역할**: 사용자 개인 설정 기능을 제공하는 통합 컴포넌트
|
|
- **내부 포함 기능**:
|
|
- 프로필 표시 (사용자명, 부서)
|
|
- 테마 선택 (기본/다크/블루/그린)
|
|
- 대시보드 보이기/숨기기 체크박스 (같은 화면의 userConfigurable=true 컴포넌트를 자동 수집)
|
|
- 하단 메뉴 보이기/숨기기
|
|
- 드래그앤드롭으로 순서 변경
|
|
- **디자이너가 설정하는 것**: 크기(그리드에서 차지하는 영역), 내부 라벨/아이콘 크기와 위치
|
|
- **사용자가 하는 것**: 체크박스로 컴포넌트 보이기/숨기기, 테마 선택, 순서 변경
|
|
- **데이터**: 같은 화면의 layout_data에서 컴포넌트 목록을 자동 수집
|
|
- **저장**: 사용자별 설정을 localStorage에 저장 (데스크탑 패턴 따름)
|
|
- **이벤트**:
|
|
- 수신: 없음
|
|
- 발행: visibility_changed (컴포넌트 보이기/숨기기 변경 시), theme_changed (테마 변경 시)
|
|
- **설정**: 내부 라벨 크기, 아이콘 크기, 위치 정도만
|
|
- **특이사항**:
|
|
- 디자이너가 이 컴포넌트를 배치하지 않으면 해당 화면에 개인 설정 기능이 없다
|
|
- 디자이너가 "이 화면에 설정 기능을 넣을지 말지"를 직접 결정하는 구조
|
|
- 메인 홈에는 배치, 업무 화면(입고 등)에는 안 배치하는 식으로 사용
|
|
|
|
---
|
|
|
|
## 컴포넌트 간 통신 예시
|
|
|
|
### 예시 1: 검색 -> 필터 연동
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Search as pop-search
|
|
participant Dashboard as pop-dashboard
|
|
participant Table as pop-table
|
|
|
|
Note over Search: 사용자가 창고 WH01 선택
|
|
Search->>Dashboard: filter_changed
|
|
Search->>Table: filter_changed
|
|
Note over Dashboard: DataSource 재조회
|
|
Note over Table: DataSource 재조회
|
|
```
|
|
|
|
### 예시 2: 데이터 전달 + 선택적 저장
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Table as pop-table
|
|
participant Field as pop-field
|
|
participant Button as pop-button
|
|
|
|
Note over Table: 사용자가 발주 행 선택
|
|
Table->>Field: row_selected
|
|
Table->>Button: row_selected
|
|
Note over Field: 사용자가 qty를 500으로 입력
|
|
Field->>Button: value_changed
|
|
Note over Button: 사용자가 저장 클릭
|
|
Note over Button: write/readwrite 컬럼만 추출하여 저장
|
|
Button->>Table: save_complete
|
|
Note over Table: 데이터 새로고침
|
|
```
|
|
|
|
### 예시 3: pop-lookup 거래처 선택 -> 품목 조회
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Lookup as pop-lookup
|
|
participant Table as pop-table
|
|
|
|
Note over Lookup: 사용자가 거래처 필드 클릭
|
|
Note over Lookup: 모달 열림 - 거래처 목록 표시
|
|
Note over Lookup: 사용자가 대한금속 선택
|
|
Note over Lookup: 모달 닫힘 - 필드에 대한금속 표시
|
|
Lookup->>Table: filter_changed { company: "대한금속" }
|
|
Note over Table: company=대한금속 필터로 재조회
|
|
Note over Table: 발주 품목 3건 표시
|
|
```
|
|
|
|
### 예시 4: pop-lookup 인라인 모달 vs 외부 화면 참조
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User as 사용자
|
|
participant Lookup as pop-lookup (거래처)
|
|
participant Modal as 모달
|
|
|
|
Note over User,Modal: [방식 A: 인라인 모달]
|
|
User->>Lookup: 거래처 필드 클릭
|
|
Lookup->>Modal: 인라인 모달 열림 (DataSourceConfig 기반)
|
|
Note over Modal: supplier 테이블에서 목록 조회
|
|
Note over Modal: 테이블형 목록 표시
|
|
User->>Modal: "대한금속" 선택
|
|
Modal->>Lookup: value_selected { supplier_code: "DH001", name: "대한금속" }
|
|
Note over Lookup: 필드에 "대한금속" 표시
|
|
|
|
Note over User,Modal: [방식 B: 외부 화면 참조]
|
|
User->>Lookup: 거래처 필드 클릭
|
|
Lookup->>Modal: 모달 열림 (screenId=42 화면 로드)
|
|
Note over Modal: 별도 POP 화면 렌더링
|
|
Note over Modal: pop-search(검색) + pop-table(목록) 등 배치된 컴포넌트 동작
|
|
User->>Modal: 검색 후 "대한금속" 선택
|
|
Modal->>Lookup: returnMapping 기반으로 값 반환
|
|
Note over Lookup: 필드에 "대한금속" 표시
|
|
```
|
|
|
|
### 예시 5: 컬럼별 읽기/쓰기 분리 동작
|
|
|
|
5개 컬럼이 있는 발주 화면:
|
|
|
|
- item_code (read) -> 화면에 표시, 저장 안 함
|
|
- item_name (read, 조인) -> item_info 테이블에서 가져옴, 저장 안 함
|
|
- inbound_qty (readwrite) -> 화면에 표시 + 사용자 수정 + 저장
|
|
- warehouse (write) -> 사용자 입력 + 저장
|
|
- memo (write) -> 사용자 입력 + 저장
|
|
|
|
저장 API 호출 시: `{ inbound_qty: 500, warehouse: "WH01", memo: "긴급" }` 만 전달
|
|
조회 API 호출 시: 5개 컬럼 전부 + 조인된 item_name까지 조회
|
|
|
|
---
|
|
|
|
## 구현 우선순위
|
|
|
|
- Phase 0 (공통 인프라): ColumnBinding, JoinConfig, DataSourceConfig 타입, useDataSource 훅 (CRUD 포함), usePopEvent 훅 (데이터 전달 포함), PopActionConfig 타입
|
|
- Phase 1 (기본 표시): pop-dashboard (4개 서브타입 전부 + 멀티 아이템 컨테이너 + 4개 표시 모드 + 계산식)
|
|
- Phase 2 (기본 액션): pop-button, pop-icon
|
|
- Phase 3 (데이터 목록): pop-table (테이블형부터, 카드형은 후순위)
|
|
- Phase 4 (입력/연동): pop-search, pop-field, pop-lookup
|
|
- Phase 5 (고도화): pop-table 카드 템플릿
|
|
- Phase 6 (시스템): pop-system (프로필, 테마, 대시보드 보이기/숨기기 통합)
|
|
|
|
### Phase 1 상세 변경 (2026-02-09 토의 결정)
|
|
|
|
기존 계획에서 "KPI 카드 우선"이었으나, 토의 결과 **4개 서브타입 전부를 Phase 1에서 구현**으로 변경:
|
|
- kpi-card, chart, gauge, stat-card 모두 Phase 1
|
|
- 멀티 아이템 컨테이너 (arrows, auto-slide, grid, scroll)
|
|
- 계산식 지원 (formula)
|
|
- 드롭다운 기반 쉬운 집계 설정
|
|
- 기존 `frontend/components/pop/dashboard/` 폴더는 Phase 1 완료 후 폐기/삭제
|
|
|
|
### 백엔드 API 현황 (호환성 점검 완료)
|
|
|
|
기존 백엔드에 이미 구현되어 있어 새로 만들 필요 없는 API:
|
|
|
|
| API | 용도 | 비고 |
|
|
|-----|------|------|
|
|
| `dataApi.getTableData()` | 동적 테이블 조회 | 페이징, 검색, 정렬, 필터 |
|
|
| `dataApi.getJoinedData()` | 2개 테이블 조인 | Entity 조인, 필터링, 중복제거 |
|
|
| `entityJoinApi.getTableDataWithJoins()` | Entity 조인 전용 | ID->이름 자동 변환 |
|
|
| `dataApi.createRecord/updateRecord/deleteRecord()` | 동적 CRUD | - |
|
|
| `dataApi.upsertGroupedRecords()` | 그룹 UPSERT | - |
|
|
| `dashboardApi.executeQuery()` | SELECT SQL 직접 실행 | 집계/복합조인용 |
|
|
| `dashboardApi.getTableSchema()` | 테이블/컬럼 목록 | 설정 패널 드롭다운용 |
|
|
|
|
**백엔드 신규 개발 불필요** - 기존 API만으로 모든 데이터 연동 가능
|
|
|
|
### useDataSource의 API 선택 전략
|
|
|
|
```
|
|
단순 조회 (조인/집계 없음) -> dataApi.getTableData() 또는 entityJoinApi
|
|
2개 테이블 조인 -> dataApi.getJoinedData()
|
|
3개+ 테이블 조인 또는 집계 -> DataSourceConfig를 SQL로 변환 -> dashboardApi.executeQuery()
|
|
CRUD -> dataApi.createRecord/updateRecord/deleteRecord()
|
|
```
|
|
|
|
### POP 전용 훅 분리 (2026-02-09 결정)
|
|
|
|
데스크탑과의 완전 분리를 위해 POP 전용 훅은 별도 폴더:
|
|
- `frontend/hooks/pop/usePopEvent.ts` (POP 전용)
|
|
- `frontend/hooks/pop/useDataSource.ts` (POP 전용)
|
|
|
|
## 기존 시스템 호환성 검증 결과 (v8.0 추가)
|
|
|
|
v8.0에서 추가된 모달 설계 방식에 대해 기존 시스템과의 호환성을 검증한 결과:
|
|
|
|
### DB 스키마 (변경 불필요)
|
|
|
|
| 테이블 | 현재 구조 | 호환성 |
|
|
|--------|-----------|--------|
|
|
| screen_layouts_v2 | layout_data JSONB + screen_id + company_code + layer_id | modalConfig를 컴포넌트 overrides에 포함하면 됨 |
|
|
| screen_layouts_pop | 동일 구조 (POP 전용) | 모달 화면도 별도 screen_id로 저장 가능 |
|
|
|
|
- layout_data가 JSONB 타입이므로 어떤 JSON 구조든 저장 가능
|
|
- 모달 화면을 별도 screen_id로 만들어도 기존 UNIQUE(screen_id, company_code, layer_id) 제약조건과 충돌 없음
|
|
- DB 마이그레이션 불필요
|
|
|
|
### 백엔드 API (변경 불필요)
|
|
|
|
| API | 엔드포인트 | 호환성 |
|
|
|-----|-----------|--------|
|
|
| POP 레이아웃 저장 | POST /api/screen-management/screens/:screenId/layout-pop | 모달 화면도 동일 API로 저장 |
|
|
| POP 레이아웃 조회 | GET /api/screen-management/screens/:screenId/layout-pop | 모달 화면도 동일 API로 조회 |
|
|
| 연결 모달 감지 | detectLinkedModals(screenId) | 화면 간 참조 관계 추적에 활용 |
|
|
|
|
### 프론트엔드 (참고 패턴 존재)
|
|
|
|
| 기존 기능 | 위치 | 활용 방안 |
|
|
|-----------|------|-----------|
|
|
| TabsWidget screenId 참조 | frontend/components/screen/widgets/TabsWidget.tsx | 같은 패턴으로 모달에서 외부 화면 로드 |
|
|
| TabsConfigPanel | frontend/components/screen/config-panels/TabsConfigPanel.tsx | pop-lookup 설정 패널의 모달 화면 선택 UI 참조 |
|
|
| ScreenDesigner 탭 내부 컴포넌트 | frontend/components/screen/ScreenDesigner.tsx | 모달 내부 컴포넌트 편집 패턴 참조 |
|
|
|
|
### 결론
|
|
|
|
- DB 마이그레이션: 불필요
|
|
- 백엔드 변경: 불필요
|
|
- 프론트엔드: pop-lookup 컴포넌트 구현 시 기존 TabsWidget의 screenId 참조 패턴을 그대로 차용
|
|
- 새로운 API: 불필요 (기존 saveLayoutPop/getLayoutPop로 충분)
|
|
|
|
## 참고 파일
|
|
|
|
- 레지스트리: `frontend/lib/registry/PopComponentRegistry.ts`
|
|
- 기존 텍스트 컴포넌트: `frontend/lib/registry/pop-components/pop-text.tsx`
|
|
- 공통 스타일 타입: `frontend/lib/registry/pop-components/types.ts`
|
|
- POP 타입 정의: `frontend/components/pop/designer/types/pop-layout.ts`
|
|
- 기존 스펙 (v4): `popdocs/components-spec.md`
|
|
- 탭 위젯 (모달 참조 패턴): `frontend/components/screen/widgets/TabsWidget.tsx`
|
|
- POP 레이아웃 API: `frontend/lib/api/screen.ts` (saveLayoutPop, getLayoutPop)
|
|
- 백엔드 화면관리: `backend-node/src/controllers/screenManagementController.ts`
|