# POP v4 통합 설계 모드 스펙 **작성일: 2026-02-04** **최종 업데이트: 2026-02-04** **상태: Phase 3 완료 (visibility + 줄바꿈 컴포넌트)** --- ## 개요 v3/v4 탭을 제거하고, **v4 자동 모드를 기본**으로 하되 **모드별 오버라이드** 기능을 지원하는 통합 설계 방식. --- ## 핵심 개념 ### 기존 방식 (v3) ``` 4개 모드 각각 설계 필요 태블릿 가로: 버튼 → col 1, row 1 태블릿 세로: 버튼 → col 1, row 5 (따로 설정) 모바일 가로: 버튼 → col 1, row 1 (따로 설정) 모바일 세로: 버튼 → col 1, row 10 (따로 설정) ``` ### 새로운 방식 (v4 통합) ``` 기본: 태블릿 가로에서 규칙 설정 버튼 → width: fill, height: 48px 결과: 모든 모드에 자동 적용 태블릿 가로: 버튼 너비 1024px, 높이 48px 태블릿 세로: 버튼 너비 768px, 높이 48px 모바일 가로: 버튼 너비 667px, 높이 48px 모바일 세로: 버튼 너비 375px, 높이 48px 예외: 특정 모드에서 편집하면 오버라이드 모바일 세로: 버튼 높이 36px (수동 설정) ``` --- ## 현재 UI (Phase 1.5 완료) ``` ┌─────────────────────────────────────────────────────────────────┐ │ ← 목록 화면명 *변경됨 [↶][↷] 자동 레이아웃 (v4) [저장] │ ├─────────────────────────────────────────────────────────────────┤ │ 편집 중: v4 (자동 반응형) │ │ 규칙 기반 레이아웃 │ ├────────────┬────────────────────────────────────┬───────────────┤ │ 컴포넌트 │ 미리보기: [모바일↕][모바일↔] │ 속성 │ │ │ [태블릿↕][태블릿↔(기본)] │ │ │ 필드 │ 너비: [====●====] 1024 x 768 │ │ │ 버튼 │ │ │ │ 리스트 │ ┌──────────────────────────────┐ │ 탭: 크기 │ │ 인디케이터 │ │ [필드1] [필드2] [필드3] │ │ 설정 │ │ 스캐너 │ │ [필드4] [Spacer] [버튼] │ │ 표시 ⬅ 🆕│ │ 숫자패드 │ │ │ │ 데이터 │ │ 스페이서 │ │ (가로 배치 + 자동 줄바꿈) │ │ │ │ 줄바꿈 🆕 │ │ (스크롤 가능) │ │ │ │ │ └──────────────────────────────┘ │ │ │ │ 태블릿 가로 (1024x768) │ │ └────────────┴────────────────────────────────────┴───────────────┘ ``` ### 레이아웃 방식 (업계 표준) | 서비스 | 방식 | |--------|------| | Figma | Auto Layout (Flexbox) | | Webflow | Flexbox + CSS Grid | | FlutterFlow | Row/Column/Stack | | Adalo 2.0 | Flexbox + Constraints | | **POP v4** | **Flexbox (horizontal + wrap)** | ### 특수 컴포넌트 사용법 #### Spacer (빈 공간) ``` [버튼A] [Spacer(fill)] [버튼B] → 버튼B가 오른쪽 끝으로 [Spacer] [컴포넌트] [Spacer] → 컴포넌트가 가운데로 [Spacer(fill)] [컴포넌트] → 컴포넌트가 오른쪽으로 ``` #### 줄바꿈 (Break) 🆕 Phase 3 ``` [필드A] [필드B] [줄바꿈] [필드C] → 필드C가 새 줄로 이동 태블릿: [필드A] [필드B] [필드C] ← 줄바꿈 숨김 (한 줄) 모바일: [필드A] [필드B] ← 줄바꿈 표시 (두 줄) [필드C] ``` ### 프리셋 버튼 (4개 모드) | 버튼 | 해상도 | 설명 | |------|--------|------| | 모바일↕ | 375 x 667 | 모바일 세로 | | 모바일↔ | 667 x 375 | 모바일 가로 | | 태블릿↕ | 768 x 1024 | 태블릿 세로 | | 태블릿↔* | 1024 x 768 | 태블릿 가로 (기본) | ### 레이아웃 판별 로직 ```typescript // 새 화면 또는 빈 레이아웃 → v4로 시작 const hasValidLayout = loadedLayout && loadedLayout.version; const hasComponents = loadedLayout?.components && Object.keys(loadedLayout.components).length > 0; if (hasValidLayout && hasComponents) { // v4면 v4, 그 외 v3로 변환 } else { // v4로 새로 시작 } ``` --- ## 오버라이드 동작 (Phase 2 예정) ### 자동 감지 방식 1. 사용자가 **태블릿 가로(기본)**에서 편집 → 기본 규칙 저장 2. 사용자가 **다른 모드**에서 편집 → 해당 모드 오버라이드 자동 저장 3. 편집 안 한 모드 → 기본 규칙에서 자동 계산 ### 편집 상태 표시 | 상태 | 버튼 색상 | 설명 | |------|----------|------| | 기본 (태블릿 가로) | 강조 + "(기본)" | 항상 표시 | | 자동 | 기본 색상 | 편집 안 함 | | 편집됨 | 강조 색상 | 오버라이드 있음 | ### 되돌리기 - 편집된 모드에만 "자동으로 되돌리기" 버튼 활성화 - 클릭 시 오버라이드 삭제 → 기본 규칙 복원 --- ## 데이터 구조 ### PopLayoutDataV4 (Phase 2에서 수정 예정) ```typescript interface PopLayoutDataV4 { version: "pop-4.0"; root: PopContainerV4; components: Record; dataFlow: PopDataFlow; settings: PopGlobalSettingsV4; // 모드별 오버라이드 (Phase 2에서 추가) overrides?: { mobile_portrait?: ModeOverride; mobile_landscape?: ModeOverride; tablet_portrait?: ModeOverride; // tablet_landscape는 기본이므로 오버라이드 없음 }; } interface ModeOverride { components?: Record>; containers?: Record>; } ``` ### PopComponentDefinitionV4 (Phase 3에서 수정 예정) ```typescript interface PopComponentDefinitionV4 { type: PopComponentType; label?: string; size: PopSizeConstraintV4; alignSelf?: "start" | "center" | "end" | "stretch"; // 모드별 표시 설정 (Phase 3에서 추가) visibility?: { mobile_portrait?: boolean; // 기본 true mobile_landscape?: boolean; // 기본 true tablet_portrait?: boolean; // 기본 true tablet_landscape?: boolean; // 기본 true }; } ``` --- ## 컴포넌트 표시/숨김 (Phase 3 예정) ### 업계 표준 (Webflow, Figma) - 삭제가 아닌 **숨김** 처리 - 특정 모드에서만 `display: none` - 언제든 다시 표시 가능 ### UI (속성 패널) ``` ┌─────────────────────────┐ │ 버튼 │ ├─────────────────────────┤ │ 표시 설정 │ │ [x] 모바일 세로 │ │ [x] 모바일 가로 │ │ [x] 태블릿 세로 │ │ [x] 태블릿 가로 │ │ │ │ (체크 해제 = 숨김) │ └─────────────────────────┘ ``` --- ## 구현 상태 ### Phase 1: 기본 구조 (완료) - [x] v3/v4 탭 제거 (자동 판별) - [x] 새 화면 → v4로 시작 - [x] 기존 v3 화면 → v3로 로드 (하위 호환) - [x] 4개 프리셋 버튼 (모바일↕, 모바일↔, 태블릿↕, 태블릿↔) - [x] 기본 프리셋 표시 (태블릿 가로 + "(기본)") - [x] 슬라이더 유지 (320~1200px, 비율 유지) - [x] ComponentPaletteV4 생성 ### Phase 1.5: Flexbox 가로 배치 (완료) - [x] Undo/Redo (Ctrl+Z / Ctrl+Shift+Z, 데스크탑 모드와 동일 방식) - [x] 드래그 리사이즈 핸들 - [x] Flexbox 가로 배치 (`direction: horizontal`, `wrap: true`) - [x] 컴포넌트 타입별 기본 크기 설정 - [x] Spacer 컴포넌트 (`pop-spacer`) - [x] 컴포넌트 순서 변경 (드래그 앤 드롭) - [x] 디바이스 스크린 무한 스크롤 ### Phase 1.6: 비율 스케일링 시스템 (완료) - [x] 기준 너비 1024px (10인치 태블릿 가로) - [x] 최대 너비 1366px (12인치 태블릿) - [x] 뷰포트 감지 및 resize 이벤트 리스너 - [x] 컴포넌트 크기 스케일 적용 (fixedWidth/Height) - [x] 컨테이너 스케일 적용 (gap, padding) - [x] 디자인 모드 분리 (scale=1) - [x] DndProvider 에러 수정 ### Phase 2: 오버라이드 기능 (다음) - [ ] ModeOverride 데이터 구조 추가 - [ ] 편집 감지 → 자동 오버라이드 저장 - [ ] 편집 상태 표시 (버튼 색상) - [ ] "자동으로 되돌리기" 버튼 ### Phase 3: 컴포넌트 표시/숨김 - [ ] visibility 속성 추가 - [ ] 속성 패널 체크박스 UI - [ ] 렌더러에서 visibility 처리 ### Phase 4: 순서 오버라이드 - [ ] 모드별 children 순서 오버라이드 - [ ] 드래그로 순서 변경 UI --- ## 관련 파일 | 파일 | 역할 | 상태 | |------|------|------| | `PopDesigner.tsx` | v3/v4 통합 디자이너 | 완료 | | `PopCanvasV4.tsx` | v4 캔버스 (4개 프리셋 + 슬라이더) | 완료 | | `PopFlexRenderer.tsx` | v4 Flexbox 렌더러 + 비율 스케일링 | 완료 | | `ComponentPaletteV4.tsx` | v4 컴포넌트 팔레트 | 완료 | | `ComponentEditorPanelV4.tsx` | v4 속성 편집 패널 | 완료 | | `pop-layout.ts` | v3/v4 타입 정의 | 완료, Phase 2-3에서 수정 예정 | | `page.tsx` (뷰어) | v4 뷰어 + viewportWidth 감지 | 완료 | --- ## 비율 스케일링 시스템 ### 업계 표준 Rockwell Automation HMI의 "Scale with Fixed Aspect Ratio" 방식 적용 ### 원리 10인치(1024px) 기준으로 디자인 → 8~12인치에서 배치 유지, 크기만 비례 조정 ### 계산 ``` scale = viewportWidth / 1024 scaledWidth = originalWidth * scale scaledHeight = originalHeight * scale scaledGap = originalGap * scale scaledPadding = originalPadding * scale ``` ### 화면별 결과 | 화면 | scale | 200px 컴포넌트 | 8px gap | |------|-------|----------------|---------| | 8인치 (800px) | 0.78 | 156px | 6px | | 10인치 (1024px) | 1.00 | 200px | 8px | | 12인치 (1366px) | 1.33 | 266px | 11px | | 14인치+ | 1.33 (max) | 266px + 여백 | 11px | ### 적용 위치 | 파일 | 함수/변수 | 역할 | |------|----------|------| | `PopFlexRenderer.tsx` | `BASE_VIEWPORT_WIDTH` | 기준 너비 상수 (1024) | | `PopFlexRenderer.tsx` | `calculateSizeStyle(size, settings, scale)` | 크기 스케일 적용 | | `PopFlexRenderer.tsx` | `ContainerRenderer.containerStyle` | gap, padding 스케일 적용 | | `page.tsx` | `viewportWidth` state | 뷰포트 너비 감지 | | `page.tsx` | `Math.min(window.innerWidth, 1366)` | 최대 너비 제한 | --- ## Phase 3: Visibility + 줄바꿈 컴포넌트 (완료) ✅ ### 개요 모드별 컴포넌트 표시/숨김 제어 및 강제 줄바꿈 기능 추가. ### 추가 타입 #### visibility 속성 ```typescript interface PopComponentDefinitionV4 { // 기존 속성... // 🆕 모드별 표시/숨김 visibility?: { tablet_landscape?: boolean; tablet_portrait?: boolean; mobile_landscape?: boolean; mobile_portrait?: boolean; }; } ``` #### pop-break 컴포넌트 ```typescript type PopComponentType = | "pop-field" | "pop-button" | "pop-list" | "pop-indicator" | "pop-scanner" | "pop-numpad" | "pop-spacer" | "pop-break"; // 🆕 줄바꿈 ``` ### 사용 예시 #### 모바일 전용 버튼 ```typescript { id: "call-button", type: "pop-button", label: "전화 걸기", visibility: { tablet_landscape: false, // 태블릿: 숨김 mobile_portrait: true, // 모바일: 표시 }, } ``` #### 모드별 줄바꿈 ``` 레이아웃: [A] [B] [줄바꿈] [C] [D] 줄바꿈 visibility: { tablet_landscape: false, mobile_portrait: true } 결과: 태블릿: [A] [B] [C] [D] (한 줄) 모바일: [A] [B] (두 줄) [C] [D] ``` ### 속성 패널 "표시" 탭 ``` ┌─────────────────────┐ │ 탭: 크기 설정 표시 📍│ ├─────────────────────┤ │ 모드별 표시 설정 │ │ ☑ 태블릿 가로 │ │ ☑ 태블릿 세로 │ │ ☐ 모바일 가로 (숨김)│ │ ☑ 모바일 세로 │ └─────────────────────┘ ``` ### 참고 문서 - [decisions/002-phase3-visibility-break.md](./decisions/002-phase3-visibility-break.md) - 상세 설계 --- *이 문서는 v4 통합 설계 모드의 스펙을 정의합니다.* *최종 업데이트: 2026-02-04 (Phase 3 완료)*