# 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`
