feat(pop): 뷰어 스크롤 수정 및 컴포넌트 레지스트리 연동
- page.tsx: overflow-hidden 제거로 뷰어 스크롤 활성화, pop-components 레지스트리 자동 등록 import 추가 - PopRenderer.tsx: 레지스트리에서 실제 컴포넌트 조회 후 렌더링, 미등록 컴포넌트는 플레이스홀더 fallback 표시 - PLAN.MD: POP 뷰어 스크롤 수정 계획으로 업데이트 - POPUPDATE_2.md: v8.0 - 모달 화면 설계 규칙(제9조) 추가, 버튼 modal action 스펙 확장 (inline/screen-ref 모드) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a017c1352a
commit
f825d65bfc
259
PLAN.MD
259
PLAN.MD
|
|
@ -1,139 +1,156 @@
|
||||||
# 프로젝트: V2/V2 컴포넌트 설정 스키마 정비
|
# 현재 구현 계획: POP 뷰어 스크롤 수정
|
||||||
|
|
||||||
## 개요
|
> **작성일**: 2026-02-09
|
||||||
|
> **상태**: 계획 완료, 코딩 대기
|
||||||
레거시 컴포넌트를 제거하고, V2/V2 컴포넌트 전용 Zod 스키마와 기본값 레지스트리를 한 곳에서 관리한다.
|
> **목적**: 뷰어에서 화면 높이를 초과하는 컴포넌트가 잘리지 않고 스크롤 가능하도록 수정
|
||||||
|
|
||||||
## 핵심 기능
|
|
||||||
|
|
||||||
1. [x] 레거시 컴포넌트 스키마 제거
|
|
||||||
2. [x] V2 컴포넌트 overrides 스키마 정의 (16개)
|
|
||||||
3. [x] V2 컴포넌트 overrides 스키마 정의 (9개)
|
|
||||||
4. [x] componentConfig.ts 한 파일에서 통합 관리
|
|
||||||
|
|
||||||
## 정의된 V2 컴포넌트 (18개)
|
|
||||||
|
|
||||||
- v2-table-list, v2-button-primary, v2-text-display
|
|
||||||
- v2-split-panel-layout, v2-section-card, v2-section-paper
|
|
||||||
- v2-divider-line, v2-repeat-container, v2-rack-structure
|
|
||||||
- v2-numbering-rule, v2-category-manager, v2-pivot-grid
|
|
||||||
- v2-location-swap-selector, v2-aggregation-widget
|
|
||||||
- v2-card-display, v2-table-search-widget, v2-tabs-widget
|
|
||||||
- v2-v2-repeater
|
|
||||||
|
|
||||||
## 정의된 V2 컴포넌트 (9개)
|
|
||||||
|
|
||||||
- v2-input, v2-select, v2-date
|
|
||||||
- v2-list, v2-layout, v2-group
|
|
||||||
- v2-media, v2-biz, v2-hierarchy
|
|
||||||
|
|
||||||
## 테스트 계획
|
|
||||||
|
|
||||||
### 1단계: 기본 기능
|
|
||||||
|
|
||||||
- [x] V2 레이아웃 저장 시 컴포넌트별 overrides 스키마 검증 통과
|
|
||||||
- [x] V2 컴포넌트 기본값과 스키마가 매칭됨
|
|
||||||
|
|
||||||
### 2단계: 에러 케이스
|
|
||||||
|
|
||||||
- [x] 잘못된 overrides 입력 시 Zod 검증 실패 처리 (safeParse + console.warn + graceful fallback)
|
|
||||||
- [x] 누락된 기본값 컴포넌트 저장 시 안전한 기본값 적용 (레지스트리 조회 → 빈 객체)
|
|
||||||
|
|
||||||
## 에러 처리 계획
|
|
||||||
|
|
||||||
- 스키마 파싱 실패 시 로그/에러 메시지 표준화
|
|
||||||
- 기본값 누락 시 안전한 fallback 적용
|
|
||||||
|
|
||||||
## 진행 상태
|
|
||||||
|
|
||||||
- [x] 레거시 컴포넌트 제거 완료
|
|
||||||
- [x] V2/V2 스키마 정의 완료
|
|
||||||
- [x] 한 파일 통합 관리 완료
|
|
||||||
|
|
||||||
# 프로젝트: 화면 복제 기능 개선 (DB 구조 개편 후)
|
|
||||||
|
|
||||||
## 개요
|
|
||||||
|
|
||||||
채번/카테고리에서 `menu_objid` 의존성 제거 완료 후, 화면 복제 기능을 새 DB 구조에 맞게 수정하고 테스트합니다.
|
|
||||||
|
|
||||||
## 핵심 변경사항
|
|
||||||
|
|
||||||
### DB 구조 변경 (완료)
|
|
||||||
|
|
||||||
- 채번규칙: `menu_objid` 의존성 제거 → `table_name + column_name + company_code` 기반
|
|
||||||
- 카테고리: `menu_objid` 의존성 제거 → `table_name + column_name + company_code` 기반
|
|
||||||
- 복제 순서 의존성 문제 해결
|
|
||||||
|
|
||||||
### 복제 옵션 정리 (완료)
|
|
||||||
|
|
||||||
- [x] **삭제**: 코드 카테고리 + 코드 복사 옵션
|
|
||||||
- [x] **삭제**: 연쇄관계 설정 복사 옵션
|
|
||||||
- [x] **이름 변경**: "카테고리 매핑 + 값 복사" → "카테고리 값 복사"
|
|
||||||
|
|
||||||
### 현재 복제 옵션 (3개)
|
|
||||||
|
|
||||||
1. **채번 규칙 복사** - 채번규칙 복제
|
|
||||||
2. **카테고리 값 복사** - 카테고리 값 복제 (table_column_category_values)
|
|
||||||
3. **테이블 타입관리 입력타입 설정 복사** - table_type_columns 복제
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 테스트 계획
|
## 1. 문제 요약
|
||||||
|
|
||||||
### 1. 화면 간 연결 복제 테스트
|
설계(디자이너)에서 컴포넌트를 아래로 배치하면 캔버스가 늘어나고 스크롤이 되지만,
|
||||||
|
뷰어(`/pop/screens/4114`)에서는 화면 높이를 초과하는 컴포넌트가 잘려서 안 보임.
|
||||||
|
|
||||||
- [ ] 수주관리 1번→2번→3번→4번 화면 연결 상태에서 복제
|
**근본 원인**: CSS 컨테이너 구조가 스크롤을 차단
|
||||||
- [ ] 복제 후 연결 관계가 유지되는지 확인
|
|
||||||
- [ ] 각 화면의 고유 키값이 새로운 화면을 참조하도록 변경되는지 확인
|
|
||||||
|
|
||||||
### 2. 제어관리 복제 테스트
|
| # | 컨테이너 (라인) | 현재 클래스 | 문제 |
|
||||||
|
|---|----------------|-------------|------|
|
||||||
- [ ] 다른 회사로 제어관리 복제
|
| 1 | 최외곽 (185) | `h-screen ... overflow-hidden` | 넘치는 콘텐츠를 잘라냄 |
|
||||||
- [ ] 복제된 플로우 스텝/연결이 정상 작동하는지 확인
|
| 2 | 컨텐츠 영역 (266) | 일반 모드에 `overflow-auto` 없음 | 스크롤 불가 |
|
||||||
|
| 3 | 백색 배경 (275) | 일반 모드에 `min-h-full` 없음 | 짧은 콘텐츠 시 배경 불완전 |
|
||||||
### 3. 추가 옵션 복제 테스트
|
|
||||||
|
|
||||||
- [ ] 채번규칙 복사 정상 작동 확인
|
|
||||||
- [ ] 카테고리 값 복사 정상 작동 확인
|
|
||||||
- [ ] 테이블 타입관리 입력타입 설정 복사 정상 작동 확인
|
|
||||||
|
|
||||||
### 4. 기본 복제 테스트
|
|
||||||
|
|
||||||
- [ ] 단일 화면 복제 (모달 포함)
|
|
||||||
- [ ] 그룹 전체 복제 (재귀적)
|
|
||||||
- [ ] 메뉴 동기화 정상 작동
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 관련 파일
|
## 2. 수정 대상 파일 (1개)
|
||||||
|
|
||||||
- `frontend/components/screen/CopyScreenModal.tsx` - 복제 모달
|
### `frontend/app/(pop)/pop/screens/[screenId]/page.tsx`
|
||||||
- `frontend/components/screen/ScreenGroupTreeView.tsx` - 트리 뷰 + 컨텍스트 메뉴
|
|
||||||
- `backend-node/src/services/screenManagementService.ts` - 복제 서비스
|
|
||||||
- `backend-node/src/services/numberingRuleService.ts` - 채번규칙 서비스
|
|
||||||
- `docs/DB_STRUCTURE_DIAGRAM.md` - DB 구조 문서
|
|
||||||
|
|
||||||
## 진행 상태
|
**변경 유형**: CSS 클래스 문자열 수정 3곳 (새 변수/함수/타입 추가 없음)
|
||||||
|
|
||||||
|
#### 변경 1: 라인 185 - 최외곽 컨테이너
|
||||||
|
|
||||||
|
**현재 코드**:
|
||||||
|
```
|
||||||
|
<div className="h-screen bg-gray-100 flex flex-col overflow-hidden">
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 코드**:
|
||||||
|
```
|
||||||
|
<div className="h-screen bg-gray-100 flex flex-col">
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 내용**: `overflow-hidden` 제거
|
||||||
|
**이유**: 이 div는 프리뷰 툴바 + 컨텐츠의 flex 컨테이너 역할만 하면 됨. `overflow-hidden`이 자식의 스크롤까지 차단하므로 제거
|
||||||
|
|
||||||
|
#### 변경 2: 라인 266 - 컨텐츠 영역
|
||||||
|
|
||||||
|
**현재 코드**:
|
||||||
|
```
|
||||||
|
<div className={`flex-1 flex flex-col ${isPreviewMode ? "py-4 overflow-auto items-center" : ""}`}>
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 코드**:
|
||||||
|
```
|
||||||
|
<div className={`flex-1 flex flex-col overflow-auto ${isPreviewMode ? "py-4 items-center" : ""}`}>
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 내용**: `overflow-auto`를 조건문 밖으로 이동 (공통 적용)
|
||||||
|
**이유**: 프리뷰/일반 모드 모두 스크롤이 필요함
|
||||||
|
|
||||||
|
#### 변경 3: 라인 275 - 백색 배경 컨테이너
|
||||||
|
|
||||||
|
**현재 코드**:
|
||||||
|
```
|
||||||
|
className={`bg-white transition-all duration-300 ${isPreviewMode ? "shadow-2xl rounded-3xl overflow-auto border-8 border-gray-800" : "w-full"}`}
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 코드**:
|
||||||
|
```
|
||||||
|
className={`bg-white transition-all duration-300 ${isPreviewMode ? "shadow-2xl rounded-3xl overflow-auto border-8 border-gray-800" : "w-full min-h-full"}`}
|
||||||
|
```
|
||||||
|
|
||||||
|
**변경 내용**: 일반 모드에 `min-h-full` 추가
|
||||||
|
**이유**: 컴포넌트가 적어 콘텐츠가 짧을 때에도 흰색 배경이 화면 전체를 채우도록 보장
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 구현 순서 (의존성 기반)
|
||||||
|
|
||||||
|
| 순서 | 작업 | 라인 | 의존성 | 상태 |
|
||||||
|
|------|------|------|--------|------|
|
||||||
|
| 1 | 라인 185: `overflow-hidden` 제거 | 185 | 없음 | [x] 완료 |
|
||||||
|
| 2 | 라인 266: `overflow-auto` 공통 적용 | 266 | 순서 1 | [x] 완료 |
|
||||||
|
| 3 | 라인 275: 일반 모드 `min-h-full` 추가 | 275 | 순서 2 | [x] 완료 |
|
||||||
|
| 4 | 린트 검사 | - | 순서 1~3 | [x] 통과 |
|
||||||
|
| 5 | 브라우저 검증 | - | 순서 4 | [ ] 대기 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 사전 충돌 검사 결과
|
||||||
|
|
||||||
|
**새로 추가할 변수/함수/타입: 없음**
|
||||||
|
|
||||||
|
이번 수정은 기존 Tailwind CSS 클래스 문자열만 변경합니다.
|
||||||
|
새로운 식별자(변수, 함수, 타입)를 추가하지 않으므로 충돌 검사 대상이 없습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 에러 함정 경고
|
||||||
|
|
||||||
|
### 함정 1: 순서 1만 하고 순서 2를 빼먹으면
|
||||||
|
`overflow-hidden`만 제거하면 콘텐츠가 화면 밖으로 넘쳐 보이지만 스크롤은 안 됨.
|
||||||
|
부모는 열었지만 자식에 스크롤 속성이 없는 상태.
|
||||||
|
|
||||||
|
### 함정 2: 순서 2만 하고 순서 1을 빼먹으면
|
||||||
|
자식에 `overflow-auto`를 넣어도 부모가 `overflow-hidden`으로 잘라내므로 여전히 스크롤 안 됨.
|
||||||
|
**반드시 순서 1과 2를 함께 적용해야 함.**
|
||||||
|
|
||||||
|
### 함정 3: 프리뷰 모드 영향
|
||||||
|
프리뷰 모드는 이미 자체적으로 `overflow-auto`가 있으므로 이 수정에 영향 없음.
|
||||||
|
`overflow-auto`가 중복 적용되어도 CSS에서 문제 없음.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 검증 방법
|
||||||
|
|
||||||
|
1. `localhost:9771/pop/screens/4114` 접속 (iPhone SE 375px 기준)
|
||||||
|
2. 화면 아래로 스크롤 가능한지 확인
|
||||||
|
3. 맨 아래에 이미지(pop-text 5, 6)가 보이는지 확인
|
||||||
|
4. 프리뷰 모드(`?preview=true`)에서도 기존처럼 정상 동작하는지 확인
|
||||||
|
5. 컴포넌트가 적은 화면에서 흰색 배경이 화면 전체를 채우는지 확인
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 이전 완료 계획 (아카이브)
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>POP 뷰어 실제 컴포넌트 렌더링 (완료)</summary>
|
||||||
|
|
||||||
|
- [x] 뷰어 페이지에 레지스트리 초기화 import 추가
|
||||||
|
- [x] `renderActualComponent()` 실제 컴포넌트 렌더링으로 교체
|
||||||
|
- [x] 린트 검사 통과
|
||||||
|
- 브라우저 검증: 컴포넌트 표시 정상, 스크롤 문제 발견 -> 별도 수정
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>V2/V2 컴포넌트 설정 스키마 정비 (완료)</summary>
|
||||||
|
|
||||||
|
- [x] 레거시 컴포넌트 스키마 제거
|
||||||
|
- [x] V2 컴포넌트 overrides 스키마 정의 (16개)
|
||||||
|
- [x] V2 컴포넌트 overrides 스키마 정의 (9개)
|
||||||
|
- [x] componentConfig.ts 한 파일에서 통합 관리
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>화면 복제 기능 개선 (진행 중)</summary>
|
||||||
|
|
||||||
- [완료] DB 구조 개편 (menu_objid 의존성 제거)
|
- [완료] DB 구조 개편 (menu_objid 의존성 제거)
|
||||||
- [완료] 복제 옵션 정리 (코드카테고리/연쇄관계 삭제, 이름 변경)
|
- [완료] 복제 옵션 정리
|
||||||
- [완료] 화면 간 연결 복제 버그 수정 (targetScreenId 매핑 추가)
|
- [완료] 화면 간 연결 복제 버그 수정
|
||||||
- [대기] 화면 간 연결 복제 테스트
|
- [대기] 화면 간 연결 복제 테스트
|
||||||
- [대기] 제어관리 복제 테스트
|
- [대기] 제어관리 복제 테스트
|
||||||
- [대기] 추가 옵션 복제 테스트
|
- [대기] 추가 옵션 복제 테스트
|
||||||
|
|
||||||
---
|
</details>
|
||||||
|
|
||||||
## 수정 이력
|
|
||||||
|
|
||||||
### 2026-01-26: 버튼 targetScreenId 매핑 버그 수정
|
|
||||||
|
|
||||||
**문제**: 그룹 복제 시 버튼의 `targetScreenId`가 새 화면으로 매핑되지 않음
|
|
||||||
|
|
||||||
- 수주관리 1→2→3→4 화면 복제 시 연결이 깨지는 문제
|
|
||||||
|
|
||||||
**수정 파일**: `backend-node/src/services/screenManagementService.ts`
|
|
||||||
|
|
||||||
- `updateTabScreenReferences` 함수에 `targetScreenId` 처리 로직 추가
|
|
||||||
- 쿼리에 `targetScreenId` 검색 조건 추가
|
|
||||||
- 문자열/숫자 타입 모두 처리
|
|
||||||
|
|
|
||||||
273
POPUPDATE_2.md
273
POPUPDATE_2.md
|
|
@ -1,4 +1,4 @@
|
||||||
# POP 컴포넌트 정의서 v7.0
|
# POP 컴포넌트 정의서 v8.0
|
||||||
|
|
||||||
## POP 헌법 (공통 규칙)
|
## POP 헌법 (공통 규칙)
|
||||||
|
|
||||||
|
|
@ -50,6 +50,14 @@
|
||||||
- 모든 컴포넌트는 레지스트리에 등록해야 디자이너에 나타난다
|
- 모든 컴포넌트는 레지스트리에 등록해야 디자이너에 나타난다
|
||||||
- 모든 컴포넌트 인스턴스는 userConfigurable, displayName 공통 속성을 가진다
|
- 모든 컴포넌트 인스턴스는 userConfigurable, displayName 공통 속성을 가진다
|
||||||
|
|
||||||
|
### 제9조. 모달 화면의 설계
|
||||||
|
|
||||||
|
- 모달은 인라인(컴포넌트 설정만으로 구성)과 외부 참조(별도 POP 화면 연결) 두 가지 방식이 있다
|
||||||
|
- 단순한 목록 선택은 인라인 모달을 사용한다 (설정만으로 완결)
|
||||||
|
- 복잡한 검색/필터가 필요하거나 여러 곳에서 재사용하는 모달은 별도 POP 화면을 만들어 참조한다
|
||||||
|
- 모달 안의 화면도 동일한 POP 컴포넌트 시스템으로 구성된다 (같은 그리드, 같은 컴포넌트)
|
||||||
|
- 모달 화면의 layout_data는 기존 screen_layouts_pop 테이블에 저장한다 (DB 변경 불필요)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 현재 상태
|
## 현재 상태
|
||||||
|
|
@ -203,7 +211,12 @@ DataSourceConfig를 받아서 기존 API를 호출하고 결과를 반환:
|
||||||
|
|
||||||
- `type`: "navigate" | "modal" | "save" | "delete" | "api" | "event" | "refresh"
|
- `type`: "navigate" | "modal" | "save" | "delete" | "api" | "event" | "refresh"
|
||||||
- `navigate`: { screenId, url }
|
- `navigate`: { screenId, url }
|
||||||
- `modal`: { title, dataSource }
|
- `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 }
|
- `save`: { targetColumns }
|
||||||
- `delete`: { confirmMessage }
|
- `delete`: { confirmMessage }
|
||||||
- `api`: { method, endpoint, body }
|
- `api`: { method, endpoint, body }
|
||||||
|
|
@ -224,22 +237,97 @@ DataSourceConfig를 받아서 기존 API를 호출하고 결과를 반환:
|
||||||
- **이벤트**: 발행 없음, 수신 없음
|
- **이벤트**: 발행 없음, 수신 없음
|
||||||
- **설정**: 내용, 폰트 크기/굵기, 좌우/상하 정렬, 이미지 URL/맞춤/크기, 날짜 포맷 빌더
|
- **설정**: 내용, 폰트 크기/굵기, 좌우/상하 정렬, 이미지 URL/맞춤/크기, 날짜 포맷 빌더
|
||||||
|
|
||||||
### 2. pop-dashboard (신규)
|
### 2. pop-dashboard (신규 - 2026-02-09 토의 결과 반영)
|
||||||
|
|
||||||
- **한 줄 정의**: 숫자를 집계해서 보여줌
|
- **한 줄 정의**: 여러 집계 아이템을 묶어서 다양한 방식으로 보여줌
|
||||||
- **카테고리**: display
|
- **카테고리**: display
|
||||||
- **역할**: 숫자 데이터를 집계/계산하여 시각화
|
- **역할**: 숫자 데이터를 집계/계산하여 시각화. 하나의 컴포넌트 안에 여러 집계 아이템을 담는 컨테이너
|
||||||
- **서브타입**:
|
- **구조**: 1개 pop-dashboard = 여러 DashboardItem의 묶음. 각 아이템은 독립적으로 데이터 소스/서브타입/보이기숨기기 설정 가능
|
||||||
|
- **서브타입** (아이템별로 선택, 한 묶음에 혼합 가능):
|
||||||
- kpi-card: 숫자 + 단위 + 라벨 + 증감 표시
|
- kpi-card: 숫자 + 단위 + 라벨 + 증감 표시
|
||||||
- chart: 막대/원형/라인 차트
|
- chart: 막대/원형/라인 차트
|
||||||
- gauge: 게이지 (목표 대비 달성률)
|
- gauge: 게이지 (목표 대비 달성률)
|
||||||
- stat-card: 통계 카드 (건수 + 대기 + 링크)
|
- stat-card: 통계 카드 (건수 + 대기 + 링크)
|
||||||
- **데이터**: DataSourceConfig (조인/집계 자유)
|
- **표시 모드** (디자이너가 선택):
|
||||||
|
- arrows: 좌우 버튼으로 아이템 넘기기
|
||||||
|
- auto-slide: 전광판처럼 자동 전환 (터치 시 멈춤, 일정 시간 후 재개)
|
||||||
|
- grid: 컴포넌트 영역 내부를 행/열로 쪼개서 여러 아이템 동시 표시 (디자이너가 각 아이템 위치 직접 지정)
|
||||||
|
- scroll: 좌우 또는 상하 스와이프
|
||||||
|
- **데이터**: 각 아이템별 독립 DataSourceConfig (조인/집계 자유)
|
||||||
|
- **계산식 지원**: "생산량/총재고량", "출고량/현재고량" 같은 복합 표현 가능
|
||||||
|
- 값 A, B를 각각 다른 테이블/집계로 설정
|
||||||
|
- 표시 형태: 분수(1,234/5,678), 퍼센트(21.7%), 비율(1,234:5,678)
|
||||||
- **CRUD**: 주로 읽기. 목표값 수정 등 필요 시 write 컬럼으로 저장 가능
|
- **CRUD**: 주로 읽기. 목표값 수정 등 필요 시 write 컬럼으로 저장 가능
|
||||||
- **이벤트**:
|
- **이벤트**:
|
||||||
- 수신: filter_changed, data_ready
|
- 수신: filter_changed, data_ready
|
||||||
- 발행: kpi_clicked (KPI 카드 클릭 시 상세 데이터 전달)
|
- 발행: kpi_clicked (아이템 클릭 시 상세 데이터 전달)
|
||||||
- **설정**: 데이터 소스, 집계 함수, 라벨, 단위, 색상 구간, 차트 타입, 새로고침 주기, 목표값, 표시 모드(slide/scroll/grid)
|
- **설정**: 데이터 소스(드롭다운 기반 쉬운 집계), 집계 함수, 계산식, 라벨, 단위, 색상 구간, 차트 타입, 새로고침 주기, 목표값, 표시 모드, 아이템별 보이기/숨기기
|
||||||
|
- **보이기/숨기기**: 각 아이템별로 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 (신규 - 가장 복잡)
|
### 3. pop-table (신규 - 가장 복잡)
|
||||||
|
|
||||||
|
|
@ -335,6 +423,59 @@ DataSourceConfig를 받아서 기존 API를 호출하고 결과를 반환:
|
||||||
- **pop-icon과의 차이**: pop-icon은 이동/실행만 하고 값이 안 돌아옴. pop-lookup은 값을 골라서 돌려줌
|
- **pop-icon과의 차이**: pop-icon은 이동/실행만 하고 값이 안 돌아옴. pop-lookup은 값을 골라서 돌려줌
|
||||||
- **pop-search와의 차이**: pop-search는 텍스트/날짜/드롭다운으로 필터링. 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 (신규)
|
### 9. pop-system (신규)
|
||||||
|
|
||||||
- **한 줄 정의**: 시스템 설정을 하나로 통합한 컴포넌트 (프로필, 테마, 보이기/숨기기)
|
- **한 줄 정의**: 시스템 설정을 하나로 통합한 컴포넌트 (프로필, 테마, 보이기/숨기기)
|
||||||
|
|
@ -413,7 +554,34 @@ sequenceDiagram
|
||||||
Note over Table: 발주 품목 3건 표시
|
Note over Table: 발주 품목 3건 표시
|
||||||
```
|
```
|
||||||
|
|
||||||
### 예시 4: 컬럼별 읽기/쓰기 분리 동작
|
### 예시 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개 컬럼이 있는 발주 화면:
|
5개 컬럼이 있는 발주 화면:
|
||||||
|
|
||||||
|
|
@ -431,13 +599,91 @@ sequenceDiagram
|
||||||
## 구현 우선순위
|
## 구현 우선순위
|
||||||
|
|
||||||
- Phase 0 (공통 인프라): ColumnBinding, JoinConfig, DataSourceConfig 타입, useDataSource 훅 (CRUD 포함), usePopEvent 훅 (데이터 전달 포함), PopActionConfig 타입
|
- Phase 0 (공통 인프라): ColumnBinding, JoinConfig, DataSourceConfig 타입, useDataSource 훅 (CRUD 포함), usePopEvent 훅 (데이터 전달 포함), PopActionConfig 타입
|
||||||
- Phase 1 (기본 표시): pop-dashboard (KPI 카드 서브타입부터)
|
- Phase 1 (기본 표시): pop-dashboard (4개 서브타입 전부 + 멀티 아이템 컨테이너 + 4개 표시 모드 + 계산식)
|
||||||
- Phase 2 (기본 액션): pop-button, pop-icon
|
- Phase 2 (기본 액션): pop-button, pop-icon
|
||||||
- Phase 3 (데이터 목록): pop-table (테이블형부터, 카드형은 후순위)
|
- Phase 3 (데이터 목록): pop-table (테이블형부터, 카드형은 후순위)
|
||||||
- Phase 4 (입력/연동): pop-search, pop-field, pop-lookup
|
- Phase 4 (입력/연동): pop-search, pop-field, pop-lookup
|
||||||
- Phase 5 (고도화): pop-table 카드 템플릿, 차트, 게이지
|
- Phase 5 (고도화): pop-table 카드 템플릿
|
||||||
- Phase 6 (시스템): pop-system (프로필, 테마, 대시보드 보이기/숨기기 통합)
|
- 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/PopComponentRegistry.ts`
|
||||||
|
|
@ -445,3 +691,6 @@ sequenceDiagram
|
||||||
- 공통 스타일 타입: `frontend/lib/registry/pop-components/types.ts`
|
- 공통 스타일 타입: `frontend/lib/registry/pop-components/types.ts`
|
||||||
- POP 타입 정의: `frontend/components/pop/designer/types/pop-layout.ts`
|
- POP 타입 정의: `frontend/components/pop/designer/types/pop-layout.ts`
|
||||||
- 기존 스펙 (v4): `popdocs/components-spec.md`
|
- 기존 스펙 (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`
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ import {
|
||||||
GRID_BREAKPOINTS,
|
GRID_BREAKPOINTS,
|
||||||
detectGridMode,
|
detectGridMode,
|
||||||
} from "@/components/pop/designer/types/pop-layout";
|
} from "@/components/pop/designer/types/pop-layout";
|
||||||
|
// POP 컴포넌트 자동 등록 (레지스트리 초기화 - PopRenderer보다 먼저 import)
|
||||||
|
import "@/lib/registry/pop-components";
|
||||||
import PopRenderer from "@/components/pop/designer/renderers/PopRenderer";
|
import PopRenderer from "@/components/pop/designer/renderers/PopRenderer";
|
||||||
import {
|
import {
|
||||||
useResponsiveModeWithOverride,
|
useResponsiveModeWithOverride,
|
||||||
|
|
@ -180,7 +182,7 @@ function PopScreenViewPage() {
|
||||||
<ScreenPreviewProvider isPreviewMode={isPreviewMode}>
|
<ScreenPreviewProvider isPreviewMode={isPreviewMode}>
|
||||||
<ActiveTabProvider>
|
<ActiveTabProvider>
|
||||||
<TableOptionsProvider>
|
<TableOptionsProvider>
|
||||||
<div className="h-screen bg-gray-100 flex flex-col overflow-hidden">
|
<div className="h-screen bg-gray-100 flex flex-col">
|
||||||
{/* 상단 툴바 (프리뷰 모드에서만) */}
|
{/* 상단 툴바 (프리뷰 모드에서만) */}
|
||||||
{isPreviewMode && (
|
{isPreviewMode && (
|
||||||
<div className="sticky top-0 z-50 bg-white border-b shadow-sm">
|
<div className="sticky top-0 z-50 bg-white border-b shadow-sm">
|
||||||
|
|
@ -261,7 +263,7 @@ function PopScreenViewPage() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* POP 화면 컨텐츠 */}
|
{/* POP 화면 컨텐츠 */}
|
||||||
<div className={`flex-1 flex flex-col ${isPreviewMode ? "py-4 overflow-auto items-center" : ""}`}>
|
<div className={`flex-1 flex flex-col overflow-auto ${isPreviewMode ? "py-4 items-center" : ""}`}>
|
||||||
{/* 현재 모드 표시 (일반 모드) */}
|
{/* 현재 모드 표시 (일반 모드) */}
|
||||||
{!isPreviewMode && (
|
{!isPreviewMode && (
|
||||||
<div className="absolute top-2 right-2 z-10 bg-black/50 text-white text-xs px-2 py-1 rounded">
|
<div className="absolute top-2 right-2 z-10 bg-black/50 text-white text-xs px-2 py-1 rounded">
|
||||||
|
|
@ -270,7 +272,7 @@ function PopScreenViewPage() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`bg-white transition-all duration-300 ${isPreviewMode ? "shadow-2xl rounded-3xl overflow-auto border-8 border-gray-800" : "w-full"}`}
|
className={`bg-white transition-all duration-300 ${isPreviewMode ? "shadow-2xl rounded-3xl overflow-auto border-8 border-gray-800" : "w-full min-h-full"}`}
|
||||||
style={isPreviewMode ? {
|
style={isPreviewMode ? {
|
||||||
width: currentDevice.width,
|
width: currentDevice.width,
|
||||||
maxHeight: "80vh",
|
maxHeight: "80vh",
|
||||||
|
|
|
||||||
|
|
@ -553,9 +553,20 @@ function ComponentContent({ component, effectivePosition, isDesignMode, isSelect
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
function renderActualComponent(component: PopComponentDefinitionV5): React.ReactNode {
|
function renderActualComponent(component: PopComponentDefinitionV5): React.ReactNode {
|
||||||
const typeLabel = COMPONENT_TYPE_LABELS[component.type];
|
// 레지스트리에서 등록된 실제 컴포넌트 조회
|
||||||
|
const registeredComp = PopComponentRegistry.getComponent(component.type);
|
||||||
|
const ActualComp = registeredComp?.component;
|
||||||
|
|
||||||
// 샘플 박스 렌더링
|
if (ActualComp) {
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full overflow-hidden">
|
||||||
|
<ActualComp config={component.config} label={component.label} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 미등록 컴포넌트: 플레이스홀더 (fallback)
|
||||||
|
const typeLabel = COMPONENT_TYPE_LABELS[component.type] || component.type;
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center p-2">
|
<div className="flex h-full w-full items-center justify-center p-2">
|
||||||
<span className="text-xs text-gray-500">{component.label || typeLabel}</span>
|
<span className="text-xs text-gray-500">{component.label || typeLabel}</span>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue