From 760e545444fd1e3f146f92ff6f6490baf17152ca Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Wed, 4 Feb 2026 14:19:32 +0900 Subject: [PATCH] =?UTF-8?q?feat(pop):=20=EB=B7=B0=ED=8F=AC=ED=8A=B8=20?= =?UTF-8?q?=EA=B0=90=EC=A7=80=20=EB=B0=8F=20=EB=B9=84=EC=9C=A8=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=9D=BC=EB=A7=81=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PopFlexRenderer에 뷰포트 너비 감지 기능 추가 (최대 1366px 제한) - 비율 스케일링 시스템 구현: 컴포넌트 크기, gap, padding에 스케일 적용 - 디자인 모드와 뷰어 모드에서의 스케일 차별화 - 문서 업데이트: 비율 스케일링 시스템 및 적용 위치 설명 추가 --- popdocs/ARCHITECTURE.md | 85 ++++++++++++++++++++++++++----- popdocs/V4_UNIFIED_DESIGN_SPEC.md | 41 ++++++++++++++- 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/popdocs/ARCHITECTURE.md b/popdocs/ARCHITECTURE.md index 1259e9cc..357e5c9b 100644 --- a/popdocs/ARCHITECTURE.md +++ b/popdocs/ARCHITECTURE.md @@ -68,12 +68,31 @@ POP 메인 대시보드. 메뉴 그리드, KPI, 공지사항 등을 표시. - v3/v4 레이아웃 자동 감지 및 렌더링 - 반응형 모드 감지 (태블릿/모바일, 가로/세로) - 프리뷰 모드 지원 (`?preview=true`) +- **뷰포트 감지 및 비율 스케일링** ```typescript +// 뷰포트 너비 감지 (최대 1366px 제한) +const [viewportWidth, setViewportWidth] = useState(1024); + +useEffect(() => { + const updateViewportWidth = () => { + setViewportWidth(Math.min(window.innerWidth, 1366)); + }; + updateViewportWidth(); + window.addEventListener("resize", updateViewportWidth); + return () => window.removeEventListener("resize", updateViewportWidth); +}, []); + // 레이아웃 버전 감지 및 렌더링 if (popLayoutV4) { - // v4: PopFlexRenderer 사용 - + // v4: PopFlexRenderer 사용 (비율 스케일링 적용) +
+ +
} else if (popLayoutV3) { // v3: PopLayoutRenderer 사용 @@ -228,37 +247,77 @@ type SizeMode = "fixed" | "fill" | "hug"; #### `PopFlexRenderer.tsx` (v4용) -**역할**: v4 레이아웃을 Flexbox로 렌더링 +**역할**: v4 레이아웃을 Flexbox로 렌더링 + 비율 스케일링 **핵심 기능**: - 컨테이너 재귀 렌더링 - 반응형 규칙 적용 (breakpoint) - 크기 제약 → CSS 스타일 변환 - 컴포넌트 숨김 처리 (hideBelow) +- **비율 스케일링** (뷰어 모드) -**크기 제약 변환 로직**: +**비율 스케일링 시스템**: ```typescript -function calculateSizeStyle(size: PopSizeConstraintV4): React.CSSProperties { +// 기준 너비 (10인치 태블릿 가로) +const BASE_VIEWPORT_WIDTH = 1024; + +// 스케일 계산 (디자인 모드: 1, 뷰어 모드: 실제 비율) +const scale = isDesignMode ? 1 : viewportWidth / BASE_VIEWPORT_WIDTH; + +// 예시: 12인치(1366px) 화면 +// scale = 1366 / 1024 = 1.33 +// 200px 컴포넌트 → 266px +``` + +**크기 제약 변환 로직** (스케일 적용): +```typescript +function calculateSizeStyle( + size: PopSizeConstraintV4, + settings: PopGlobalSettingsV4, + scale: number = 1 // 스케일 파라미터 추가 +): React.CSSProperties { const style: React.CSSProperties = {}; - // 너비 + // 너비 (스케일 적용) switch (size.width) { - case "fixed": style.width = `${size.fixedWidth}px`; style.flexShrink = 0; break; - case "fill": style.flex = 1; style.minWidth = size.minWidth || 0; break; - case "hug": style.width = "auto"; style.flexShrink = 0; break; + case "fixed": + style.width = `${size.fixedWidth * scale}px`; + style.flexShrink = 0; + break; + case "fill": + style.flex = 1; + style.minWidth = size.minWidth ? `${size.minWidth * scale}px` : 0; + break; + case "hug": + style.width = "auto"; + style.flexShrink = 0; + break; } - // 높이 + // 높이 (스케일 적용) switch (size.height) { - case "fixed": style.height = `${size.fixedHeight}px`; break; - case "fill": style.flexGrow = 1; break; - case "hug": style.height = "auto"; break; + case "fixed": + style.height = `${size.fixedHeight * scale}px`; + break; + case "fill": + style.flexGrow = 1; + break; + case "hug": + style.height = "auto"; + break; } return style; } ``` +**컨테이너 스케일 적용**: +```typescript +// gap, padding도 스케일 적용 +const scaledGap = gap * scale; +const scaledPadding = padding ? padding * scale : undefined; +``` + #### `ComponentRenderer.tsx` **역할**: 개별 컴포넌트 렌더링 (디자인 모드용 플레이스홀더) diff --git a/popdocs/V4_UNIFIED_DESIGN_SPEC.md b/popdocs/V4_UNIFIED_DESIGN_SPEC.md index 6f45d014..ade03edd 100644 --- a/popdocs/V4_UNIFIED_DESIGN_SPEC.md +++ b/popdocs/V4_UNIFIED_DESIGN_SPEC.md @@ -250,10 +250,49 @@ interface PopComponentDefinitionV4 { |------|------|------| | `PopDesigner.tsx` | v3/v4 통합 디자이너 | 완료 | | `PopCanvasV4.tsx` | v4 캔버스 (4개 프리셋 + 슬라이더) | 완료 | -| `PopFlexRenderer.tsx` | v4 Flexbox 렌더러 | 완료 | +| `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)` | 최대 너비 제한 | ---