659 lines
23 KiB
Markdown
659 lines
23 KiB
Markdown
|
|
# POP 화면 시스템 구현 계획서
|
||
|
|
|
||
|
|
## 개요
|
||
|
|
|
||
|
|
Vexplor 서비스 내에서 POP(Point of Production) 화면을 구성할 수 있는 시스템을 구현합니다.
|
||
|
|
기존 Vexplor와 충돌 없이 별도 공간에서 개발하되, 장기적으로 통합 가능하도록 동일한 서비스 로직을 사용합니다.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 핵심 원칙
|
||
|
|
|
||
|
|
| 원칙 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| **충돌 방지** | POP 전용 공간에서 개발 |
|
||
|
|
| **통합 준비** | 기본 서비스 로직은 Vexplor와 동일 |
|
||
|
|
| **데이터 공유** | 같은 DB, 같은 데이터 소스 사용 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 아키텍처 개요
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
||
|
|
│ [데이터베이스] │
|
||
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
||
|
|
│ │ screen_ │ │ screen_layouts_ │ │ screen_layouts_ │ │
|
||
|
|
│ │ definitions │ │ v2 (데스크톱) │ │ pop (POP) │ │
|
||
|
|
│ │ (공통) │ └─────────────────┘ └─────────────────┘ │
|
||
|
|
│ └─────────────────┘ │
|
||
|
|
└─────────────────────────────────────────────────────────────────────┘
|
||
|
|
│
|
||
|
|
▼
|
||
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
||
|
|
│ [백엔드 API] │
|
||
|
|
│ /screen-management/screens/:id/layout-v2 (데스크톱) │
|
||
|
|
│ /screen-management/screens/:id/layout-pop (POP) │
|
||
|
|
└─────────────────────────────────────────────────────────────────────┘
|
||
|
|
│
|
||
|
|
┌───────────────┴───────────────┐
|
||
|
|
▼ ▼
|
||
|
|
┌─────────────────────────┐ ┌─────────────────────────┐
|
||
|
|
│ [프론트엔드 - 데스크톱] │ │ [프론트엔드 - POP] │
|
||
|
|
│ │ │ │
|
||
|
|
│ app/(main)/ │ │ app/(pop)/ │
|
||
|
|
│ lib/registry/ │ │ lib/registry/ │
|
||
|
|
│ components/ │ │ pop-components/ │
|
||
|
|
│ components/screen/ │ │ components/pop/ │
|
||
|
|
└─────────────────────────┘ └─────────────────────────┘
|
||
|
|
│ │
|
||
|
|
▼ ▼
|
||
|
|
┌─────────────────────────┐ ┌─────────────────────────┐
|
||
|
|
│ PC 브라우저 │ │ 모바일/태블릿 브라우저 │
|
||
|
|
│ (마우스 + 키보드) │ │ (터치 + 스캐너) │
|
||
|
|
└─────────────────────────┘ └─────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. 데이터베이스 변경사항
|
||
|
|
|
||
|
|
### 1-1. 테이블 추가/유지 현황
|
||
|
|
|
||
|
|
| 구분 | 테이블명 | 변경 내용 | 비고 |
|
||
|
|
|------|----------|----------|------|
|
||
|
|
| **추가** | `screen_layouts_pop` | POP 레이아웃 저장용 | 신규 테이블 |
|
||
|
|
| **유지** | `screen_definitions` | 변경 없음 | 공통 사용 |
|
||
|
|
| **유지** | `screen_layouts_v2` | 변경 없음 | 데스크톱 전용 |
|
||
|
|
|
||
|
|
### 1-2. 신규 테이블 DDL
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 마이그레이션 파일: db/migrations/XXX_create_screen_layouts_pop.sql
|
||
|
|
|
||
|
|
CREATE TABLE screen_layouts_pop (
|
||
|
|
layout_id SERIAL PRIMARY KEY,
|
||
|
|
screen_id INTEGER NOT NULL REFERENCES screen_definitions(screen_id),
|
||
|
|
company_code VARCHAR(20) NOT NULL,
|
||
|
|
layout_data JSONB NOT NULL DEFAULT '{}'::jsonb, -- 반응형 레이아웃 JSON
|
||
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
created_by VARCHAR(50),
|
||
|
|
updated_by VARCHAR(50),
|
||
|
|
UNIQUE(screen_id, company_code)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX idx_pop_screen_id ON screen_layouts_pop(screen_id);
|
||
|
|
CREATE INDEX idx_pop_company_code ON screen_layouts_pop(company_code);
|
||
|
|
|
||
|
|
COMMENT ON TABLE screen_layouts_pop IS 'POP 화면 레이아웃 저장 테이블 (모바일/태블릿 반응형)';
|
||
|
|
COMMENT ON COLUMN screen_layouts_pop.layout_data IS 'V2 형식의 레이아웃 JSON (반응형 구조)';
|
||
|
|
```
|
||
|
|
|
||
|
|
### 1-3. 레이아웃 JSON 구조 (V2 형식 동일)
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"version": "2.0",
|
||
|
|
"components": [
|
||
|
|
{
|
||
|
|
"id": "comp_xxx",
|
||
|
|
"url": "@/lib/registry/pop-components/pop-card-list",
|
||
|
|
"position": { "x": 0, "y": 0 },
|
||
|
|
"size": { "width": 100, "height": 50 },
|
||
|
|
"displayOrder": 0,
|
||
|
|
"overrides": {
|
||
|
|
"tableName": "user_info",
|
||
|
|
"columns": ["id", "name", "status"],
|
||
|
|
"cardStyle": "compact"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"updatedAt": "2026-01-29T12:00:00Z"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. 백엔드 변경사항
|
||
|
|
|
||
|
|
### 2-1. 파일 수정 목록
|
||
|
|
|
||
|
|
| 구분 | 파일 경로 | 변경 내용 |
|
||
|
|
|------|----------|----------|
|
||
|
|
| **수정** | `backend-node/src/services/screenManagementService.ts` | POP 레이아웃 CRUD 함수 추가 |
|
||
|
|
| **수정** | `backend-node/src/routes/screenManagementRoutes.ts` | POP API 엔드포인트 추가 |
|
||
|
|
|
||
|
|
### 2-2. 추가 API 엔드포인트
|
||
|
|
|
||
|
|
```
|
||
|
|
GET /screen-management/screens/:screenId/layout-pop # POP 레이아웃 조회
|
||
|
|
POST /screen-management/screens/:screenId/layout-pop # POP 레이아웃 저장
|
||
|
|
DELETE /screen-management/screens/:screenId/layout-pop # POP 레이아웃 삭제
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2-3. screenManagementService.ts 추가 함수
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 기존 함수 (유지)
|
||
|
|
getScreenLayoutV2(screenId, companyCode)
|
||
|
|
saveLayoutV2(screenId, companyCode, layoutData)
|
||
|
|
|
||
|
|
// 추가 함수 (신규) - 로직은 V2와 동일, 테이블명만 다름
|
||
|
|
getScreenLayoutPop(screenId, companyCode)
|
||
|
|
saveLayoutPop(screenId, companyCode, layoutData)
|
||
|
|
deleteLayoutPop(screenId, companyCode)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. 프론트엔드 변경사항
|
||
|
|
|
||
|
|
### 3-1. 폴더 구조
|
||
|
|
|
||
|
|
```
|
||
|
|
frontend/
|
||
|
|
├── app/
|
||
|
|
│ └── (pop)/ # [기존] POP 라우팅 그룹
|
||
|
|
│ ├── layout.tsx # [수정] POP 전용 레이아웃
|
||
|
|
│ ├── pop/
|
||
|
|
│ │ └── page.tsx # [기존] POP 메인
|
||
|
|
│ └── screens/ # [추가] POP 화면 뷰어
|
||
|
|
│ └── [screenId]/
|
||
|
|
│ └── page.tsx # [추가] POP 동적 화면
|
||
|
|
│
|
||
|
|
├── lib/
|
||
|
|
│ ├── api/
|
||
|
|
│ │ └── screen.ts # [수정] POP API 함수 추가
|
||
|
|
│ │
|
||
|
|
│ ├── registry/
|
||
|
|
│ │ ├── pop-components/ # [추가] POP 전용 컴포넌트
|
||
|
|
│ │ │ ├── pop-card-list/
|
||
|
|
│ │ │ │ ├── PopCardListComponent.tsx
|
||
|
|
│ │ │ │ ├── PopCardListConfigPanel.tsx
|
||
|
|
│ │ │ │ └── index.ts
|
||
|
|
│ │ │ ├── pop-touch-button/
|
||
|
|
│ │ │ ├── pop-scanner-input/
|
||
|
|
│ │ │ └── index.ts # POP 컴포넌트 내보내기
|
||
|
|
│ │ │
|
||
|
|
│ │ ├── PopComponentRegistry.ts # [추가] POP 컴포넌트 레지스트리
|
||
|
|
│ │ └── ComponentRegistry.ts # [유지] 기존 유지
|
||
|
|
│ │
|
||
|
|
│ ├── schemas/
|
||
|
|
│ │ └── popComponentConfig.ts # [추가] POP용 Zod 스키마
|
||
|
|
│ │
|
||
|
|
│ └── utils/
|
||
|
|
│ └── layoutPopConverter.ts # [추가] POP 레이아웃 변환기
|
||
|
|
│
|
||
|
|
└── components/
|
||
|
|
└── pop/ # [기존] POP UI 컴포넌트
|
||
|
|
├── PopScreenDesigner.tsx # [추가] POP 화면 설계 도구
|
||
|
|
├── PopPreview.tsx # [추가] POP 미리보기
|
||
|
|
└── PopDynamicRenderer.tsx # [추가] POP 동적 렌더러
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3-2. 파일별 상세 내용
|
||
|
|
|
||
|
|
#### A. 신규 파일 (추가)
|
||
|
|
|
||
|
|
| 파일 | 역할 | 기반 |
|
||
|
|
|------|------|------|
|
||
|
|
| `app/(pop)/screens/[screenId]/page.tsx` | POP 화면 뷰어 | `app/(main)/screens/[screenId]/page.tsx` 참고 |
|
||
|
|
| `lib/registry/PopComponentRegistry.ts` | POP 컴포넌트 등록 | `ComponentRegistry.ts` 구조 동일 |
|
||
|
|
| `lib/registry/pop-components/*` | POP 전용 컴포넌트 | 신규 개발 |
|
||
|
|
| `lib/schemas/popComponentConfig.ts` | POP Zod 스키마 | `componentConfig.ts` 구조 동일 |
|
||
|
|
| `lib/utils/layoutPopConverter.ts` | POP 레이아웃 변환 | `layoutV2Converter.ts` 구조 동일 |
|
||
|
|
| `components/pop/PopScreenDesigner.tsx` | POP 화면 설계 | 신규 개발 |
|
||
|
|
| `components/pop/PopDynamicRenderer.tsx` | POP 동적 렌더러 | `DynamicComponentRenderer.tsx` 참고 |
|
||
|
|
|
||
|
|
#### B. 수정 파일
|
||
|
|
|
||
|
|
| 파일 | 변경 내용 |
|
||
|
|
|------|----------|
|
||
|
|
| `lib/api/screen.ts` | `getLayoutPop()`, `saveLayoutPop()` 함수 추가 |
|
||
|
|
| `app/(pop)/layout.tsx` | POP 전용 레이아웃 스타일 적용 |
|
||
|
|
|
||
|
|
#### C. 유지 파일 (변경 없음)
|
||
|
|
|
||
|
|
| 파일 | 이유 |
|
||
|
|
|------|------|
|
||
|
|
| `lib/registry/ComponentRegistry.ts` | 데스크톱 전용, 분리 유지 |
|
||
|
|
| `lib/schemas/componentConfig.ts` | 데스크톱 전용, 분리 유지 |
|
||
|
|
| `lib/utils/layoutV2Converter.ts` | 데스크톱 전용, 분리 유지 |
|
||
|
|
| `app/(main)/*` | 데스크톱 전용, 변경 없음 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. 서비스 로직 흐름
|
||
|
|
|
||
|
|
### 4-1. 데스크톱 (기존 - 변경 없음)
|
||
|
|
|
||
|
|
```
|
||
|
|
[사용자] → /screens/123 접속
|
||
|
|
↓
|
||
|
|
[app/(main)/screens/[screenId]/page.tsx]
|
||
|
|
↓
|
||
|
|
[getLayoutV2(screenId)] → API 호출
|
||
|
|
↓
|
||
|
|
[screen_layouts_v2 테이블] → 레이아웃 JSON 반환
|
||
|
|
↓
|
||
|
|
[DynamicComponentRenderer] → 컴포넌트 렌더링
|
||
|
|
↓
|
||
|
|
[ComponentRegistry] → 컴포넌트 찾기
|
||
|
|
↓
|
||
|
|
[lib/registry/components/table-list] → 컴포넌트 실행
|
||
|
|
↓
|
||
|
|
[화면 표시]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4-2. POP (신규 - 동일 로직)
|
||
|
|
|
||
|
|
```
|
||
|
|
[사용자] → /pop/screens/123 접속
|
||
|
|
↓
|
||
|
|
[app/(pop)/screens/[screenId]/page.tsx]
|
||
|
|
↓
|
||
|
|
[getLayoutPop(screenId)] → API 호출
|
||
|
|
↓
|
||
|
|
[screen_layouts_pop 테이블] → 레이아웃 JSON 반환
|
||
|
|
↓
|
||
|
|
[PopDynamicRenderer] → 컴포넌트 렌더링
|
||
|
|
↓
|
||
|
|
[PopComponentRegistry] → 컴포넌트 찾기
|
||
|
|
↓
|
||
|
|
[lib/registry/pop-components/pop-card-list] → 컴포넌트 실행
|
||
|
|
↓
|
||
|
|
[화면 표시]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. 로직 변경 여부
|
||
|
|
|
||
|
|
| 구분 | 로직 변경 | 설명 |
|
||
|
|
|------|----------|------|
|
||
|
|
| 데이터베이스 CRUD | **없음** | 동일한 SELECT/INSERT/UPDATE 패턴 |
|
||
|
|
| API 호출 방식 | **없음** | 동일한 REST API 패턴 |
|
||
|
|
| 컴포넌트 렌더링 | **없음** | 동일한 URL 기반 + overrides 방식 |
|
||
|
|
| Zod 스키마 검증 | **없음** | 동일한 검증 로직 |
|
||
|
|
| 레이아웃 JSON 구조 | **없음** | 동일한 V2 JSON 구조 사용 |
|
||
|
|
|
||
|
|
**결론: 로직 변경 없음, 파일/테이블 분리만 진행**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. 데스크톱 vs POP 비교
|
||
|
|
|
||
|
|
| 구분 | Vexplor (데스크톱) | POP (모바일/태블릿) |
|
||
|
|
|------|-------------------|---------------------|
|
||
|
|
| **타겟 기기** | PC (마우스+키보드) | 모바일/태블릿 (터치) |
|
||
|
|
| **화면 크기** | 1920x1080 고정 | 반응형 (다양한 크기) |
|
||
|
|
| **UI 스타일** | 테이블 중심, 작은 버튼 | 카드 중심, 큰 터치 버튼 |
|
||
|
|
| **입력 방식** | 키보드 타이핑 | 터치, 스캐너, 음성 |
|
||
|
|
| **사용 환경** | 사무실 | 현장, 창고, 공장 |
|
||
|
|
| **레이아웃 테이블** | `screen_layouts_v2` | `screen_layouts_pop` |
|
||
|
|
| **컴포넌트 경로** | `lib/registry/components/` | `lib/registry/pop-components/` |
|
||
|
|
| **레지스트리** | `ComponentRegistry.ts` | `PopComponentRegistry.ts` |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. 장기 통합 시나리오
|
||
|
|
|
||
|
|
### Phase 1: 분리 개발 (현재 목표)
|
||
|
|
|
||
|
|
```
|
||
|
|
[데스크톱] [POP]
|
||
|
|
ComponentRegistry PopComponentRegistry
|
||
|
|
components/ pop-components/
|
||
|
|
screen_layouts_v2 screen_layouts_pop
|
||
|
|
```
|
||
|
|
|
||
|
|
### Phase 2: 부분 통합 (향후)
|
||
|
|
|
||
|
|
```
|
||
|
|
[통합 가능한 부분]
|
||
|
|
- 공통 유틸리티 함수
|
||
|
|
- 공통 Zod 스키마
|
||
|
|
- 공통 타입 정의
|
||
|
|
|
||
|
|
[분리 유지]
|
||
|
|
- 플랫폼별 컴포넌트
|
||
|
|
- 플랫폼별 레이아웃
|
||
|
|
```
|
||
|
|
|
||
|
|
### Phase 3: 완전 통합 (최종)
|
||
|
|
|
||
|
|
```
|
||
|
|
[단일 컴포넌트 레지스트리]
|
||
|
|
ComponentRegistry
|
||
|
|
├── components/ (공통)
|
||
|
|
├── desktop-components/ (데스크톱 전용)
|
||
|
|
└── pop-components/ (POP 전용)
|
||
|
|
|
||
|
|
[단일 레이아웃 테이블] (선택사항)
|
||
|
|
screen_layouts
|
||
|
|
├── platform = 'desktop'
|
||
|
|
└── platform = 'pop'
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. V2 공통 요소 (통합 핵심)
|
||
|
|
|
||
|
|
POP과 데스크톱이 장기적으로 통합될 수 있는 **핵심 기반**입니다.
|
||
|
|
|
||
|
|
### 8-1. 공통 유틸리티 함수
|
||
|
|
|
||
|
|
**파일 위치:** `frontend/lib/schemas/componentConfig.ts`, `frontend/lib/utils/layoutV2Converter.ts`
|
||
|
|
|
||
|
|
#### 핵심 병합/추출 함수 (가장 중요!)
|
||
|
|
|
||
|
|
| 함수명 | 역할 | 사용 시점 |
|
||
|
|
|--------|------|----------|
|
||
|
|
| `deepMerge()` | 객체 깊은 병합 | 기본값 + overrides 합칠 때 |
|
||
|
|
| `mergeComponentConfig()` | 기본값 + 커스텀 병합 | **렌더링 시** (화면 표시) |
|
||
|
|
| `extractCustomConfig()` | 기본값과 다른 부분만 추출 | **저장 시** (DB 저장) |
|
||
|
|
| `isDeepEqual()` | 두 객체 깊은 비교 | 변경 여부 판단 |
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 예시: 저장 시 차이값만 추출
|
||
|
|
const defaults = { showHeader: true, pageSize: 20 };
|
||
|
|
const fullConfig = { showHeader: true, pageSize: 50, customField: "test" };
|
||
|
|
const overrides = extractCustomConfig(fullConfig, defaults);
|
||
|
|
// 결과: { pageSize: 50, customField: "test" } (차이값만!)
|
||
|
|
```
|
||
|
|
|
||
|
|
#### URL 처리 함수
|
||
|
|
|
||
|
|
| 함수명 | 역할 | 예시 |
|
||
|
|
|--------|------|------|
|
||
|
|
| `getComponentUrl()` | 타입 → URL 변환 | `"v2-table-list"` → `"@/lib/registry/components/v2-table-list"` |
|
||
|
|
| `getComponentTypeFromUrl()` | URL → 타입 추출 | `"@/lib/registry/components/v2-table-list"` → `"v2-table-list"` |
|
||
|
|
|
||
|
|
#### 기본값 조회 함수
|
||
|
|
|
||
|
|
| 함수명 | 역할 |
|
||
|
|
|--------|------|
|
||
|
|
| `getComponentDefaults()` | 컴포넌트 타입으로 기본값 조회 |
|
||
|
|
| `getDefaultsByUrl()` | URL로 기본값 조회 |
|
||
|
|
|
||
|
|
#### V2 로드/저장 함수 (핵심!)
|
||
|
|
|
||
|
|
| 함수명 | 역할 | 사용 시점 |
|
||
|
|
|--------|------|----------|
|
||
|
|
| `loadComponentV2()` | 컴포넌트 로드 (기본값 병합) | DB → 화면 |
|
||
|
|
| `saveComponentV2()` | 컴포넌트 저장 (차이값 추출) | 화면 → DB |
|
||
|
|
| `loadLayoutV2()` | 레이아웃 전체 로드 | DB → 화면 |
|
||
|
|
| `saveLayoutV2()` | 레이아웃 전체 저장 | 화면 → DB |
|
||
|
|
|
||
|
|
#### 변환 함수
|
||
|
|
|
||
|
|
| 함수명 | 역할 |
|
||
|
|
|--------|------|
|
||
|
|
| `convertV2ToLegacy()` | V2 → Legacy 변환 (하위 호환) |
|
||
|
|
| `convertLegacyToV2()` | Legacy → V2 변환 |
|
||
|
|
| `isValidV2Layout()` | V2 레이아웃인지 검증 |
|
||
|
|
| `isLegacyLayout()` | 레거시 레이아웃인지 확인 |
|
||
|
|
|
||
|
|
### 8-2. 공통 Zod 스키마
|
||
|
|
|
||
|
|
**파일 위치:** `frontend/lib/schemas/componentConfig.ts`
|
||
|
|
|
||
|
|
#### 핵심 스키마 (필수!)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 컴포넌트 기본 구조
|
||
|
|
export const componentV2Schema = z.object({
|
||
|
|
id: z.string(),
|
||
|
|
url: z.string(),
|
||
|
|
position: z.object({ x: z.number(), y: z.number() }),
|
||
|
|
size: z.object({ width: z.number(), height: z.number() }),
|
||
|
|
displayOrder: z.number().default(0),
|
||
|
|
overrides: z.record(z.string(), z.any()).default({}),
|
||
|
|
});
|
||
|
|
|
||
|
|
// 레이아웃 기본 구조
|
||
|
|
export const layoutV2Schema = z.object({
|
||
|
|
version: z.string().default("2.0"),
|
||
|
|
components: z.array(componentV2Schema).default([]),
|
||
|
|
updatedAt: z.string().optional(),
|
||
|
|
screenResolution: z.object({...}).optional(),
|
||
|
|
gridSettings: z.any().optional(),
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 컴포넌트별 overrides 스키마 (25개+)
|
||
|
|
|
||
|
|
| 스키마명 | 컴포넌트 | 주요 기본값 |
|
||
|
|
|----------|----------|------------|
|
||
|
|
| `v2TableListOverridesSchema` | 테이블 리스트 | displayMode: "table", pageSize: 20 |
|
||
|
|
| `v2ButtonPrimaryOverridesSchema` | 버튼 | text: "저장", variant: "primary" |
|
||
|
|
| `v2SplitPanelLayoutOverridesSchema` | 분할 레이아웃 | splitRatio: 30, resizable: true |
|
||
|
|
| `v2SectionCardOverridesSchema` | 섹션 카드 | padding: "md", collapsible: false |
|
||
|
|
| `v2TabsWidgetOverridesSchema` | 탭 위젯 | orientation: "horizontal" |
|
||
|
|
| `v2RepeaterOverridesSchema` | 리피터 | renderMode: "inline" |
|
||
|
|
|
||
|
|
#### 스키마 레지스트리 (자동 매핑)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const componentOverridesSchemaRegistry = {
|
||
|
|
"v2-table-list": v2TableListOverridesSchema,
|
||
|
|
"v2-button-primary": v2ButtonPrimaryOverridesSchema,
|
||
|
|
"v2-split-panel-layout": v2SplitPanelLayoutOverridesSchema,
|
||
|
|
// ... 25개+ 컴포넌트
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8-3. 공통 타입 정의
|
||
|
|
|
||
|
|
**파일 위치:** `frontend/types/v2-core.ts`, `frontend/types/v2-components.ts`
|
||
|
|
|
||
|
|
#### 핵심 공통 타입 (v2-core.ts)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 웹 입력 타입
|
||
|
|
export type WebType =
|
||
|
|
| "text" | "textarea" | "email" | "tel" | "url"
|
||
|
|
| "number" | "decimal"
|
||
|
|
| "date" | "datetime"
|
||
|
|
| "select" | "dropdown" | "radio" | "checkbox" | "boolean"
|
||
|
|
| "code" | "entity" | "file" | "image" | "button"
|
||
|
|
| "container" | "group" | "list" | "tree" | "custom";
|
||
|
|
|
||
|
|
// 버튼 액션 타입
|
||
|
|
export type ButtonActionType =
|
||
|
|
| "save" | "cancel" | "delete" | "edit" | "copy" | "add"
|
||
|
|
| "search" | "reset" | "submit"
|
||
|
|
| "close" | "popup" | "modal"
|
||
|
|
| "navigate" | "newWindow"
|
||
|
|
| "control" | "transferData" | "quickInsert";
|
||
|
|
|
||
|
|
// 위치/크기
|
||
|
|
export interface Position { x: number; y: number; z?: number; }
|
||
|
|
export interface Size { width: number; height: number; }
|
||
|
|
|
||
|
|
// 공통 스타일
|
||
|
|
export interface CommonStyle {
|
||
|
|
margin?: string;
|
||
|
|
padding?: string;
|
||
|
|
border?: string;
|
||
|
|
backgroundColor?: string;
|
||
|
|
color?: string;
|
||
|
|
fontSize?: string;
|
||
|
|
// ... 30개+ 속성
|
||
|
|
}
|
||
|
|
|
||
|
|
// 유효성 검사
|
||
|
|
export interface ValidationRule {
|
||
|
|
type: "required" | "minLength" | "maxLength" | "pattern" | "min" | "max" | "email" | "url";
|
||
|
|
value?: unknown;
|
||
|
|
message: string;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### V2 컴포넌트 타입 (v2-components.ts)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 10개 통합 컴포넌트 타입
|
||
|
|
export type V2ComponentType =
|
||
|
|
| "V2Input" | "V2Select" | "V2Date" | "V2Text" | "V2Media"
|
||
|
|
| "V2List" | "V2Layout" | "V2Group" | "V2Biz" | "V2Hierarchy";
|
||
|
|
|
||
|
|
// 공통 속성
|
||
|
|
export interface V2BaseProps {
|
||
|
|
id: string;
|
||
|
|
label?: string;
|
||
|
|
required?: boolean;
|
||
|
|
readonly?: boolean;
|
||
|
|
disabled?: boolean;
|
||
|
|
tableName?: string;
|
||
|
|
columnName?: string;
|
||
|
|
position?: Position;
|
||
|
|
size?: Size;
|
||
|
|
style?: CommonStyle;
|
||
|
|
validation?: ValidationRule[];
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8-4. POP 통합 시 공유/분리 기준
|
||
|
|
|
||
|
|
#### 반드시 공유 (그대로 사용)
|
||
|
|
|
||
|
|
| 구분 | 파일/요소 | 이유 |
|
||
|
|
|------|----------|------|
|
||
|
|
| **유틸리티** | `deepMerge`, `extractCustomConfig`, `mergeComponentConfig` | 저장/로드 로직 동일 |
|
||
|
|
| **스키마** | `componentV2Schema`, `layoutV2Schema` | JSON 구조 동일 |
|
||
|
|
| **타입** | `Position`, `Size`, `WebType`, `ButtonActionType` | 기본 구조 동일 |
|
||
|
|
|
||
|
|
#### POP 전용으로 분리
|
||
|
|
|
||
|
|
| 구분 | 파일/요소 | 이유 |
|
||
|
|
|------|----------|------|
|
||
|
|
| **overrides 스키마** | `popCardListOverridesSchema` 등 | POP 컴포넌트 전용 기본값 |
|
||
|
|
| **스키마 레지스트리** | `popComponentOverridesSchemaRegistry` | POP 컴포넌트 매핑 |
|
||
|
|
| **기본값 레지스트리** | `popComponentDefaultsRegistry` | POP 컴포넌트 기본값 |
|
||
|
|
|
||
|
|
### 8-5. 추천 폴더 구조 (공유 분리)
|
||
|
|
|
||
|
|
```
|
||
|
|
frontend/lib/schemas/
|
||
|
|
├── componentConfig.ts # 기존 (데스크톱)
|
||
|
|
├── popComponentConfig.ts # 신규 (POP) - 구조는 동일
|
||
|
|
└── shared/ # 신규 (공유) - 향후 통합 시
|
||
|
|
├── baseSchemas.ts # componentV2Schema, layoutV2Schema
|
||
|
|
├── mergeUtils.ts # deepMerge, extractCustomConfig 등
|
||
|
|
└── types.ts # Position, Size 등
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. 작업 우선순위
|
||
|
|
|
||
|
|
### [ ] 1단계: 데이터베이스
|
||
|
|
|
||
|
|
- [ ] `screen_layouts_pop` 테이블 생성 마이그레이션 작성
|
||
|
|
- [ ] 마이그레이션 실행 및 검증
|
||
|
|
|
||
|
|
### [ ] 2단계: 백엔드 API
|
||
|
|
|
||
|
|
- [ ] `screenManagementService.ts`에 POP 함수 추가
|
||
|
|
- [ ] `getScreenLayoutPop()`
|
||
|
|
- [ ] `saveLayoutPop()`
|
||
|
|
- [ ] `deleteLayoutPop()`
|
||
|
|
- [ ] `screenManagementRoutes.ts`에 엔드포인트 추가
|
||
|
|
- [ ] `GET /screens/:screenId/layout-pop`
|
||
|
|
- [ ] `POST /screens/:screenId/layout-pop`
|
||
|
|
- [ ] `DELETE /screens/:screenId/layout-pop`
|
||
|
|
|
||
|
|
### [ ] 3단계: 프론트엔드 기반
|
||
|
|
|
||
|
|
- [ ] `lib/api/screen.ts`에 POP API 함수 추가
|
||
|
|
- [ ] `getLayoutPop()`
|
||
|
|
- [ ] `saveLayoutPop()`
|
||
|
|
- [ ] `lib/registry/PopComponentRegistry.ts` 생성
|
||
|
|
- [ ] `lib/schemas/popComponentConfig.ts` 생성
|
||
|
|
- [ ] `lib/utils/layoutPopConverter.ts` 생성
|
||
|
|
|
||
|
|
### [ ] 4단계: POP 컴포넌트 개발
|
||
|
|
|
||
|
|
- [ ] `lib/registry/pop-components/` 폴더 구조 생성
|
||
|
|
- [ ] 기본 컴포넌트 개발
|
||
|
|
- [ ] `pop-card-list` (카드형 리스트)
|
||
|
|
- [ ] `pop-touch-button` (터치 버튼)
|
||
|
|
- [ ] `pop-scanner-input` (스캐너 입력)
|
||
|
|
- [ ] `pop-status-badge` (상태 배지)
|
||
|
|
|
||
|
|
### [ ] 5단계: POP 화면 페이지
|
||
|
|
|
||
|
|
- [ ] `app/(pop)/screens/[screenId]/page.tsx` 생성
|
||
|
|
- [ ] `components/pop/PopDynamicRenderer.tsx` 생성
|
||
|
|
- [ ] `app/(pop)/layout.tsx` 수정 (POP 전용 스타일)
|
||
|
|
|
||
|
|
### [ ] 6단계: POP 화면 디자이너 (선택)
|
||
|
|
|
||
|
|
- [ ] `components/pop/PopScreenDesigner.tsx` 생성
|
||
|
|
- [ ] `components/pop/PopPreview.tsx` 생성
|
||
|
|
- [ ] 관리자 메뉴에 POP 화면 설계 기능 추가
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. 참고 파일 위치
|
||
|
|
|
||
|
|
### 데스크톱 참고 파일 (기존)
|
||
|
|
|
||
|
|
| 구분 | 파일 경로 |
|
||
|
|
|------|----------|
|
||
|
|
| 화면 페이지 | `frontend/app/(main)/screens/[screenId]/page.tsx` |
|
||
|
|
| 컴포넌트 레지스트리 | `frontend/lib/registry/ComponentRegistry.ts` |
|
||
|
|
| 동적 렌더러 | `frontend/lib/registry/DynamicComponentRenderer.tsx` |
|
||
|
|
| Zod 스키마 | `frontend/lib/schemas/componentConfig.ts` |
|
||
|
|
| 레이아웃 변환기 | `frontend/lib/utils/layoutV2Converter.ts` |
|
||
|
|
| 화면 API | `frontend/lib/api/screen.ts` |
|
||
|
|
| 백엔드 서비스 | `backend-node/src/services/screenManagementService.ts` |
|
||
|
|
| 백엔드 라우트 | `backend-node/src/routes/screenManagementRoutes.ts` |
|
||
|
|
|
||
|
|
### 관련 문서
|
||
|
|
|
||
|
|
| 문서 | 경로 |
|
||
|
|
|------|------|
|
||
|
|
| V2 아키텍처 | `docs/DDD1542/COMPONENT_LAYOUT_V2_ARCHITECTURE.md` |
|
||
|
|
| 화면관리 설계 | `docs/kjs/화면관리_시스템_설계.md` |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. 주의사항
|
||
|
|
|
||
|
|
### 멀티테넌시
|
||
|
|
|
||
|
|
- 모든 테이블에 `company_code` 필수
|
||
|
|
- 모든 쿼리에 `company_code` 필터링 적용
|
||
|
|
- 최고 관리자(`company_code = "*"`)는 모든 데이터 조회 가능
|
||
|
|
|
||
|
|
### 충돌 방지
|
||
|
|
|
||
|
|
- 기존 데스크톱 파일 수정 최소화
|
||
|
|
- POP 전용 폴더/파일에서 작업
|
||
|
|
- 공통 로직은 별도 유틸리티로 분리
|
||
|
|
|
||
|
|
### 테스트
|
||
|
|
|
||
|
|
- 데스크톱 기능 회귀 테스트 필수
|
||
|
|
- POP 반응형 테스트 (모바일/태블릿)
|
||
|
|
- 멀티테넌시 격리 테스트
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 변경 이력
|
||
|
|
|
||
|
|
| 날짜 | 버전 | 내용 |
|
||
|
|
|------|------|------|
|
||
|
|
| 2026-01-29 | 1.0 | 초기 계획서 작성 |
|
||
|
|
| 2026-01-29 | 1.1 | V2 공통 요소 (통합 핵심) 섹션 추가 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 작성자
|
||
|
|
|
||
|
|
- 작성일: 2026-01-29
|
||
|
|
- 프로젝트: Vexplor POP 화면 시스템
|