# POP v4.0 제약조건 기반 시스템 구현 계획 ## 1. 현재 시스템 분석 ### 1.1 현재 구조 (v3.0) ```typescript // 4개 모드별 그리드 위치 기반 interface PopLayoutDataV3 { version: "pop-3.0"; layouts: { tablet_landscape: { componentPositions: Record }; tablet_portrait: { componentPositions: Record }; mobile_landscape: { componentPositions: Record }; mobile_portrait: { componentPositions: Record }; }; components: Record; // ... } interface GridPosition { col: number; // 1-based row: number; // 1-based colSpan: number; rowSpan: number; } ``` ### 1.2 현재 문제점 1. **4배 작업량**: 4개 모드 각각 설계 필요 2. **수동 동기화**: 컴포넌트 추가/삭제 시 4모드 수동 동기화 3. **그리드 한계**: col/row 기반이라 자동 재배치 불가 4. **반응형 미흡**: 화면 크기 변화에 자동 적응 불가 5. **디바이스 차이 무시**: 태블릿/모바일 물리적 크기 차이 고려 안됨 --- ## 2. 새로운 시스템 설계 (v4.0) ### 2.1 핵심 철학 ``` "하나의 레이아웃 설계 → 제약조건 설정 → 모든 화면 자동 적응" ``` - **단일 소스**: 1개 레이아웃만 설계 - **제약조건 기반**: 컴포넌트가 "어떻게 반응할지" 규칙 정의 - **Flexbox 렌더링**: CSS Grid에서 Flexbox 기반으로 전환 - **자동 줄바꿈**: 공간 부족 시 자동 재배치 ### 2.2 새로운 데이터 구조 ```typescript // v4.0 레이아웃 interface PopLayoutDataV4 { version: "pop-4.0"; // 루트 컨테이너 root: PopContainer; // 컴포넌트 정의 (ID → 정의) components: Record; // 데이터 흐름 dataFlow: PopDataFlow; // 전역 설정 settings: PopGlobalSettingsV4; // 메타데이터 metadata?: PopLayoutMetadata; } ``` ### 2.3 컨테이너 (스택) ```typescript // 컨테이너: 컴포넌트들을 담는 그룹 interface PopContainer { id: string; type: "stack"; // 스택 방향 direction: "horizontal" | "vertical"; // 줄바꿈 허용 wrap: boolean; // 요소 간 간격 gap: number; // 정렬 alignItems: "start" | "center" | "end" | "stretch"; justifyContent: "start" | "center" | "end" | "space-between" | "space-around"; // 패딩 padding?: { top: number; right: number; bottom: number; left: number; }; // 반응형 규칙 (선택) responsive?: { // 브레이크포인트 (이 너비 이하에서 적용) breakpoint: number; // 변경할 방향 direction?: "horizontal" | "vertical"; // 변경할 간격 gap?: number; }[]; // 자식 요소 (컴포넌트 ID 또는 중첩 컨테이너) children: (string | PopContainer)[]; } ``` ### 2.4 컴포넌트 제약조건 ```typescript interface PopComponentDefinitionV4 { id: string; type: PopComponentType; label?: string; // ===== 크기 제약 (핵심) ===== size: { // 너비 모드 width: "fixed" | "fill" | "hug"; // 높이 모드 height: "fixed" | "fill" | "hug"; // 고정 크기 (width/height가 fixed일 때) fixedWidth?: number; fixedHeight?: number; // 최소/최대 크기 minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number; // 비율 (fill일 때, 같은 컨테이너 내 다른 요소와의 비율) flexGrow?: number; // 기본 1 flexShrink?: number; // 기본 1 }; // ===== 정렬 ===== alignSelf?: "start" | "center" | "end" | "stretch"; // ===== 여백 ===== margin?: { top: number; right: number; bottom: number; left: number; }; // ===== 모바일 스케일 (선택) ===== // 모바일에서 컴포넌트를 더 크게 표시 mobileScale?: number; // 기본 1.0, 예: 1.2 = 20% 더 크게 // ===== 기존 속성 ===== dataBinding?: PopDataBinding; style?: PopStylePreset; config?: PopComponentConfig; } ``` ### 2.5 크기 모드 설명 | 모드 | 설명 | CSS 변환 | |------|------|----------| | `fixed` | 고정 크기 (px) | `width: {fixedWidth}px` | | `fill` | 부모 공간 채우기 | `flex: {flexGrow} {flexShrink} 0` | | `hug` | 내용에 맞춤 | `flex: 0 0 auto` | ### 2.6 전역 설정 ```typescript interface PopGlobalSettingsV4 { // 기본 터치 타겟 크기 touchTargetMin: number; // 48px // 모드 (일반/산업현장) mode: "normal" | "industrial"; // 기본 간격 defaultGap: number; // 8px // 기본 패딩 defaultPadding: number; // 16px // 반응형 브레이크포인트 (전역) breakpoints: { tablet: number; // 768px mobile: number; // 480px }; } ``` --- ## 3. 디자이너 UI 변경 ### 3.1 기존 디자이너 vs 새 디자이너 ``` 기존 (그리드 기반): ┌──────────────────────────────────────────────┐ │ [태블릿 가로] [태블릿 세로] [모바일 가로] [모바일 세로] │ │ │ │ 24x24 그리드에 컴포넌트 드래그 배치 │ │ │ └──────────────────────────────────────────────┘ 새로운 (제약조건 기반): ┌──────────────────────────────────────────────┐ │ [단일 캔버스] 미리보기: [태블릿▼] │ │ │ │ 스택(컨테이너)에 컴포넌트 배치 │ │ + 우측 패널에서 제약조건 설정 │ │ │ └──────────────────────────────────────────────┘ ``` ### 3.2 새로운 디자이너 레이아웃 ``` ┌─────────────────────────────────────────────────────────────────┐ │ POP 화면 디자이너 v4 [저장] [미리보기] │ ├────────────────┬────────────────────────┬───────────────────────┤ │ │ │ │ │ 컴포넌트 │ 캔버스 │ 속성 패널 │ │ │ │ │ │ ▼ 기본 │ ┌──────────────────┐ │ ▼ 선택됨: 입력창 │ │ [필드] │ │ ┌──────────────┐ │ │ │ │ [버튼] │ │ │입력창 │ │ │ ▼ 크기 │ │ [리스트] │ │ └──────────────┘ │ │ 너비: [채우기 ▼] │ │ [인디케이터] │ │ │ │ 최소: [100] px │ │ │ │ ┌─────┐ ┌─────┐ │ │ 최대: [없음] │ │ ▼ 입력 │ │ │버튼1│ │버튼2│ │ │ │ │ [스캐너] │ │ └─────┘ └─────┘ │ │ 높이: [고정 ▼] │ │ [숫자패드] │ │ │ │ 값: [48] px │ │ │ └──────────────────┘ │ │ │ ───────── │ │ ▼ 정렬 │ │ │ 미리보기: │ [늘이기 ▼] │ │ ▼ 레이아웃 │ ┌──────────────────┐ │ │ │ [스택 (가로)] │ │[태블릿 가로 ▼] │ │ ▼ 여백 │ │ [스택 (세로)] │ │[768px] │ │ 상[8] 우[0] 하[8] 좌[0]│ │ │ └──────────────────┘ │ │ │ │ │ ▼ 반응형 │ │ │ │ 모바일 스케일: [1.2] │ │ │ │ │ └────────────────┴────────────────────────┴───────────────────────┘ ``` ### 3.3 컨테이너(스택) 편집 ``` ┌─ 스택 속성 ─────────────────────┐ │ │ │ 방향: [가로 ▼] │ │ 줄바꿈: [허용 ☑] │ │ 간격: [8] px │ │ │ │ 정렬 (가로): [가운데 ▼] │ │ 정렬 (세로): [늘이기 ▼] │ │ │ │ ▼ 반응형 규칙 │ │ ┌─────────────────────────────┐ │ │ │ 768px 이하: 세로 방향 │ │ │ │ [+ 규칙 추가] │ │ │ └─────────────────────────────┘ │ │ │ └─────────────────────────────────┘ ``` --- ## 4. 렌더링 로직 변경 ### 4.1 기존 렌더링 (CSS Grid) ```typescript // v3: CSS Grid 기반
{componentIds.map(id => (
))}
``` ### 4.2 새로운 렌더링 (Flexbox) ```typescript // v4: Flexbox 기반 function renderContainer(container: PopContainer, components: Record) { const direction = useResponsiveValue(container, 'direction'); const gap = useResponsiveValue(container, 'gap'); return (
{container.children.map(child => { if (typeof child === "string") { // 컴포넌트 렌더링 return renderComponent(components[child]); } else { // 중첩 컨테이너 렌더링 return renderContainer(child, components); } })}
); } function renderComponent(component: PopComponentDefinitionV4) { const { size, margin, mobileScale } = component; const isMobile = useIsMobile(); const scale = isMobile && mobileScale ? mobileScale : 1; // 크기 계산 let width: string; let flex: string; if (size.width === "fixed") { width = `${(size.fixedWidth || 100) * scale}px`; flex = "0 0 auto"; } else if (size.width === "fill") { width = "auto"; flex = `${size.flexGrow || 1} ${size.flexShrink || 1} 0`; } else { // hug width = "auto"; flex = "0 0 auto"; } return (
); } ``` ### 4.3 반응형 훅 ```typescript function useResponsiveValue( container: PopContainer, property: keyof PopContainer ): T { const windowWidth = useWindowWidth(); // 기본값 let value = container[property] as T; // 반응형 규칙 적용 (작은 브레이크포인트 우선) if (container.responsive) { const sortedRules = [...container.responsive].sort((a, b) => b.breakpoint - a.breakpoint); for (const rule of sortedRules) { if (windowWidth <= rule.breakpoint && rule[property] !== undefined) { value = rule[property] as T; } } } return value; } ``` --- ## 5. 구현 단계 ### Phase 1: 데이터 구조 (1-2일) **파일**: `frontend/components/pop/designer/types/pop-layout.ts` 1. `PopLayoutDataV4` 인터페이스 정의 2. `PopContainer` 인터페이스 정의 3. `PopComponentDefinitionV4` 인터페이스 정의 4. `createEmptyPopLayoutV4()` 함수 5. `migrateV3ToV4()` 마이그레이션 함수 6. `ensureV4Layout()` 함수 7. 타입 가드 함수들 ### Phase 2: 렌더러 (2-3일) **파일**: `frontend/components/pop/designer/renderers/PopLayoutRendererV4.tsx` 1. `renderContainer()` 함수 2. `renderComponent()` 함수 3. `useResponsiveValue()` 훅 4. `useWindowWidth()` 훅 5. CSS 스타일 계산 로직 6. 반응형 브레이크포인트 처리 ### Phase 3: 디자이너 UI (3-4일) **파일**: `frontend/components/pop/designer/PopDesignerV4.tsx` 1. 캔버스 영역 (드래그 앤 드롭) 2. 컴포넌트 팔레트 (기존 + 스택) 3. 속성 패널 - 크기 제약 편집 - 정렬 편집 - 여백 편집 - 반응형 규칙 편집 4. 미리보기 모드 (다양한 화면 크기) 5. 컨테이너(스택) 관리 - 컨테이너 추가/삭제 - 컨테이너 설정 편집 - 컴포넌트 이동 (컨테이너 간) ### Phase 4: 뷰어 통합 (1-2일) **파일**: `frontend/app/(pop)/pop/screens/[screenId]/page.tsx` 1. v4 레이아웃 감지 및 렌더링 2. 기존 v3 호환 유지 3. 반응형 모드 감지 연동 4. 성능 최적화 ### Phase 5: 백엔드 수정 (1일) **파일**: `backend-node/src/services/screenManagementService.ts` 1. `saveLayoutPop` - v4 버전 감지 및 저장 2. `getLayoutPop` - v4 버전 반환 3. 버전 마이그레이션 로직 ### Phase 6: 테스트 및 마이그레이션 (2-3일) 1. 단위 테스트 2. 통합 테스트 3. 기존 v3 레이아웃 마이그레이션 도구 4. 크로스 디바이스 테스트 --- ## 6. 마이그레이션 전략 ### 6.1 v3 → v4 자동 변환 ```typescript function migrateV3ToV4(v3: PopLayoutDataV3): PopLayoutDataV4 { // 태블릿 가로 모드 기준으로 변환 const baseLayout = v3.layouts.tablet_landscape; const componentIds = Object.keys(baseLayout.componentPositions); // 컴포넌트를 row, col 순으로 정렬 const sortedIds = componentIds.sort((a, b) => { const posA = baseLayout.componentPositions[a]; const posB = baseLayout.componentPositions[b]; if (posA.row !== posB.row) return posA.row - posB.row; return posA.col - posB.col; }); // 같은 row에 있는 컴포넌트들을 가로 스택으로 그룹화 const rowGroups = groupByRow(sortedIds, baseLayout.componentPositions); // 루트 컨테이너 (세로 스택) const rootContainer: PopContainer = { id: "root", type: "stack", direction: "vertical", wrap: false, gap: v3.settings.canvasGrid.gap, alignItems: "stretch", justifyContent: "start", children: [], }; // 각 행을 가로 스택으로 변환 for (const [row, ids] of rowGroups) { if (ids.length === 1) { // 단일 컴포넌트면 직접 추가 rootContainer.children.push(ids[0]); } else { // 여러 컴포넌트면 가로 스택으로 감싸기 const rowStack: PopContainer = { id: `row-${row}`, type: "stack", direction: "horizontal", wrap: true, gap: v3.settings.canvasGrid.gap, alignItems: "center", justifyContent: "start", children: ids, }; rootContainer.children.push(rowStack); } } // 컴포넌트 정의 변환 const components: Record = {}; for (const id of componentIds) { const v3Comp = v3.components[id]; const pos = baseLayout.componentPositions[id]; components[id] = { ...v3Comp, size: { // colSpan을 기반으로 크기 모드 결정 width: pos.colSpan >= 20 ? "fill" : "fixed", height: "fixed", fixedWidth: pos.colSpan * (1024 / 24), // 대략적인 픽셀 변환 fixedHeight: pos.rowSpan * (768 / 24), minWidth: 100, }, }; } return { version: "pop-4.0", root: rootContainer, components, dataFlow: v3.dataFlow, settings: { touchTargetMin: v3.settings.touchTargetMin, mode: v3.settings.mode, defaultGap: v3.settings.canvasGrid.gap, defaultPadding: 16, breakpoints: { tablet: 768, mobile: 480, }, }, metadata: v3.metadata, }; } ``` ### 6.2 하위 호환 - v3 레이아웃은 계속 지원 - 디자이너에서 v3 → v4 업그레이드 버튼 제공 - 새로 생성하는 레이아웃은 v4 --- ## 7. 예상 효과 ### 7.1 사용자 경험 | 항목 | 기존 (v3) | 새로운 (v4) | |------|-----------|-------------| | 설계 개수 | 4개 | 1개 | | 작업 시간 | 4배 | 1배 | | 반응형 | 수동 | 자동 | | 디바이스 대응 | 각각 설정 | mobileScale | ### 7.2 개발자 경험 | 항목 | 기존 (v3) | 새로운 (v4) | |------|-----------|-------------| | 렌더링 | CSS Grid | Flexbox | | 위치 계산 | col/row | 자동 | | 반응형 로직 | 4모드 분기 | 브레이크포인트 | | 유지보수 | 복잡 | 단순 | --- ## 8. 일정 (예상) | Phase | 내용 | 기간 | |-------|------|------| | 1 | 데이터 구조 | 1-2일 | | 2 | 렌더러 | 2-3일 | | 3 | 디자이너 UI | 3-4일 | | 4 | 뷰어 통합 | 1-2일 | | 5 | 백엔드 수정 | 1일 | | 6 | 테스트/마이그레이션 | 2-3일 | | **총계** | | **10-15일** | --- ## 9. 리스크 및 대응 ### 9.1 기존 레이아웃 호환성 - **리스크**: v3 → v4 자동 변환이 완벽하지 않을 수 있음 - **대응**: - 마이그레이션 미리보기 기능 - 수동 조정 도구 제공 - v3 유지 옵션 ### 9.2 학습 곡선 - **리스크**: 제약조건 개념이 익숙하지 않을 수 있음 - **대응**: - 프리셋 제공 (예: "화면 전체 채우기", "고정 크기") - 툴팁/도움말 - 예제 템플릿 ### 9.3 성능 - **리스크**: Flexbox 중첩으로 렌더링 성능 저하 - **대응**: - 컨테이너 중첩 깊이 제한 (최대 3-4) - React.memo 활용 - 가상화 (리스트 컴포넌트) --- ## 10. 결론 v4.0 제약조건 기반 시스템은 업계 표준(Figma, Flutter, SwiftUI)을 따르며, 사용자의 작업량을 75% 줄이고 자동 반응형을 제공합니다. 구현 후 POP 디자이너는: - **1개 레이아웃**만 설계 - **모든 화면 크기**에 자동 적응 - **모바일 특화 설정** (mobileScale)으로 세밀한 제어 가능 --- ## 11. 추가 설정 (2026-02-03 업데이트) ### 11.1 확장된 전역 설정 ```typescript interface PopGlobalSettingsV4 { // 기존 touchTargetMin: number; // 48 (normal) / 60 (industrial) mode: "normal" | "industrial"; defaultGap: number; defaultPadding: number; breakpoints: { tablet: number; // 768 mobile: number; // 480 }; // 신규 추가 environment: "indoor" | "outdoor"; // 야외면 대비 높임 typography: { body: { min: number; max: number }; // 14-18px heading: { min: number; max: number }; // 18-28px caption: { min: number; max: number }; // 12-14px }; contrast: "normal" | "high"; // outdoor면 자동 high } ``` ### 11.2 컴포넌트 기본값 프리셋 컴포넌트 추가 시 자동 적용되는 안전한 기본값: ```typescript const COMPONENT_DEFAULTS = { "pop-button": { minWidth: 80, minHeight: 48, height: "fixed", fixedHeight: 48, }, "pop-field": { minWidth: 120, minHeight: 40, height: "fixed", fixedHeight: 48, }, "pop-list": { minHeight: 200, itemHeight: 48, }, // ... }; ``` ### 11.3 리스트 반응형 컬럼 ```typescript interface PopListConfig { // 기존 listType: PopListType; displayColumns?: string[]; // 신규 추가 responsiveColumns?: { tablet: string[]; // 전체 컬럼 mobile: string[]; // 주요 컬럼만 }; } ``` ### 11.4 라벨 배치 자동화 ```typescript interface PopContainer { // 기존 direction: "horizontal" | "vertical"; // 신규 추가 labelPlacement?: "auto" | "above" | "beside"; // auto: 모바일 세로=위, 태블릿 가로=옆 } ``` --- ## 12. 관련 문서 - [v4 핵심 규칙 가이드](./V4_CORE_RULES.md) - **3가지 핵심 규칙 (필독)** - [반응형 디자인 가이드](./RESPONSIVE_DESIGN_GUIDE.md) - [컴포넌트 로드맵](./COMPONENT_ROADMAP.md) - [크기 프리셋 가이드](./SIZE_PRESETS.md) - [컴포넌트 상세 스펙](./components-spec.md) --- ## 13. 현재 상태 (2026-02-03) **구현 대기**: 컴포넌트가 아직 없어서 레이아웃 시스템보다 컴포넌트 개발이 선행되어야 함. **권장 진행 순서**: 1. 기초 컴포넌트 개발 (PopButton, PopInput 등) 2. 조합 컴포넌트 개발 (PopFormField, PopCard 등) 3. 복합 컴포넌트 개발 (PopDataTable, PopCardList 등) 4. v4 레이아웃 시스템 구현 5. 디자이너 UI 개발 --- *최종 업데이트: 2026-02-03*