ERP-node/popdocs/archive/GRID_SYSTEM_PLAN.md

481 lines
13 KiB
Markdown
Raw Normal View History

# POP 그리드 시스템 도입 계획
> 작성일: 2026-02-05
> 상태: 계획 승인, 구현 대기
---
## 개요
### 목표
현재 Flexbox 기반 v4 시스템을 **CSS Grid 기반 v5 시스템**으로 전환하여
4~14인치 화면에서 일관된 배치와 예측 가능한 반응형 레이아웃 구현
### 핵심 변경점
| 항목 | v4 (현재) | v5 (그리드) |
|------|----------|-------------|
| 배치 방식 | Flexbox 흐름 | **Grid 위치 지정** |
| 크기 단위 | 픽셀 (200px) | **칸 (col, row)** |
| 위치 지정 | 순서대로 자동 | **열/행 좌표** |
| 줄바꿈 | 자동 (넘치면) | **명시적 (row 지정)** |
---
## Phase 구조
```
[Phase 5.1] [Phase 5.2] [Phase 5.3] [Phase 5.4]
그리드 타입 정의 → 그리드 렌더러 → 디자이너 UI → 반응형 자동화
1주 1주 1~2주 1주
```
---
## Phase 5.1: 그리드 타입 정의
### 목표
v5 레이아웃 데이터 구조 설계
### 작업 항목
- [ ] `PopLayoutDataV5` 인터페이스 정의
- [ ] `PopGridConfig` 인터페이스 (그리드 설정)
- [ ] `PopComponentPositionV5` 인터페이스 (위치: col, row, colSpan, rowSpan)
- [ ] `PopSizeConstraintV5` 인터페이스 (칸 기반 크기)
- [ ] 브레이크포인트 상수 정의
- [ ] `createEmptyPopLayoutV5()` 생성 함수
- [ ] `isV5Layout()` 타입 가드
### 데이터 구조 설계
```typescript
// v5 레이아웃
interface PopLayoutDataV5 {
version: "pop-5.0";
// 그리드 설정
gridConfig: PopGridConfig;
// 컴포넌트 목록
components: Record<string, PopComponentDefinitionV5>;
// 모드별 오버라이드
overrides?: {
mobile_portrait?: PopModeOverrideV5;
mobile_landscape?: PopModeOverrideV5;
tablet_portrait?: PopModeOverrideV5;
};
// 기존 호환
dataFlow: PopDataFlow;
settings: PopGlobalSettingsV5;
}
// 그리드 설정
interface PopGridConfig {
// 모드별 칸 수
columns: {
tablet_landscape: 12; // 기본 (10~14인치)
tablet_portrait: 8; // 8~10인치 세로
mobile_landscape: 6; // 6~8인치 가로
mobile_portrait: 4; // 4~6인치 세로
};
// 행 높이 (px) - 1행의 기본 높이
rowHeight: number; // 기본 48px
// 간격
gap: number; // 기본 8px
padding: number; // 기본 16px
}
// 컴포넌트 정의
interface PopComponentDefinitionV5 {
id: string;
type: PopComponentType;
label?: string;
// 위치 (열/행 좌표) - 기본 모드(태블릿 가로) 기준
position: {
col: number; // 시작 열 (1부터)
row: number; // 시작 행 (1부터)
colSpan: number; // 차지할 열 수 (1~12)
rowSpan: number; // 차지할 행 수 (1~)
};
// 모드별 표시/숨김
visibility?: {
tablet_landscape?: boolean;
tablet_portrait?: boolean;
mobile_landscape?: boolean;
mobile_portrait?: boolean;
};
// 기존 속성
dataBinding?: PopDataBinding;
config?: PopComponentConfig;
}
```
### 브레이크포인트 정의
```typescript
// 브레이크포인트 상수
const GRID_BREAKPOINTS = {
// 4~6인치 모바일 세로
mobile_portrait: {
maxWidth: 599,
columns: 4,
rowHeight: 40,
gap: 8,
padding: 12,
},
// 6~8인치 모바일 가로 / 작은 태블릿
mobile_landscape: {
minWidth: 600,
maxWidth: 839,
columns: 6,
rowHeight: 44,
gap: 8,
padding: 16,
},
// 8~10인치 태블릿 세로
tablet_portrait: {
minWidth: 840,
maxWidth: 1023,
columns: 8,
rowHeight: 48,
gap: 12,
padding: 16,
},
// 10~14인치 태블릿 가로 (기본)
tablet_landscape: {
minWidth: 1024,
columns: 12,
rowHeight: 48,
gap: 16,
padding: 24,
},
} as const;
```
### 산출물
- `frontend/components/pop/designer/types/pop-layout-v5.ts`
---
## Phase 5.2: 그리드 렌더러
### 목표
CSS Grid 기반 렌더러 구현
### 작업 항목
- [ ] `PopGridRenderer.tsx` 생성
- [ ] CSS Grid 스타일 계산 로직
- [ ] 브레이크포인트 감지 및 칸 수 자동 변경
- [ ] 컴포넌트 위치 렌더링 (grid-column, grid-row)
- [ ] 모드별 자동 위치 재계산 (12칸→4칸 변환)
- [ ] visibility 처리
- [ ] 기존 PopFlexRenderer와 공존
### 렌더링 로직
```typescript
// CSS Grid 스타일 생성
function calculateGridStyle(config: PopGridConfig, mode: string): React.CSSProperties {
const columns = config.columns[mode];
const { rowHeight, gap, padding } = config;
return {
display: 'grid',
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gridAutoRows: `${rowHeight}px`,
gap: `${gap}px`,
padding: `${padding}px`,
};
}
// 컴포넌트 위치 스타일
function calculatePositionStyle(
position: PopComponentPositionV5['position'],
sourceColumns: number, // 원본 모드 칸 수 (12)
targetColumns: number // 현재 모드 칸 수 (4)
): React.CSSProperties {
// 12칸 → 4칸 변환 예시
// col: 7, colSpan: 3 → col: 3, colSpan: 1
const ratio = targetColumns / sourceColumns;
const newCol = Math.max(1, Math.ceil(position.col * ratio));
const newColSpan = Math.max(1, Math.round(position.colSpan * ratio));
return {
gridColumn: `${newCol} / span ${Math.min(newColSpan, targetColumns - newCol + 1)}`,
gridRow: `${position.row} / span ${position.rowSpan}`,
};
}
```
### 산출물
- `frontend/components/pop/designer/renderers/PopGridRenderer.tsx`
---
## Phase 5.3: 디자이너 UI
### 목표
그리드 기반 편집 UI 구현
### 작업 항목
- [ ] `PopCanvasV5.tsx` 생성 (그리드 캔버스)
- [ ] 그리드 배경 표시 (바둑판 모양)
- [ ] 컴포넌트 드래그 배치 (칸에 스냅)
- [ ] 컴포넌트 리사이즈 (칸 단위)
- [ ] 위치 편집 패널 (col, row, colSpan, rowSpan)
- [ ] 모드 전환 시 그리드 칸 수 변경 표시
- [ ] v4/v5 자동 판별 및 전환
### UI 구조
```
┌─────────────────────────────────────────────────────────────────┐
│ ← 목록 화면명 *변경됨 [↶][↷] 그리드 레이아웃 (v5) [저장] │
├─────────────────────────────────────────────────────────────────┤
│ 미리보기: [모바일↕ 4칸] [모바일↔ 6칸] [태블릿↕ 8칸] [태블릿↔ 12칸] │
├────────────┬────────────────────────────────────┬───────────────┤
│ │ 1 2 3 4 5 6 ... 12 │ │
│ 컴포넌트 │ ┌───────────┬───────────┐ │ 위치 │
│ │1│ A │ B │ │ 열: [1-12] │
│ 필드 │ ├───────────┴───────────┤ │ 행: [1-99] │
│ 버튼 │2│ C │ │ 너비: [1-12]│
│ 리스트 │ ├───────────┬───────────┤ │ 높이: [1-10]│
│ 인디케이터 │3│ D │ E │ │ │
│ ... │ └───────────┴───────────┘ │ 표시 설정 │
│ │ │ [x] 태블릿↔ │
│ │ (그리드 배경 표시) │ [x] 모바일↕ │
└────────────┴────────────────────────────────────┴───────────────┘
```
### 드래그 앤 드롭 로직
```typescript
// 마우스 위치 → 그리드 좌표 변환
function mouseToGridPosition(
mouseX: number,
mouseY: number,
gridConfig: PopGridConfig,
canvasRect: DOMRect
): { col: number; row: number } {
const { columns, rowHeight, gap, padding } = gridConfig;
// 캔버스 내 상대 위치
const relX = mouseX - canvasRect.left - padding;
const relY = mouseY - canvasRect.top - padding;
// 칸 너비 계산
const totalGap = gap * (columns - 1);
const colWidth = (canvasRect.width - padding * 2 - totalGap) / columns;
// 그리드 좌표 계산
const col = Math.max(1, Math.min(columns, Math.floor(relX / (colWidth + gap)) + 1));
const row = Math.max(1, Math.floor(relY / (rowHeight + gap)) + 1);
return { col, row };
}
```
### 산출물
- `frontend/components/pop/designer/PopCanvasV5.tsx`
- `frontend/components/pop/designer/panels/ComponentEditorPanelV5.tsx`
---
## Phase 5.4: 반응형 자동화
### 목표
모드 전환 시 자동 레이아웃 조정
### 작업 항목
- [ ] 12칸 → 4칸 자동 변환 알고리즘
- [ ] 겹침 감지 및 자동 재배치
- [ ] 모드별 오버라이드 저장
- [ ] "자동 배치" vs "수동 고정" 선택
- [ ] 변환 미리보기
### 자동 변환 알고리즘
```typescript
// 12칸 → 4칸 변환 전략
function convertLayoutToMode(
components: PopComponentDefinitionV5[],
sourceMode: 'tablet_landscape', // 12칸
targetMode: 'mobile_portrait' // 4칸
): PopComponentDefinitionV5[] {
const sourceColumns = 12;
const targetColumns = 4;
const ratio = targetColumns / sourceColumns; // 0.333
// 1. 각 컴포넌트 위치 변환
const converted = components.map(comp => {
const newCol = Math.max(1, Math.ceil(comp.position.col * ratio));
const newColSpan = Math.max(1, Math.round(comp.position.colSpan * ratio));
return {
...comp,
position: {
...comp.position,
col: newCol,
colSpan: Math.min(newColSpan, targetColumns),
},
};
});
// 2. 겹침 감지 및 해결
return resolveOverlaps(converted, targetColumns);
}
// 겹침 해결 (아래로 밀기)
function resolveOverlaps(
components: PopComponentDefinitionV5[],
columns: number
): PopComponentDefinitionV5[] {
// 행 단위로 그리드 점유 상태 추적
const grid: boolean[][] = [];
// row 순서대로 처리
const sorted = [...components].sort((a, b) =>
a.position.row - b.position.row || a.position.col - b.position.col
);
return sorted.map(comp => {
let { row, col, colSpan, rowSpan } = comp.position;
// 배치 가능한 위치 찾기
while (isOccupied(grid, row, col, colSpan, rowSpan, columns)) {
row++; // 아래로 이동
}
// 그리드에 표시
markOccupied(grid, row, col, colSpan, rowSpan);
return {
...comp,
position: { row, col, colSpan, rowSpan },
};
});
}
```
### 산출물
- `frontend/components/pop/designer/utils/gridLayoutUtils.ts`
---
## 마이그레이션 전략
### v4 → v5 변환
```typescript
function migrateV4ToV5(layoutV4: PopLayoutDataV4): PopLayoutDataV5 {
const componentsList = Object.values(layoutV4.components);
// Flexbox 순서 → Grid 위치 변환
let currentRow = 1;
let currentCol = 1;
const columns = 12;
const componentsV5: Record<string, PopComponentDefinitionV5> = {};
componentsList.forEach((comp, index) => {
// 기본 크기 추정 (픽셀 → 칸)
const colSpan = Math.max(1, Math.round((comp.size.fixedWidth || 100) / 85));
const rowSpan = Math.max(1, Math.round((comp.size.fixedHeight || 48) / 48));
// 줄바꿈 체크
if (currentCol + colSpan - 1 > columns) {
currentRow++;
currentCol = 1;
}
componentsV5[comp.id] = {
...comp,
position: {
col: currentCol,
row: currentRow,
colSpan,
rowSpan,
},
};
currentCol += colSpan;
});
return {
version: "pop-5.0",
gridConfig: { /* 기본값 */ },
components: componentsV5,
dataFlow: layoutV4.dataFlow,
settings: { /* 변환 */ },
};
}
```
### 하위 호환
| 버전 | 처리 방식 |
|------|----------|
| v1~v2 | v3로 변환 후 v5로 |
| v3 | v5로 직접 변환 |
| v4 | v5로 직접 변환 |
| v5 | 그대로 사용 |
---
## 일정 (예상)
| Phase | 작업 | 예상 기간 |
|-------|------|----------|
| 5.1 | 타입 정의 | 2~3일 |
| 5.2 | 그리드 렌더러 | 3~5일 |
| 5.3 | 디자이너 UI | 5~7일 |
| 5.4 | 반응형 자동화 | 3~5일 |
| - | 테스트 및 버그 수정 | 2~3일 |
| **총** | | **약 2~3주** |
---
## 리스크 및 대응
| 리스크 | 영향 | 대응 |
|--------|------|------|
| 기존 v4 화면 깨짐 | 높음 | 하위 호환 유지, v4 렌더러 보존 |
| 자동 변환 품질 | 중간 | 수동 오버라이드로 보완 |
| 드래그 UX 복잡 | 중간 | 스냅 기능으로 편의성 확보 |
| 성능 저하 | 낮음 | CSS Grid는 네이티브 성능 |
---
## 성공 기준
1. **배치 예측 가능**: "2열 3행"이라고 하면 정확히 그 위치에 표시
2. **일관된 디자인**: 12칸 → 4칸 전환 시 비율 유지
3. **쉬운 편집**: 드래그로 칸에 스냅되어 배치
4. **하위 호환**: 기존 v4 화면이 정상 동작
---
## 관련 문서
- [GRID_SYSTEM_DESIGN.md](./GRID_SYSTEM_DESIGN.md) - 그리드 시스템 설계 상세
- [PLAN.md](./PLAN.md) - 전체 POP 개발 계획
- [V4_UNIFIED_DESIGN_SPEC.md](./V4_UNIFIED_DESIGN_SPEC.md) - 현재 v4 스펙
---
*최종 업데이트: 2026-02-05*