274 lines
7.2 KiB
Markdown
274 lines
7.2 KiB
Markdown
# POP 화면 시스템 아키텍처
|
|
|
|
**최종 업데이트: 2026-02-05 (v5 그리드 시스템)**
|
|
|
|
POP(Point of Production) 화면은 모바일/태블릿 환경에 최적화된 터치 기반 화면 시스템입니다.
|
|
|
|
---
|
|
|
|
## 현재 버전: v5 (CSS Grid)
|
|
|
|
| 항목 | v5 (현재) |
|
|
|------|----------|
|
|
| 레이아웃 | CSS Grid |
|
|
| 배치 방식 | 좌표 기반 (col, row, colSpan, rowSpan) |
|
|
| 모드 | 4개 (mobile_portrait, mobile_landscape, tablet_portrait, tablet_landscape) |
|
|
| 칸 수 | 4/6/8/12칸 |
|
|
|
|
---
|
|
|
|
## 폴더 구조
|
|
|
|
```
|
|
frontend/
|
|
├── app/(pop)/ # Next.js App Router
|
|
│ ├── layout.tsx # POP 전용 레이아웃
|
|
│ └── pop/
|
|
│ ├── page.tsx # 대시보드
|
|
│ ├── screens/[screenId]/ # 화면 뷰어 (v5)
|
|
│ └── work/ # 작업 화면
|
|
│
|
|
├── components/pop/ # POP 컴포넌트
|
|
│ ├── designer/ # 디자이너 모듈 ★
|
|
│ │ ├── PopDesigner.tsx # 메인 (레이아웃 로드/저장)
|
|
│ │ ├── PopCanvas.tsx # 캔버스 (DnD, 줌, 모드)
|
|
│ │ ├── panels/
|
|
│ │ │ └── ComponentEditorPanel.tsx # 속성 편집
|
|
│ │ ├── renderers/
|
|
│ │ │ └── PopRenderer.tsx # CSS Grid 렌더링
|
|
│ │ ├── types/
|
|
│ │ │ └── pop-layout.ts # v5 타입 정의
|
|
│ │ └── utils/
|
|
│ │ └── gridUtils.ts # 위치 계산
|
|
│ ├── management/ # 화면 관리
|
|
│ └── dashboard/ # 대시보드
|
|
│
|
|
└── lib/
|
|
├── api/screen.ts # 화면 API
|
|
└── registry/ # 컴포넌트 레지스트리
|
|
```
|
|
|
|
---
|
|
|
|
## 핵심 파일
|
|
|
|
### 1. PopDesigner.tsx (메인)
|
|
|
|
**역할**: 레이아웃 로드/저장, 컴포넌트 CRUD, 히스토리
|
|
|
|
```typescript
|
|
// 상태 관리
|
|
const [layout, setLayout] = useState<PopLayoutDataV5>(createEmptyPopLayoutV5());
|
|
const [selectedComponentId, setSelectedComponentId] = useState<string | null>(null);
|
|
const [currentMode, setCurrentMode] = useState<GridMode>("tablet_landscape");
|
|
const [history, setHistory] = useState<PopLayoutDataV5[]>([]);
|
|
|
|
// 핵심 함수
|
|
handleSave() // 레이아웃 저장
|
|
handleAddComponent() // 컴포넌트 추가
|
|
handleUpdateComponent() // 컴포넌트 수정
|
|
handleDeleteComponent() // 컴포넌트 삭제
|
|
handleUndo() / handleRedo() // 히스토리
|
|
```
|
|
|
|
### 2. PopCanvas.tsx (캔버스)
|
|
|
|
**역할**: 그리드 캔버스, DnD, 줌, 패닝, 모드 전환
|
|
|
|
```typescript
|
|
// DnD 설정
|
|
const DND_ITEM_TYPES = { COMPONENT: "component" };
|
|
|
|
// 뷰포트 프리셋 (4개 모드)
|
|
const VIEWPORT_PRESETS = [
|
|
{ id: "mobile_portrait", width: 375, columns: 4 },
|
|
{ id: "mobile_landscape", width: 667, columns: 6 },
|
|
{ id: "tablet_portrait", width: 768, columns: 8 },
|
|
{ id: "tablet_landscape", width: 1024, columns: 12 },
|
|
];
|
|
|
|
// 기능
|
|
- useDrop(): 팔레트에서 컴포넌트 드롭
|
|
- handleWheel(): 줌 (30%~150%)
|
|
- Space + 드래그: 패닝
|
|
```
|
|
|
|
### 3. PopRenderer.tsx (렌더러)
|
|
|
|
**역할**: CSS Grid 기반 레이아웃 렌더링
|
|
|
|
```typescript
|
|
// Props
|
|
interface PopRendererProps {
|
|
layout: PopLayoutDataV5;
|
|
viewportWidth: number;
|
|
currentMode: GridMode;
|
|
isDesignMode: boolean;
|
|
selectedComponentId?: string | null;
|
|
onSelectComponent?: (id: string | null) => void;
|
|
}
|
|
|
|
// CSS Grid 스타일 생성
|
|
const gridStyle = useMemo(() => ({
|
|
display: "grid",
|
|
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
gridTemplateRows: `repeat(${rows}, 1fr)`,
|
|
gap: `${gap}px`,
|
|
padding: `${padding}px`,
|
|
}), [mode]);
|
|
|
|
// 위치 변환 (12칸 → 다른 모드)
|
|
const convertPosition = (pos: PopGridPosition, targetMode: GridMode) => {
|
|
const ratio = GRID_BREAKPOINTS[targetMode].columns / 12;
|
|
return {
|
|
col: Math.max(1, Math.round(pos.col * ratio)),
|
|
colSpan: Math.max(1, Math.round(pos.colSpan * ratio)),
|
|
row: pos.row,
|
|
rowSpan: pos.rowSpan,
|
|
};
|
|
};
|
|
```
|
|
|
|
### 4. ComponentEditorPanel.tsx (속성 패널)
|
|
|
|
**역할**: 선택된 컴포넌트 속성 편집
|
|
|
|
```typescript
|
|
// 탭 구조
|
|
- grid: col, row, colSpan, rowSpan (기본 모드에서만 편집)
|
|
- settings: label, type 등
|
|
- data: 데이터 바인딩 (미구현)
|
|
- visibility: 모드별 표시/숨김
|
|
```
|
|
|
|
### 5. pop-layout.ts (타입 정의)
|
|
|
|
**역할**: v5 타입 정의
|
|
|
|
```typescript
|
|
// 그리드 모드
|
|
type GridMode = "mobile_portrait" | "mobile_landscape" | "tablet_portrait" | "tablet_landscape";
|
|
|
|
// 브레이크포인트 설정
|
|
const GRID_BREAKPOINTS = {
|
|
mobile_portrait: { columns: 4, rowHeight: 48, gap: 8, padding: 12 },
|
|
mobile_landscape: { columns: 6, rowHeight: 44, gap: 8, padding: 16 },
|
|
tablet_portrait: { columns: 8, rowHeight: 52, gap: 12, padding: 20 },
|
|
tablet_landscape: { columns: 12, rowHeight: 56, gap: 12, padding: 24 },
|
|
};
|
|
|
|
// 레이아웃 데이터
|
|
interface PopLayoutDataV5 {
|
|
version: "pop-5.0";
|
|
metadata: PopLayoutMetadata;
|
|
gridConfig: PopGridConfig;
|
|
components: PopComponentDefinitionV5[];
|
|
globalSettings: PopGlobalSettingsV5;
|
|
}
|
|
|
|
// 컴포넌트 정의
|
|
interface PopComponentDefinitionV5 {
|
|
id: string;
|
|
type: PopComponentType;
|
|
label: string;
|
|
gridPosition: PopGridPosition; // col, row, colSpan, rowSpan
|
|
config: PopComponentConfig;
|
|
visibility: Record<GridMode, boolean>;
|
|
modeOverrides?: Record<GridMode, PopModeOverrideV5>;
|
|
}
|
|
|
|
// 위치
|
|
interface PopGridPosition {
|
|
col: number; // 시작 열 (1부터)
|
|
row: number; // 시작 행 (1부터)
|
|
colSpan: number; // 열 크기 (1~12)
|
|
rowSpan: number; // 행 크기 (1~)
|
|
}
|
|
```
|
|
|
|
### 6. gridUtils.ts (유틸리티)
|
|
|
|
**역할**: 그리드 위치 계산
|
|
|
|
```typescript
|
|
// 위치 변환
|
|
convertPositionToMode(pos, targetMode)
|
|
|
|
// 겹침 감지
|
|
isOverlapping(posA, posB)
|
|
|
|
// 빈 위치 찾기
|
|
findNextEmptyPosition(layout, mode)
|
|
|
|
// 마우스 → 그리드 좌표
|
|
mouseToGridPosition(mouseX, mouseY, canvasRect, mode)
|
|
```
|
|
|
|
---
|
|
|
|
## 데이터 흐름
|
|
|
|
```
|
|
[사용자 액션]
|
|
↓
|
|
[PopDesigner] ← 상태 관리 (layout, selectedComponentId, history)
|
|
↓
|
|
[PopCanvas] ← DnD, 줌, 모드 전환
|
|
↓
|
|
[PopRenderer] ← CSS Grid 렌더링
|
|
↓
|
|
[컴포넌트 표시]
|
|
```
|
|
|
|
### 저장 흐름
|
|
|
|
```
|
|
[저장 버튼]
|
|
↓
|
|
PopDesigner.handleSave()
|
|
↓
|
|
screenApi.saveLayoutPop(screenId, layout)
|
|
↓
|
|
[백엔드] screenManagementService.saveLayoutPop()
|
|
↓
|
|
[DB] screen_layouts_pop 테이블
|
|
```
|
|
|
|
### 로드 흐름
|
|
|
|
```
|
|
[페이지 로드]
|
|
↓
|
|
PopDesigner useEffect
|
|
↓
|
|
screenApi.getLayoutPop(screenId)
|
|
↓
|
|
isV5Layout(data) 체크
|
|
↓
|
|
setLayout(data) 또는 createEmptyPopLayoutV5()
|
|
```
|
|
|
|
---
|
|
|
|
## API 엔드포인트
|
|
|
|
| 메서드 | 경로 | 설명 |
|
|
|--------|------|------|
|
|
| GET | `/api/screen-management/layout-pop/:screenId` | 레이아웃 조회 |
|
|
| POST | `/api/screen-management/layout-pop/:screenId` | 레이아웃 저장 |
|
|
|
|
---
|
|
|
|
## 삭제된 레거시 (참고용)
|
|
|
|
| 파일 | 버전 | 이유 |
|
|
|------|------|------|
|
|
| PopCanvasV4.tsx | v4 | Flexbox 기반, v5로 대체 |
|
|
| PopFlexRenderer.tsx | v4 | Flexbox 렌더러, v5로 대체 |
|
|
| PopLayoutRenderer.tsx | v3 | 절대 좌표 기반, v5로 대체 |
|
|
| ComponentEditorPanelV4.tsx | v4 | v5 전용으로 통합 |
|
|
|
|
---
|
|
|
|
*상세 스펙: [SPEC.md](./SPEC.md) | 파일 목록: [FILES.md](./FILES.md)*
|