631 lines
22 KiB
Markdown
631 lines
22 KiB
Markdown
|
|
# POP 화면 관리 시스템 개발 기록
|
||
|
|
|
||
|
|
> **AI 에이전트 안내**: 이 문서는 Progressive Disclosure 방식으로 구성되어 있습니다.
|
||
|
|
> 1. 먼저 [Quick Reference](#quick-reference)에서 필요한 정보 확인
|
||
|
|
> 2. 상세 내용이 필요하면 해당 섹션으로 이동
|
||
|
|
> 3. 코드가 필요하면 파일 직접 참조
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Quick Reference
|
||
|
|
|
||
|
|
### POP이란?
|
||
|
|
Point of Production - 현장 작업자용 모바일/태블릿 화면 시스템
|
||
|
|
|
||
|
|
### 핵심 결정사항
|
||
|
|
- **분리 방식**: 레이아웃 기반 구분 (screen_layouts_pop 테이블)
|
||
|
|
- **식별 방법**: `screen_layouts_pop`에 레코드 존재 여부로 POP 화면 판별
|
||
|
|
- **데스크톱 영향**: 없음 (모든 isPop 기본값 = false)
|
||
|
|
|
||
|
|
### 주요 경로
|
||
|
|
|
||
|
|
| 용도 | 경로 |
|
||
|
|
|------|------|
|
||
|
|
| POP 뷰어 URL | `/pop/screens/{screenId}?preview=true&device=tablet` |
|
||
|
|
| POP 관리 페이지 | `/admin/screenMng/popScreenMngList` |
|
||
|
|
| POP 레이아웃 API | `/api/screen-management/layout-pop/:screenId` |
|
||
|
|
|
||
|
|
### 파일 찾기 가이드
|
||
|
|
|
||
|
|
| 작업 | 파일 |
|
||
|
|
|------|------|
|
||
|
|
| POP 레이아웃 DB 스키마 | `db/migrations/052_create_screen_layouts_pop.sql` |
|
||
|
|
| POP API 서비스 로직 | `backend-node/src/services/screenManagementService.ts` (getLayoutPop, saveLayoutPop) |
|
||
|
|
| POP API 라우트 | `backend-node/src/routes/screenManagementRoutes.ts` |
|
||
|
|
| 프론트엔드 API 클라이언트 | `frontend/lib/api/screen.ts` (screenApi.getLayoutPop 등) |
|
||
|
|
| POP 화면 관리 UI | `frontend/app/(main)/admin/screenMng/popScreenMngList/page.tsx` |
|
||
|
|
| POP 뷰어 페이지 | `frontend/app/(pop)/pop/screens/[screenId]/page.tsx` |
|
||
|
|
| 미리보기 URL 분기 | `frontend/components/screen/ScreenSettingModal.tsx` (PreviewTab) |
|
||
|
|
| POP 컴포넌트 설계서 | `docs/pop/components-spec.md` (13개 컴포넌트 상세) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 섹션 목차
|
||
|
|
|
||
|
|
| # | 섹션 | 한 줄 요약 |
|
||
|
|
|---|------|----------|
|
||
|
|
| 1 | [아키텍처](#1-아키텍처) | 레이아웃 테이블로 POP/데스크톱 분리 |
|
||
|
|
| 2 | [데이터베이스](#2-데이터베이스) | screen_layouts_pop 테이블 (FK 없음) |
|
||
|
|
| 3 | [백엔드 API](#3-백엔드-api) | CRUD 4개 엔드포인트 |
|
||
|
|
| 4 | [프론트엔드 API](#4-프론트엔드-api) | screenApi에 4개 함수 추가 |
|
||
|
|
| 5 | [관리 페이지](#5-관리-페이지) | POP 화면만 필터링하여 표시 |
|
||
|
|
| 6 | [뷰어](#6-뷰어) | 모바일/태블릿 프레임 미리보기 |
|
||
|
|
| 7 | [미리보기](#7-미리보기) | isPop prop으로 URL 분기 |
|
||
|
|
| 8 | [파일 목록](#8-파일-목록) | 생성 3개, 수정 9개 |
|
||
|
|
| 9 | [반응형 전략](#9-반응형-전략-신규-결정사항) | Flow 레이아웃 (세로 쌓기) 채택 |
|
||
|
|
| 10 | [POP 사용자 앱](#10-pop-사용자-앱-구조-신규-결정사항) | 대시보드 카드 → 화면 뷰어 |
|
||
|
|
| 11 | [POP 디자이너](#11-pop-디자이너-신규-계획) | 좌(탭패널) + 우(팬캔버스), 반응형 편집 |
|
||
|
|
| 12 | [데이터 구조](#12-pop-레이아웃-데이터-구조-신규) | PopLayoutData, mobileOverride |
|
||
|
|
| 13 | [컴포넌트 재사용성](#13-컴포넌트-재사용성-분석-신규) | 2개 재사용, 4개 부분, 7개 신규 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. 아키텍처
|
||
|
|
|
||
|
|
**결정**: Option B (레이아웃 기반 구분)
|
||
|
|
|
||
|
|
```
|
||
|
|
screen_definitions (공용)
|
||
|
|
├── screen_layouts_v2 (데스크톱)
|
||
|
|
└── screen_layouts_pop (POP)
|
||
|
|
```
|
||
|
|
|
||
|
|
**선택 이유**: 기존 테이블 변경 없음, 데스크톱 영향 없음, 향후 통합 가능
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. 데이터베이스
|
||
|
|
|
||
|
|
**테이블**: `screen_layouts_pop`
|
||
|
|
|
||
|
|
| 컬럼 | 타입 | 설명 |
|
||
|
|
|------|------|------|
|
||
|
|
| id | SERIAL | PK |
|
||
|
|
| screen_id | INTEGER | 화면 ID (unique) |
|
||
|
|
| layout_data | JSONB | 컴포넌트 JSON |
|
||
|
|
|
||
|
|
**특이사항**: FK 없음 (soft-delete 지원)
|
||
|
|
|
||
|
|
**파일**: `db/migrations/052_create_screen_layouts_pop.sql`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 백엔드 API
|
||
|
|
|
||
|
|
| Method | Endpoint | 용도 |
|
||
|
|
|--------|----------|------|
|
||
|
|
| GET | `/api/screen-management/layout-pop/:screenId` | 조회 |
|
||
|
|
| POST | `/api/screen-management/layout-pop/:screenId` | 저장 |
|
||
|
|
| DELETE | `/api/screen-management/layout-pop/:screenId` | 삭제 |
|
||
|
|
| GET | `/api/screen-management/pop-layout-screen-ids` | ID 목록 |
|
||
|
|
|
||
|
|
**파일**: `backend-node/src/services/screenManagementService.ts`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. 프론트엔드 API
|
||
|
|
|
||
|
|
**파일**: `frontend/lib/api/screen.ts`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
screenApi.getLayoutPop(screenId) // 조회
|
||
|
|
screenApi.saveLayoutPop(screenId, data) // 저장
|
||
|
|
screenApi.deleteLayoutPop(screenId) // 삭제
|
||
|
|
screenApi.getScreenIdsWithPopLayout() // ID 목록
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. 관리 페이지
|
||
|
|
|
||
|
|
**파일**: `frontend/app/(main)/admin/screenMng/popScreenMngList/page.tsx`
|
||
|
|
|
||
|
|
**핵심 로직**:
|
||
|
|
```typescript
|
||
|
|
const popIds = await screenApi.getScreenIdsWithPopLayout();
|
||
|
|
const filteredScreens = screens.filter(s => new Set(popIds).has(s.screenId));
|
||
|
|
```
|
||
|
|
|
||
|
|
**기능**: POP 화면만 표시, 새 POP 화면 생성):, 보기/설계 버튼
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 뷰어
|
||
|
|
|
||
|
|
**파일**: `frontend/app/(pop)/pop/screens/[screenId]/page.tsx`
|
||
|
|
|
||
|
|
**URL 파라미터**:
|
||
|
|
| 파라미터 | 값 | 설명 |
|
||
|
|
|---------|---|------|
|
||
|
|
| preview | true | 툴바 표시 |
|
||
|
|
| device | mobile/tablet | 디바이스 크기 (기본: tablet) |
|
||
|
|
|
||
|
|
**디바이스 크기**: mobile(375x812), tablet(768x1024)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. 미리보기
|
||
|
|
|
||
|
|
**핵심**: `isPop` prop으로 URL 분기
|
||
|
|
|
||
|
|
```
|
||
|
|
popScreenMngList
|
||
|
|
└─► ScreenRelationFlow(isPop=true)
|
||
|
|
└─► ScreenSettingModal
|
||
|
|
└─► PreviewTab → /pop/screens/{id}
|
||
|
|
|
||
|
|
screenMngList (데스크톱)
|
||
|
|
└─► ScreenRelationFlow(isPop=false 기본값)
|
||
|
|
└─► ScreenSettingModal
|
||
|
|
└─► PreviewTab → /screens/{id}
|
||
|
|
```
|
||
|
|
|
||
|
|
**안전성**: isPop 기본값 = false → 데스크톱 영향 없음
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. 파일 목록
|
||
|
|
|
||
|
|
### 생성 (3개)
|
||
|
|
|
||
|
|
| 파일 | 용도 |
|
||
|
|
|------|------|
|
||
|
|
| `db/migrations/052_create_screen_layouts_pop.sql` | DB 스키마 |
|
||
|
|
| `frontend/app/(pop)/pop/screens/[screenId]/page.tsx` | POP 뷰어 |
|
||
|
|
| `frontend/app/(main)/admin/screenMng/popScreenMngList/page.tsx` | POP 관리 |
|
||
|
|
|
||
|
|
### 수정 (9개)
|
||
|
|
|
||
|
|
| 파일 | 변경 내용 |
|
||
|
|
|------|----------|
|
||
|
|
| `backend-node/src/services/screenManagementService.ts` | POP CRUD 함수 |
|
||
|
|
| `backend-node/src/controllers/screenManagementController.ts` | 컨트롤러 |
|
||
|
|
| `backend-node/src/routes/screenManagementRoutes.ts` | 라우트 |
|
||
|
|
| `frontend/lib/api/screen.ts` | API 클라이언트 |
|
||
|
|
| `frontend/components/screen/CreateScreenModal.tsx` | isPop prop |
|
||
|
|
| `frontend/components/screen/ScreenSettingModal.tsx` | isPop, PreviewTab |
|
||
|
|
| `frontend/components/screen/ScreenRelationFlow.tsx` | isPop 전달 |
|
||
|
|
| `frontend/components/screen/ScreenDesigner.tsx` | isPop, 미리보기 |
|
||
|
|
| `frontend/components/screen/toolbar/SlimToolbar.tsx` | POP 미리보기 버튼 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. 반응형 전략 (신규 결정사항)
|
||
|
|
|
||
|
|
### 문제점
|
||
|
|
- 데스크톱은 절대 좌표(`position: { x, y }`) 사용
|
||
|
|
- 모바일 화면 크기가 달라지면 레이아웃 깨짐
|
||
|
|
|
||
|
|
### 결정: Flow 레이아웃 채택
|
||
|
|
|
||
|
|
| 항목 | 데스크톱 | POP |
|
||
|
|
|-----|---------|-----|
|
||
|
|
| 배치 방식 | `position: { x, y }` | `order: number` (순서) |
|
||
|
|
| 컨테이너 | 자유 배치 | 중첩 구조 (섹션 > 필드) |
|
||
|
|
| 렌더러 | 절대 좌표 계산 | Flexbox column (세로 쌓기) |
|
||
|
|
|
||
|
|
### Flow 레이아웃 데이터 구조
|
||
|
|
```typescript
|
||
|
|
{
|
||
|
|
layoutMode: "flow", // flow | absolute
|
||
|
|
components: [
|
||
|
|
{
|
||
|
|
id: "section-1",
|
||
|
|
type: "pop-section",
|
||
|
|
order: 0, // 순서로 배치
|
||
|
|
children: [...]
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. POP 사용자 앱 구조 (신규 결정사항)
|
||
|
|
|
||
|
|
### 데스크톱 vs POP 진입 구조
|
||
|
|
|
||
|
|
| | 데스크톱 | POP |
|
||
|
|
|---|---------|-----|
|
||
|
|
| 메뉴 | 왼쪽 사이드바 | 대시보드 카드 |
|
||
|
|
| 네비게이션 | 복잡한 트리 구조 | 화면 → 뒤로가기 |
|
||
|
|
| URL | `/screens/{id}` | `/pop/screens/{id}` |
|
||
|
|
|
||
|
|
### POP 화면 흐름
|
||
|
|
```
|
||
|
|
/pop/login (POP 로그인)
|
||
|
|
↓
|
||
|
|
/pop/dashboard (화면 목록 - 카드형)
|
||
|
|
↓
|
||
|
|
/pop/screens/{id} (화면 뷰어)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. POP 디자이너 (신규 계획)
|
||
|
|
|
||
|
|
### 진입 경로
|
||
|
|
```
|
||
|
|
popScreenMngList → [설계] 버튼 → PopDesigner 컴포넌트
|
||
|
|
```
|
||
|
|
|
||
|
|
### 레이아웃 구조 (2026-02-02 수정)
|
||
|
|
데스크톱 Screen Designer와 유사하게 **좌측 탭 패널 + 우측 캔버스**:
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ [툴바] ← 목록 | 화면명 | 📱모바일 📱태블릿 | 🔄 | 💾저장 │
|
||
|
|
├────────────────┬────────────────────────────────────────────────┤
|
||
|
|
│ [패널] │ [캔버스 영역] │
|
||
|
|
│ ◀━━━━▶ │ │
|
||
|
|
│ (리사이즈) │ ┌────────────────────────┐ │
|
||
|
|
│ │ │ 디바이스 프레임 │ ← 드래그로 │
|
||
|
|
│ ┌────────────┐ │ │ │ 팬 이동 │
|
||
|
|
│ │컴포넌트│편집│ │ │ [섹션 1] │ │
|
||
|
|
│ └────────────┘ │ │ ├─ 필드 A │ │
|
||
|
|
│ │ │ └─ 필드 B │ │
|
||
|
|
│ (컴포넌트 탭) │ │ │ │
|
||
|
|
│ 📦 섹션 │ │ [섹션 2] │ │
|
||
|
|
│ 📝 필드 │ │ ├─ 버튼1 ─ 버튼2 │ │
|
||
|
|
│ 🔘 버튼 │ │ │ │
|
||
|
|
│ 📋 리스트 │ └────────────────────────┘ │
|
||
|
|
│ 📊 인디케이터 │ │
|
||
|
|
│ │ │
|
||
|
|
│ (편집 탭) │ │
|
||
|
|
│ 선택된 컴포 │ │
|
||
|
|
│ 넌트 설정 │ │
|
||
|
|
└────────────────┴────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### 패널 기능
|
||
|
|
| 기능 | 설명 |
|
||
|
|
|-----|------|
|
||
|
|
| **리사이즈** | 드래그로 패널 너비 조절 (min: 200px, max: 400px) |
|
||
|
|
| **컴포넌트 탭** | POP 전용 컴포넌트만 표시 |
|
||
|
|
| **편집 탭** | 선택된 컴포넌트 설정 (프리셋 기반) |
|
||
|
|
|
||
|
|
### 캔버스 기능
|
||
|
|
| 기능 | 설명 |
|
||
|
|
|-----|------|
|
||
|
|
| **팬(Pan)** | 마우스 드래그로 보는 위치 이동 |
|
||
|
|
| **줌** | 마우스 휠로 확대/축소 (선택사항) |
|
||
|
|
| **디바이스 탭** | 📱모바일 / 📱태블릿 전환 |
|
||
|
|
| **나란히 보기** | 옵션으로 둘 다 표시 가능 |
|
||
|
|
| **실시간 미리보기** | 편집 = 미리보기 (별도 창 불필요) |
|
||
|
|
|
||
|
|
### 캔버스 방식: 블록 쌓기
|
||
|
|
- 섹션끼리는 위→아래로 쌓임
|
||
|
|
- 섹션 안에서는 가로(row) 또는 세로(column) 선택 가능
|
||
|
|
- 드래그앤드롭으로 순서 변경
|
||
|
|
- 캔버스 자체가 실시간 미리보기
|
||
|
|
|
||
|
|
### 기준 해상도
|
||
|
|
| 디바이스 | 논리적 크기 (dp) | 용도 |
|
||
|
|
|---------|-----------------|------|
|
||
|
|
| 모바일 | 360 x 640 | Zebra TC52/57 등 산업용 핸드헬드 |
|
||
|
|
| 태블릿 | 768 x 1024 | 8~10인치 산업용 태블릿 |
|
||
|
|
|
||
|
|
### 터치 타겟 (장갑 착용 고려)
|
||
|
|
- 최소 버튼 크기: **60dp** (일반 앱 48dp보다 큼)
|
||
|
|
- 버튼 간격: **16dp** 이상
|
||
|
|
|
||
|
|
### 반응형 편집 방식
|
||
|
|
| 모드 | 설명 |
|
||
|
|
|-----|------|
|
||
|
|
| **기준 디바이스** | 태블릿 (메인 편집) |
|
||
|
|
| **자동 조정** | CSS flex-wrap, grid로 모바일 자동 줄바꿈 |
|
||
|
|
| **수동 조정** | 모바일 탭에서 그리드 열 수, 숨기기 설정 |
|
||
|
|
|
||
|
|
**흐름:**
|
||
|
|
```
|
||
|
|
1. 태블릿 탭에서 편집 (기준)
|
||
|
|
→ 모든 컴포넌트, 섹션, 순서, 데이터 바인딩 설정
|
||
|
|
|
||
|
|
2. 모바일 탭에서 확인
|
||
|
|
A) 자동 조정 OK → 그대로 저장
|
||
|
|
B) 배치 어색함 → 그리드 열 수 조정 또는 숨기기
|
||
|
|
```
|
||
|
|
|
||
|
|
### 섹션 내 컴포넌트 배치 옵션
|
||
|
|
| 설정 | 옵션 |
|
||
|
|
|-----|------|
|
||
|
|
| 배치 방향 | `row` / `column` |
|
||
|
|
| 순서 | 드래그로 변경 |
|
||
|
|
| 비율 | flex (1:1, 2:1, 1:2 등) |
|
||
|
|
| 정렬 | `start` / `center` / `end` |
|
||
|
|
| 간격 | `none` / `small` / `medium` / `large` |
|
||
|
|
| 줄바꿈 | `wrap` / `nowrap` |
|
||
|
|
| **그리드 열 수** | 태블릿용, 모바일용 각각 설정 가능 |
|
||
|
|
|
||
|
|
### 관리자가 설정 가능한 것
|
||
|
|
| 항목 | 설정 방식 |
|
||
|
|
|-----|----------|
|
||
|
|
| 섹션 순서 | 드래그로 위/아래 이동 |
|
||
|
|
| 섹션 내 배치 | 가로(row) / 세로(column) |
|
||
|
|
| 정렬 | 왼쪽/가운데/오른쪽, 위/가운데/아래 |
|
||
|
|
| 컴포넌트 비율 | 1:1, 2:1, 1:2 등 (flex) |
|
||
|
|
| 크기 | S/M/L/XL 프리셋 |
|
||
|
|
| 여백/간격 | 작음/보통/넓음 프리셋 |
|
||
|
|
| 아이콘 | 선택 가능 |
|
||
|
|
| 테마/색상 | 프리셋 또는 커스텀 |
|
||
|
|
| 그리드 열 수 | 태블릿/모바일 각각 |
|
||
|
|
| 모바일 숨기기 | 특정 컴포넌트 숨김 |
|
||
|
|
|
||
|
|
### 관리자가 설정 불가능한 것 (반응형 유지)
|
||
|
|
- 정확한 x, y 좌표
|
||
|
|
- 정확한 픽셀 크기 (예: 347px)
|
||
|
|
- 고정 위치 (예: 왼쪽에서 100px)
|
||
|
|
|
||
|
|
### 스타일 분리 원칙
|
||
|
|
```
|
||
|
|
뼈대 (변경 어려움 - 처음부터 잘 설계):
|
||
|
|
- 데이터 바인딩 구조 (columnName, dataSource)
|
||
|
|
- 컴포넌트 계층 (섹션 > 필드)
|
||
|
|
- 액션 로직
|
||
|
|
|
||
|
|
옷 (변경 쉬움 - 나중에 조정 가능):
|
||
|
|
- 색상, 폰트 크기 → CSS 변수/테마
|
||
|
|
- 버튼 모양 → 프리셋
|
||
|
|
- 아이콘 → 선택
|
||
|
|
```
|
||
|
|
|
||
|
|
### 다국어 연동 (준비)
|
||
|
|
- 상태: `showMultilangSettingsModal` 미리 추가
|
||
|
|
- 버튼: 툴바에 자리만 (비활성)
|
||
|
|
- 연결: 추후 `MultilangSettingsModal` import
|
||
|
|
|
||
|
|
### 데스크톱 시스템 재사용
|
||
|
|
| 기능 | 재사용 | 비고 |
|
||
|
|
|-----|-------|------|
|
||
|
|
| formData 관리 | O | 그대로 |
|
||
|
|
| 필드간 연결 | O | cascading, hierarchy |
|
||
|
|
| 테이블 참조 | O | dataSource, filter |
|
||
|
|
| 저장 이벤트 | O | beforeFormSave |
|
||
|
|
| 집계 | O | 스타일만 변경 |
|
||
|
|
| 설정 패널 | O | 탭 방식 참고 |
|
||
|
|
| CRUD API | O | 그대로 |
|
||
|
|
| buttonActions | O | 그대로 |
|
||
|
|
| 다국어 | O | MultilangSettingsModal |
|
||
|
|
|
||
|
|
### 파일 구조 (신규 생성 예정)
|
||
|
|
```
|
||
|
|
frontend/components/pop/
|
||
|
|
├── PopDesigner.tsx # 메인 (좌: 패널, 우: 캔버스)
|
||
|
|
├── PopCanvas.tsx # 캔버스 (팬/줌 + 프레임)
|
||
|
|
├── PopToolbar.tsx # 상단 툴바
|
||
|
|
│
|
||
|
|
├── panels/
|
||
|
|
│ └── PopPanel.tsx # 통합 패널 (컴포넌트/편집 탭)
|
||
|
|
│
|
||
|
|
├── components/ # POP 전용 컴포넌트
|
||
|
|
│ ├── PopSection.tsx
|
||
|
|
│ ├── PopField.tsx
|
||
|
|
│ ├── PopButton.tsx
|
||
|
|
│ └── ...
|
||
|
|
│
|
||
|
|
└── types/
|
||
|
|
└── pop-layout.ts # PopLayoutData, PopComponentData
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 12. POP 레이아웃 데이터 구조 (신규)
|
||
|
|
|
||
|
|
### PopLayoutData
|
||
|
|
```typescript
|
||
|
|
interface PopLayoutData {
|
||
|
|
version: "pop-1.0";
|
||
|
|
layoutMode: "flow"; // 항상 flow (절대좌표 없음)
|
||
|
|
deviceTarget: "mobile" | "tablet" | "both";
|
||
|
|
components: PopComponentData[];
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### PopComponentData
|
||
|
|
```typescript
|
||
|
|
interface PopComponentData {
|
||
|
|
id: string;
|
||
|
|
type: "pop-section" | "pop-field" | "pop-button" | "pop-list" | "pop-indicator";
|
||
|
|
order: number; // 순서 (x, y 좌표 대신)
|
||
|
|
|
||
|
|
// 개별 컴포넌트 flex 비율
|
||
|
|
flex?: number; // 기본 1
|
||
|
|
|
||
|
|
// 섹션인 경우: 내부 레이아웃 설정
|
||
|
|
layout?: {
|
||
|
|
direction: "row" | "column";
|
||
|
|
justify: "start" | "center" | "end" | "between";
|
||
|
|
align: "start" | "center" | "end";
|
||
|
|
gap: "none" | "small" | "medium" | "large";
|
||
|
|
wrap: boolean;
|
||
|
|
grid?: number; // 태블릿 기준 열 수
|
||
|
|
};
|
||
|
|
|
||
|
|
// 크기 프리셋
|
||
|
|
size?: "S" | "M" | "L" | "XL" | "full";
|
||
|
|
|
||
|
|
// 데이터 바인딩
|
||
|
|
dataBinding?: {
|
||
|
|
tableName: string;
|
||
|
|
columnName: string;
|
||
|
|
displayField?: string;
|
||
|
|
};
|
||
|
|
|
||
|
|
// 스타일 프리셋
|
||
|
|
style?: {
|
||
|
|
variant: "default" | "primary" | "success" | "warning" | "danger";
|
||
|
|
padding: "none" | "small" | "medium" | "large";
|
||
|
|
};
|
||
|
|
|
||
|
|
// 모바일 오버라이드 (선택사항)
|
||
|
|
mobileOverride?: {
|
||
|
|
grid?: number; // 모바일 열 수 (없으면 자동)
|
||
|
|
hidden?: boolean; // 모바일에서 숨기기
|
||
|
|
};
|
||
|
|
|
||
|
|
// 하위 컴포넌트 (섹션 내부)
|
||
|
|
children?: PopComponentData[];
|
||
|
|
|
||
|
|
// 컴포넌트별 설정
|
||
|
|
config?: Record<string, any>;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 데스크톱 vs POP 데이터 비교
|
||
|
|
| 항목 | 데스크톱 (LayoutData) | POP (PopLayoutData) |
|
||
|
|
|-----|----------------------|---------------------|
|
||
|
|
| 배치 | `position: { x, y, z }` | `order: number` |
|
||
|
|
| 크기 | `size: { width, height }` (픽셀) | `size: "S" | "M" | "L"` (프리셋) |
|
||
|
|
| 컨테이너 | 없음 (자유 배치) | `layout: { direction, grid }` |
|
||
|
|
| 반응형 | 없음 | `mobileOverride` |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 13. 컴포넌트 재사용성 분석
|
||
|
|
|
||
|
|
### 최종 분류
|
||
|
|
|
||
|
|
| 분류 | 개수 | 컴포넌트 |
|
||
|
|
|-----|-----|---------|
|
||
|
|
| 완전 재사용 | 2 | form-field, action-button |
|
||
|
|
| 부분 재사용 | 4 | tab-panel, data-table, kpi-gauge, process-flow |
|
||
|
|
| 신규 개발 | 7 | section, card-list, status-indicator, number-pad, barcode-scanner, timer, alarm-list |
|
||
|
|
|
||
|
|
### 핵심 컴포넌트 7개 (최소 필수)
|
||
|
|
|
||
|
|
| 컴포넌트 | 역할 | 포함 기능 |
|
||
|
|
|---------|------|----------|
|
||
|
|
| **pop-section** | 레이아웃 컨테이너 | 카드, 그룹핑, 접기/펼치기 |
|
||
|
|
| **pop-field** | 데이터 입력/표시 | 텍스트, 숫자, 드롭다운, 바코드, 숫자패드 |
|
||
|
|
| **pop-button** | 액션 실행 | 저장, 삭제, API 호출, 화면이동 |
|
||
|
|
| **pop-list** | 데이터 목록 | 카드리스트, 선택목록, 테이블 참조 |
|
||
|
|
| **pop-indicator** | 상태/수치 표시 | KPI, 게이지, 신호등, 진행률 |
|
||
|
|
| **pop-scanner** | 바코드/QR 입력 | 카메라, 외부 스캐너 |
|
||
|
|
| **pop-numpad** | 숫자 입력 특화 | 큰 버튼, 계산기 모드 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## TODO
|
||
|
|
|
||
|
|
### Phase 1: POP 디자이너 개발 (현재 진행)
|
||
|
|
|
||
|
|
| # | 작업 | 설명 | 상태 |
|
||
|
|
|---|------|------|------|
|
||
|
|
| 1 | `PopLayoutData` 타입 정의 | order, layout, mobileOverride | 완료 |
|
||
|
|
| 2 | `PopDesigner.tsx` | 좌: 리사이즈 패널, 우: 팬 가능 캔버스 | 완료 |
|
||
|
|
| 3 | `PopPanel.tsx` | 탭 (컴포넌트/편집), POP 컴포넌트만 | 완료 |
|
||
|
|
| 4 | `PopCanvas.tsx` | 팬/줌 + 디바이스 프레임 + 블록 렌더링 | 완료 |
|
||
|
|
| 5 | `SectionGrid.tsx` | 섹션 내부 컴포넌트 배치 (react-grid-layout) | 완료 |
|
||
|
|
| 6 | 드래그앤드롭 | 팔레트→캔버스 (섹션), 팔레트→섹션 (컴포넌트) | 완료 |
|
||
|
|
| 7 | 컴포넌트 자유 배치/리사이즈 | 고정 셀 크기(40px) 기반 자동 그리드 | 완료 |
|
||
|
|
| 8 | 편집 탭 | 그리드 설정, 모바일 오버라이드 | 완료 (기본) |
|
||
|
|
| 9 | 저장/로드 | 기존 API 재사용 (saveLayoutPop) | 완료 |
|
||
|
|
|
||
|
|
### Phase 2: POP 컴포넌트 개발
|
||
|
|
|
||
|
|
상세: `docs/pop/components-spec.md`
|
||
|
|
|
||
|
|
1단계 (우선):
|
||
|
|
- [ ] pop-section (레이아웃 컨테이너)
|
||
|
|
- [ ] pop-field (범용 입력)
|
||
|
|
- [ ] pop-button (액션)
|
||
|
|
|
||
|
|
2단계:
|
||
|
|
- [ ] pop-list (카드형 목록)
|
||
|
|
- [ ] pop-indicator (상태/KPI)
|
||
|
|
- [ ] pop-numpad (숫자패드)
|
||
|
|
|
||
|
|
3단계:
|
||
|
|
- [ ] pop-scanner (바코드)
|
||
|
|
- [ ] pop-timer (타이머)
|
||
|
|
- [ ] pop-alarm (알람)
|
||
|
|
|
||
|
|
### Phase 3: POP 사용자 앱
|
||
|
|
- [ ] `/pop/login` - POP 전용 로그인
|
||
|
|
- [ ] `/pop/dashboard` - 화면 목록 (카드형)
|
||
|
|
- [ ] `/pop/screens/[id]` - Flow 렌더러 적용
|
||
|
|
|
||
|
|
### 기타
|
||
|
|
- [ ] POP 컴포넌트 레지스트리
|
||
|
|
- [ ] POP 메뉴/폴더 관리
|
||
|
|
- [ ] POP 인증 분리
|
||
|
|
- [ ] 다국어 연동
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 핵심 파일 참조
|
||
|
|
|
||
|
|
### 기존 파일 (참고용)
|
||
|
|
| 파일 | 용도 |
|
||
|
|
|------|------|
|
||
|
|
| `frontend/app/(main)/admin/screenMng/popScreenMngList/page.tsx` | 진입점, PopDesigner 호출 위치 |
|
||
|
|
| `frontend/components/screen/ScreenDesigner.tsx` | 데스크톱 디자이너 (구조 참고) |
|
||
|
|
| `frontend/components/screen/modals/MultilangSettingsModal.tsx` | 다국어 모달 (추후 연동) |
|
||
|
|
| `frontend/lib/api/screen.ts` | API (getLayoutPop, saveLayoutPop) |
|
||
|
|
| `backend-node/src/services/screenManagementService.ts` | POP CRUD (4720~4920행) |
|
||
|
|
|
||
|
|
### 신규 생성 예정
|
||
|
|
| 파일 | 용도 |
|
||
|
|
|------|------|
|
||
|
|
| `frontend/components/pop/PopDesigner.tsx` | 메인 디자이너 |
|
||
|
|
| `frontend/components/pop/PopCanvas.tsx` | 캔버스 (팬/줌) |
|
||
|
|
| `frontend/components/pop/PopToolbar.tsx` | 툴바 |
|
||
|
|
| `frontend/components/pop/panels/PopPanel.tsx` | 통합 패널 |
|
||
|
|
| `frontend/components/pop/types/pop-layout.ts` | 타입 정의 |
|
||
|
|
| `frontend/components/pop/components/PopSection.tsx` | 섹션 컴포넌트 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 14. 그리드 시스템 단순화 (2026-02-02 변경)
|
||
|
|
|
||
|
|
### 기존 문제: 이중 그리드 구조
|
||
|
|
```
|
||
|
|
캔버스 (24열, rowHeight 20px)
|
||
|
|
└─ 섹션 (colSpan/rowSpan으로 크기 지정)
|
||
|
|
└─ 내부 그리드 (columns/rows로 컴포넌트 배치)
|
||
|
|
```
|
||
|
|
|
||
|
|
**문제점:**
|
||
|
|
1. 섹션 크기와 내부 그리드가 독립적이라 동기화 안됨
|
||
|
|
2. 섹션을 늘려도 내부 그리드 점은 그대로 (비례 확대만)
|
||
|
|
3. 사용자가 두 가지 단위를 이해해야 함
|
||
|
|
|
||
|
|
### 변경: 단일 자동계산 그리드
|
||
|
|
|
||
|
|
**핵심 변경사항:**
|
||
|
|
- 그리드 점(dot) 제거
|
||
|
|
- 고정 셀 크기(40px) 기반으로 섹션 크기에 따라 열/행 수 자동 계산
|
||
|
|
- 컴포넌트는 react-grid-layout으로 자유롭게 드래그/리사이즈
|
||
|
|
|
||
|
|
**코드 (SectionGrid.tsx):**
|
||
|
|
```typescript
|
||
|
|
const CELL_SIZE = 40;
|
||
|
|
const cols = Math.max(1, Math.floor((availableWidth + gap) / (CELL_SIZE + gap)));
|
||
|
|
const rows = Math.max(1, Math.floor((availableHeight + gap) / (CELL_SIZE + gap)));
|
||
|
|
```
|
||
|
|
|
||
|
|
**결과:**
|
||
|
|
- 섹션 크기 변경 → 내부 셀 개수 자동 조정
|
||
|
|
- 컴포넌트 자유 배치/리사이즈 가능
|
||
|
|
- 직관적인 사용자 경험
|
||
|
|
|
||
|
|
### onLayoutChange 대신 onDragStop/onResizeStop 사용
|
||
|
|
|
||
|
|
**문제:** onLayoutChange는 드롭 직후에도 호출되어 섹션 크기가 자동 확대됨
|
||
|
|
|
||
|
|
**해결:**
|
||
|
|
```typescript
|
||
|
|
// 변경 전
|
||
|
|
<GridLayout onLayoutChange={handleLayoutChange} ... />
|
||
|
|
|
||
|
|
// 변경 후
|
||
|
|
<GridLayout onDragStop={handleDragResizeStop} onResizeStop={handleDragResizeStop} ... />
|
||
|
|
```
|
||
|
|
|
||
|
|
상태 업데이트는 드래그/리사이즈 완료 후에만 실행
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
*최종 업데이트: 2026-02-02*
|