diff --git a/frontend/components/pop/designer/types/pop-layout.ts b/frontend/components/pop/designer/types/pop-layout.ts index 7199cd4c..f67f6f3d 100644 --- a/frontend/components/pop/designer/types/pop-layout.ts +++ b/frontend/components/pop/designer/types/pop-layout.ts @@ -1,9 +1,173 @@ // POP 디자이너 레이아웃 타입 정의 -// v3.0: 섹션 제거, 컴포넌트 직접 배치 -// 그리드 기반 반응형 레이아웃 (픽셀 좌표 없음, 그리드 셀 기반) +// v4.0: 제약조건 기반, 단일 소스 → 자동 적응 (Flexbox) +// v3.0: 4모드 그리드 기반 (CSS Grid) // ======================================== -// 레이아웃 모드 키 (4가지) +// v4.0 제약조건 기반 레이아웃 +// ======================================== +// 핵심: 1번 설계 → 모든 화면 자동 적응 +// - 위치(col/row) 대신 규칙(fill/fixed/hug) 설정 +// - Flexbox로 렌더링, 자동 줄바꿈 + +/** + * v4 레이아웃 (반응형) + * - 단일 소스: 4모드 따로 설계 X + * - 규칙 기반: 컴포넌트가 어떻게 반응할지 정의 + */ +export interface PopLayoutDataV4 { + version: "pop-4.0"; + + // 루트 컨테이너 (스택) + root: PopContainerV4; + + // 컴포넌트 정의 (ID → 정의) + components: Record; + + // 데이터 흐름 (기존과 동일) + dataFlow: PopDataFlow; + + // 전역 설정 + settings: PopGlobalSettingsV4; + + // 메타데이터 + metadata?: PopLayoutMetadata; +} + +/** + * v4 컨테이너 (스택) + * - Flexbox 기반 + * - 자식: 컴포넌트 ID 또는 중첩 컨테이너 + */ +export interface PopContainerV4 { + id: string; + type: "stack"; + + // 방향: 가로 또는 세로 + direction: "horizontal" | "vertical"; + + // 줄바꿈 허용 + wrap: boolean; + + // 간격 (px) + gap: number; + + // 정렬 + alignItems: "start" | "center" | "end" | "stretch"; + justifyContent: "start" | "center" | "end" | "space-between"; + + // 패딩 (px) + padding?: number; + + // 반응형 규칙 (선택) + responsive?: PopResponsiveRuleV4[]; + + // 자식 요소 + children: (string | PopContainerV4)[]; +} + +/** + * 반응형 규칙 + * - 특정 너비 이하에서 속성 변경 + */ +export interface PopResponsiveRuleV4 { + // 이 너비 이하에서 적용 (px) + breakpoint: number; + // 변경할 속성들 + direction?: "horizontal" | "vertical"; + gap?: number; + hidden?: boolean; // 컨테이너 숨김 +} + +/** + * v4 컴포넌트 정의 + * - 크기 제약 기반 + */ +export interface PopComponentDefinitionV4 { + id: string; + type: PopComponentType; + label?: string; + + // 크기 제약 (핵심) + size: PopSizeConstraintV4; + + // 개별 정렬 (컨테이너 설정 덮어쓰기) + alignSelf?: "start" | "center" | "end" | "stretch"; + + // 반응형 숨김 + hideBelow?: number; // 이 너비 이하에서 숨김 + + // 기존 속성 + dataBinding?: PopDataBinding; + style?: PopStylePreset; + config?: PopComponentConfig; +} + +/** + * 크기 제약 + * - fixed: 고정 px + * - fill: 남은 공간 채움 + * - hug: 내용에 맞춤 + */ +export interface PopSizeConstraintV4 { + width: "fixed" | "fill" | "hug"; + height: "fixed" | "fill" | "hug"; + + // fixed일 때 값 + fixedWidth?: number; + fixedHeight?: number; + + // 최소/최대 + minWidth?: number; + maxWidth?: number; + minHeight?: number; +} + +/** + * v4 전역 설정 + */ +export interface PopGlobalSettingsV4 { + // 터치 최소 크기 (px) + touchTargetMin: number; // 기본 48 + + // 모드 + mode: "normal" | "industrial"; // industrial: 터치 60px + + // 기본 간격 + defaultGap: number; // 기본 8 + + // 기본 패딩 + defaultPadding: number; // 기본 16 +} + +// ======================================== +// v4 생성 함수 +// ======================================== + +export const createEmptyPopLayoutV4 = (): PopLayoutDataV4 => ({ + version: "pop-4.0", + root: { + id: "root", + type: "stack", + direction: "vertical", + wrap: false, + gap: 8, + alignItems: "stretch", + justifyContent: "start", + padding: 16, + children: [], + }, + components: {}, + dataFlow: { connections: [] }, + settings: { + touchTargetMin: 48, + mode: "normal", + defaultGap: 8, + defaultPadding: 16, + }, +}); + +// ======================================== +// 레이아웃 모드 키 (v3용) // ======================================== export type PopLayoutModeKey = | "tablet_landscape" // 태블릿 가로 (1024x768) @@ -127,7 +291,11 @@ export interface PopLayoutDataV1 { // 통합 타입 // ======================================== -export type PopLayoutData = PopLayoutDataV1 | PopLayoutDataV2 | PopLayoutDataV3; +export type PopLayoutData = PopLayoutDataV1 | PopLayoutDataV2 | PopLayoutDataV3 | PopLayoutDataV4; + +export function isV4Layout(data: PopLayoutData): data is PopLayoutDataV4 { + return data.version === "pop-4.0"; +} export function isV3Layout(data: PopLayoutData): data is PopLayoutDataV3 { return data.version === "pop-3.0"; diff --git a/popdocs/CHANGELOG.md b/popdocs/CHANGELOG.md new file mode 100644 index 00000000..5dbd0015 --- /dev/null +++ b/popdocs/CHANGELOG.md @@ -0,0 +1,108 @@ +# POP 변경 이력 + +형식: [Keep a Changelog](https://keepachangelog.com/) + +--- + +## [미출시] + +- v4 디자이너 UI 연결 +- Tier 2, 3 컴포넌트 + +--- + +## [2026-02-04] + +### 오늘 목표 +POP 화면을 만들 수 있는 환경 완성 (타입 + 렌더러 + 기본 컴포넌트) + +### Added +- **v4 타입 정의** (간결 버전) + - `PopLayoutDataV4` - 단일 소스 레이아웃 + - `PopContainerV4` - 스택 컨테이너 (direction, wrap, gap, alignItems) + - `PopComponentDefinitionV4` - 크기 제약 기반 (size: fixed/fill/hug) + - `PopSizeConstraintV4` - 크기 규칙 + - `PopResponsiveRuleV4` - 반응형 규칙 (breakpoint별 변경) + - `PopGlobalSettingsV4` - 전역 설정 + - `createEmptyPopLayoutV4()` - 생성 함수 + - `isV4Layout()` - 타입 가드 + +### 진행중 +- v4 렌더러 (`PopFlexRenderer`) +- 기본 컴포넌트 (PopButton, PopInput, PopLabel) + +--- + +## [2026-02-04] (earlier) + +### Added +- 저장/조회 시스템 구축 + - rangraph: AI 장기 기억 (시맨틱 검색, 요약) + - popdocs: 상세 기록 (파일 기반, 히스토리) + - 이중 저장 체계로 검색 + 기록 분리 + +### Changed +- popdocs 문서 구조 정리 + - README.md: 저장/조회 규칙 추가 + - 기존 문서 archive/로 이동 +- 문서 관리 전략 확정 + - 저장 시: 파일 형식 자동 파악 → 형식 맞춰 추가 → rangraph 요약 + - 조회 시: rangraph 시맨틱 검색 + +### Removed +- .cursorrules 변경 계획 철회 (Git 커밋 영향) + +--- + +## [2026-02-03] + +### Added +- v4 제약조건 기반 레이아웃 계획 + - 단일 소스 + 자동 적응 + - 3가지 규칙 (크기, 배치, 반응형) + - ADR: `decisions/001-v4-constraint-based.md` + +--- + +## [2026-02-02] + +### Fixed +- 캔버스 rowSpan 문제 + - 원인: gridTemplateRows 고정 px + - 해결: `1fr` 사용 + +--- + +## [2026-02-01] + +### Fixed +- 4모드 자동 전환 문제 + - 해결: useResponsiveMode 훅 추가 + +--- + +## [2026-01-31] + +### Added +- v3 섹션 제거, 순수 그리드 구조 +- 4개 모드 독립 그리드 + +--- + +## [2026-01-30] + +### Added +- POP 디자이너 기본 구조 +- PopDesigner, PopCanvas 컴포넌트 + +--- + +## [2026-01-29] + +### Added +- screen_layouts_pop 테이블 +- POP 레이아웃 API (CRUD) + +--- + +*최신이 위, 시간순 역순* diff --git a/popdocs/PLAN.md b/popdocs/PLAN.md new file mode 100644 index 00000000..8af402a8 --- /dev/null +++ b/popdocs/PLAN.md @@ -0,0 +1,91 @@ +# POP 개발 계획 + +--- + +## 오늘 목표 (2026-02-04) + +**POP 화면을 만들 수 있는 환경 완성** + +### 필요한 것 + +1. **v4 타입 정의** - 완료 +2. **v4 렌더러** - Flexbox로 화면에 표시 +3. **기본 컴포넌트** - 실제 배치할 요소들 +4. **디자이너 UI 연결** - v4 모드로 설계 가능 + +### 작업 순서 + +``` +[v4 타입] → [렌더러] → [컴포넌트] → [디자이너 연결] + 완료 진행 대기 대기 +``` + +--- + +## 현재 진행 + +### v4 타입 정의 (완료) + +- [x] `PopLayoutDataV4` - 단일 소스 레이아웃 +- [x] `PopContainerV4` - 스택 컨테이너 +- [x] `PopSizeConstraintV4` - 크기 규칙 (fixed/fill/hug) +- [x] `PopResponsiveRuleV4` - 반응형 규칙 +- [x] `createEmptyPopLayoutV4()` - 생성 함수 +- [x] `isV4Layout()` - 타입 가드 + +### v4 렌더러 (진행) + +- [ ] `PopFlexRenderer` - Flexbox 기반 렌더링 +- [ ] 컨테이너 재귀 렌더링 +- [ ] 반응형 규칙 적용 (breakpoint) +- [ ] 컴포넌트 숨김 처리 (hideBelow) + +### 기본 컴포넌트 (대기) + +- [ ] `PopButton` - 터치 버튼 +- [ ] `PopInput` - 텍스트 입력 +- [ ] `PopLabel` - 텍스트 표시 + +--- + +## v3 vs v4 비교 + +| | v3 (기존) | v4 (새로운) | +|---|---|---| +| 설계 | 4모드 각각 | 1번만 | +| 데이터 | col, row 위치 | 규칙 (fill/fixed/hug) | +| 렌더링 | CSS Grid | Flexbox | +| 반응형 | 수동 | 자동 + 규칙 | + +--- + +## 컴포넌트 로드맵 + +### Tier 1: Primitive (기본) + +| 컴포넌트 | 용도 | 상태 | +|----------|------|------| +| PopButton | 터치 버튼 | 대기 | +| PopInput | 텍스트 입력 | 대기 | +| PopLabel | 텍스트 표시 | 대기 | +| PopBadge | 상태 배지 | 계획 | + +### Tier 2: Compound (조합) + +| 컴포넌트 | 용도 | 상태 | +|----------|------|------| +| PopFormField | 라벨 + 입력 | 계획 | +| PopCard | 카드 컨테이너 | 계획 | +| PopListItem | 목록 항목 | 계획 | + +### Tier 3: Complex (복합) + +| 컴포넌트 | 용도 | 상태 | +|----------|------|------| +| PopScanner | 바코드/QR 스캔 | 계획 | +| PopNumpad | 숫자 키패드 | 계획 | +| PopDataList | 페이징 목록 | 계획 | + +--- + +*최종 업데이트: 2026-02-04* diff --git a/popdocs/README.md b/popdocs/README.md new file mode 100644 index 00000000..4b6173a2 --- /dev/null +++ b/popdocs/README.md @@ -0,0 +1,90 @@ +# POP 화면 시스템 + +> Point of Production - 현장 작업자용 모바일/태블릿 화면 + +--- + +## Quick Reference + +### 주요 경로 + +| 용도 | 경로 | +|------|------| +| 뷰어 | `/pop/screens/{screenId}` | +| 관리 | `/admin/screenMng/popScreenMngList` | +| API | `/api/screen-management/layout-pop/:screenId` | + +### 핵심 파일 + +| 작업 | 파일 | +|------|------| +| 타입 | `frontend/components/pop/designer/types/pop-layout.ts` | +| 렌더러 | `frontend/components/pop/designer/renderers/` | +| 디자이너 | `frontend/components/pop/designer/PopDesigner.tsx` | + +### 현재 상태 + +- **버전**: v3.0 (4모드 그리드) +- **다음**: v4.0 (제약조건 기반) - 계획 + +--- + +## 문서 구조 + +| 파일 | 용도 | +|------|------| +| [SPEC.md](./SPEC.md) | 기술 스펙 | +| [PLAN.md](./PLAN.md) | 계획/로드맵 | +| [CHANGELOG.md](./CHANGELOG.md) | 변경 이력 | +| [decisions/](./decisions/) | 중요 결정 기록 (ADR) | +| [components-spec.md](./components-spec.md) | 컴포넌트 상세 | +| [archive/](./archive/) | 이전 문서 | + +--- + +## 저장/조회 시스템 + +### 역할 분담 + +| 저장소 | 역할 | 특징 | +|--------|------|------| +| **rangraph** | AI 장기 기억 | 시맨틱 검색, 요약 저장 | +| **popdocs** | 상세 기록 | 파일 기반, 히스토리 | + +### 저장 요청 + +| 요청 예시 | AI 행동 | +|----------|--------| +| "@CHANGELOG.md 오늘 작업 정리해줘" | 파일 형식 맞춰 추가 + rangraph 요약 | +| "이 결정 저장해줘" | rangraph save_decision + decisions/ ADR | +| "해결됐어" | rangraph save_lesson + CHANGELOG Fixed | +| "작업 완료" | rangraph workflow_submit + CHANGELOG Added | + +### 조회 요청 + +| 요청 예시 | AI 행동 | +|----------|--------| +| "어제 뭐했지?" | rangraph 검색 | +| "지금 뭐하는 중이었지?" | rangraph workflow_status | +| "이 버그 전에도 있었어?" | rangraph search_memory | +| "v4 관련 작업들" | rangraph search_memory "v4" | + +--- + +## v4 핵심 (요약) + +``` +v3: 4개 모드 각각 위치 설정 → 4배 작업량 +v4: 3가지 규칙만 설정 → 자동 적응 + +규칙: +1. 크기: fixed(고정) / fill(채움) / hug(맞춤) +2. 배치: direction, wrap, gap +3. 반응형: breakpoint별 변경 +``` + +상세: [SPEC.md](./SPEC.md) + +--- + +*최종 업데이트: 2026-02-04* diff --git a/popdocs/SPEC.md b/popdocs/SPEC.md new file mode 100644 index 00000000..a1fb5404 --- /dev/null +++ b/popdocs/SPEC.md @@ -0,0 +1,121 @@ +# POP 기술 스펙 + +--- + +## v4 핵심 규칙 (3가지) + +### 1. 크기 규칙 (Size) + +| 모드 | 설명 | 예시 | +|------|------|------| +| `fixed` | 고정 px | 버튼 48px | +| `fill` | 부모 채움 | 입력창 100% | +| `hug` | 내용 맞춤 | 라벨 | + +### 2. 배치 규칙 (Layout) + +| 항목 | 옵션 | +|------|------| +| direction | horizontal / vertical | +| wrap | true / false | +| gap | 8 / 16 / 24 px | +| alignItems | start / center / end / stretch | + +### 3. 반응형 규칙 (Responsive) + +```typescript +{ + direction: "horizontal", + responsive: [{ breakpoint: 768, direction: "vertical" }] +} +``` + +--- + +## 크기 프리셋 + +### 터치 요소 + +| 요소 | 일반 | 산업 | +|------|-----|------| +| 버튼 높이 | 48px | 60px | +| 입력창 높이 | 48px | 56px | +| 터치 영역 | 48px | 60px | + +### 폰트 (clamp) + +| 용도 | 범위 | CSS | +|------|-----|-----| +| 본문 | 14-18px | `clamp(14px, 1.5vw, 18px)` | +| 제목 | 18-28px | `clamp(18px, 2.5vw, 28px)` | + +### 간격 + +| 이름 | 값 | 용도 | +|------|---|-----| +| sm | 8px | 요소 내부 | +| md | 16px | 컴포넌트 간 | +| lg | 24px | 섹션 간 | + +--- + +## 반응형 원칙 + +``` +누르는 것 → 고정 (48px) +읽는 것 → 범위 (clamp) +담는 것 → 비율 (%) +``` + +--- + +## 데이터 구조 + +### v3 (현재) + +```typescript +interface PopLayoutDataV3 { + version: "pop-3.0"; + layouts: { + tablet_landscape: { componentPositions: Record }; + // ... 4개 모드 + }; + components: Record; +} +``` + +### v4 (계획) + +```typescript +interface PopLayoutDataV4 { + version: "pop-4.0"; + root: PopContainer; + components: Record; +} + +interface PopContainer { + type: "stack"; + direction: "horizontal" | "vertical"; + children: (string | PopContainer)[]; + responsive?: { breakpoint: number; direction?: string }[]; +} +``` + +--- + +## Troubleshooting + +### 캔버스 rowSpan 문제 + +- **증상**: 컴포넌트가 얇게 보임 +- **원인**: gridTemplateRows 고정 px +- **해결**: `1fr` 사용 + +### 4모드 전환 안 됨 + +- **증상**: 크기 줄여도 레이아웃 유지 +- **해결**: useResponsiveMode 훅 사용 + +--- + +*상세 archive 참조: `archive/V4_CORE_RULES.md`, `archive/SIZE_PRESETS.md`* diff --git a/BUGFIX_CANVAS_ROWS.md b/popdocs/archive/BUGFIX_CANVAS_ROWS.md similarity index 100% rename from BUGFIX_CANVAS_ROWS.md rename to popdocs/archive/BUGFIX_CANVAS_ROWS.md diff --git a/popdocs/archive/COMPONENT_ROADMAP.md b/popdocs/archive/COMPONENT_ROADMAP.md new file mode 100644 index 00000000..99c0d616 --- /dev/null +++ b/popdocs/archive/COMPONENT_ROADMAP.md @@ -0,0 +1,389 @@ +# POP 컴포넌트 로드맵 + +## 큰 그림: 3단계 접근 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ 1단계: 기초 블록 2단계: 조합 블록 3단계: 완성 화면 │ +│ ─────────────── ─────────────── ─────────────── │ +│ │ +│ [버튼] [입력창] [폼 그룹] [작업지시 화면] │ +│ [아이콘] [라벨] → [카드] → [실적입력 화면] │ +│ [뱃지] [로딩] [리스트] [모니터링 대시보드] │ +│ [테이블] │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 1단계: 기초 블록 (Primitive) + +가장 작은 단위. 다른 곳에서 재사용됩니다. + +### 필수 기초 블록 + +| 컴포넌트 | 역할 | 우선순위 | +|---------|------|---------| +| `PopButton` | 모든 버튼 | 1 | +| `PopInput` | 텍스트 입력 | 1 | +| `PopLabel` | 라벨/제목 | 1 | +| `PopIcon` | 아이콘 표시 | 1 | +| `PopBadge` | 상태 뱃지 | 2 | +| `PopLoading` | 로딩 스피너 | 2 | +| `PopDivider` | 구분선 | 3 | + +### PopButton 예시 + +```typescript +interface PopButtonProps { + children: React.ReactNode; + variant: "primary" | "secondary" | "danger" | "success"; + size: "sm" | "md" | "lg" | "xl"; + disabled?: boolean; + loading?: boolean; + icon?: string; + fullWidth?: boolean; + onClick?: () => void; +} + +// 사용 + + 작업 완료 + +``` + +### PopInput 예시 + +```typescript +interface PopInputProps { + type: "text" | "number" | "date" | "time"; + value: string | number; + onChange: (value: string | number) => void; + label?: string; + placeholder?: string; + required?: boolean; + error?: string; + size: "md" | "lg"; // POP은 lg 기본 +} + +// 사용 + +``` + +--- + +## 2단계: 조합 블록 (Compound) + +기초 블록을 조합한 중간 단위. + +### 조합 블록 목록 + +| 컴포넌트 | 구성 | 용도 | +|---------|------|-----| +| `PopFormField` | Label + Input + Error | 폼 입력 그룹 | +| `PopCard` | Container + Header + Body | 정보 카드 | +| `PopListItem` | Container + Content + Action | 리스트 항목 | +| `PopNumberPad` | Grid + Buttons | 숫자 입력 | +| `PopStatusBox` | Icon + Label + Value | 상태 표시 | + +### PopFormField 예시 + +```typescript +// 기초 블록 조합 +function PopFormField({ label, required, error, children }) { + return ( +
+ {label} + {children} + {error && {error}} +
+ ); +} + +// 사용 + + + +``` + +### PopCard 예시 + +```typescript +function PopCard({ title, badge, children, onClick }) { + return ( +
+
+ {title} + {badge && {badge}} +
+
+ {children} +
+
+ ); +} + +// 사용 + +

목표 수량: 100개

+

완료 수량: 45개

+
+``` + +--- + +## 3단계: 복합 컴포넌트 (Complex) + +비즈니스 로직이 포함된 완성형. + +### 복합 컴포넌트 목록 + +| 컴포넌트 | 기능 | 데이터 | +|---------|------|-------| +| `PopDataTable` | 대량 데이터 표시/편집 | API 연동 | +| `PopCardList` | 카드 형태 리스트 | API 연동 | +| `PopBarcodeScanner` | 바코드/QR 스캔 | 카메라/외부장치 | +| `PopKpiGauge` | KPI 게이지 | 실시간 데이터 | +| `PopAlarmList` | 알람 목록 | 웹소켓 | +| `PopProcessFlow` | 공정 흐름도 | 공정 데이터 | + +### PopDataTable 예시 + +```typescript +interface PopDataTableProps { + // 데이터 + data: any[]; + columns: Column[]; + + // 기능 + selectable?: boolean; + editable?: boolean; + sortable?: boolean; + + // 반응형 (자동) + responsiveColumns?: { + tablet: string[]; + mobile: string[]; + }; + + // 이벤트 + onRowClick?: (row: any) => void; + onSelectionChange?: (selected: any[]) => void; +} + +// 사용 + openDetail(row.id)} +/> +``` + +--- + +## 개발 순서 제안 + +### Phase 1: 기초 (1-2주) + +``` +Week 1: +- PopButton (모든 버튼의 기반) +- PopInput (모든 입력의 기반) +- PopLabel +- PopIcon + +Week 2: +- PopBadge +- PopLoading +- PopDivider +``` + +### Phase 2: 조합 (2-3주) + +``` +Week 3: +- PopFormField (폼의 기본 단위) +- PopCard (카드의 기본 단위) + +Week 4: +- PopListItem +- PopStatusBox +- PopNumberPad + +Week 5: +- PopModal +- PopToast +``` + +### Phase 3: 복합 (3-4주) + +``` +Week 6-7: +- PopDataTable (가장 복잡) +- PopCardList + +Week 8-9: +- PopBarcodeScanner +- PopKpiGauge +- PopAlarmList +- PopProcessFlow +``` + +--- + +## 컴포넌트 설계 원칙 + +### 1. 크기는 외부에서 제어 + +```typescript +// 좋음: 크기를 props로 받음 +확인 + +// 나쁨: 내부에서 크기 고정 + +``` + +### 2. 최소 크기는 내부에서 보장 + +```typescript +// 컴포넌트 내부 +const styles = { + minHeight: 48, // 터치 최소 크기 보장 + minWidth: 80, +}; +``` + +### 3. 반응형은 자동 + +```typescript +// 좋음: 화면 크기에 따라 자동 조절 + + + + +// 나쁨: 모드별로 다른 컴포넌트 +{isMobile ? : } +``` + +### 4. 데이터와 UI 분리 + +```typescript +// 좋음: 데이터 로직은 훅으로 +const { data, loading, error } = useWorkOrders(); + + + +// 나쁨: 컴포넌트 안에서 fetch +function PopDataTable() { + useEffect(() => { + fetch('/api/work-orders')... + }, []); +} +``` + +--- + +## 폴더 구조 제안 + +``` +frontend/components/pop/ +├── primitives/ # 1단계: 기초 블록 +│ ├── PopButton.tsx +│ ├── PopInput.tsx +│ ├── PopLabel.tsx +│ ├── PopIcon.tsx +│ ├── PopBadge.tsx +│ ├── PopLoading.tsx +│ └── index.ts +│ +├── compounds/ # 2단계: 조합 블록 +│ ├── PopFormField.tsx +│ ├── PopCard.tsx +│ ├── PopListItem.tsx +│ ├── PopNumberPad.tsx +│ ├── PopStatusBox.tsx +│ └── index.ts +│ +├── complex/ # 3단계: 복합 컴포넌트 +│ ├── PopDataTable/ +│ │ ├── PopDataTable.tsx +│ │ ├── PopTableHeader.tsx +│ │ ├── PopTableRow.tsx +│ │ └── index.ts +│ ├── PopCardList/ +│ ├── PopBarcodeScanner/ +│ └── index.ts +│ +├── hooks/ # 공용 훅 +│ ├── usePopTheme.ts +│ ├── useResponsiveSize.ts +│ └── useTouchFeedback.ts +│ +└── styles/ # 공용 스타일 + ├── pop-variables.css + └── pop-base.css +``` + +--- + +## 스타일 변수 + +```css +/* pop-variables.css */ + +:root { + /* 터치 크기 */ + --pop-touch-min: 48px; + --pop-touch-industrial: 60px; + + /* 폰트 크기 */ + --pop-font-body: clamp(14px, 1.5vw, 18px); + --pop-font-heading: clamp(18px, 2.5vw, 28px); + --pop-font-caption: clamp(12px, 1vw, 14px); + + /* 간격 */ + --pop-gap-sm: 8px; + --pop-gap-md: 16px; + --pop-gap-lg: 24px; + + /* 색상 */ + --pop-primary: #2563eb; + --pop-success: #16a34a; + --pop-warning: #f59e0b; + --pop-danger: #dc2626; + + /* 고대비 (야외용) */ + --pop-high-contrast-bg: #000000; + --pop-high-contrast-fg: #ffffff; +} +``` + +--- + +## 다음 단계 + +1. **기초 블록부터 시작**: PopButton, PopInput 먼저 만들기 +2. **스토리북 설정**: 컴포넌트별 문서화 +3. **테스트**: 터치 크기, 반응형 확인 +4. **디자이너 연동**: v4 레이아웃 시스템과 통합 + +--- + +*최종 업데이트: 2026-02-03* diff --git a/POPREADME.md b/popdocs/archive/POPREADME.md similarity index 100% rename from POPREADME.md rename to popdocs/archive/POPREADME.md diff --git a/POPUPDATE.md b/popdocs/archive/POPUPDATE.md similarity index 100% rename from POPUPDATE.md rename to popdocs/archive/POPUPDATE.md diff --git a/popdocs/archive/POP_V4_CONSTRAINT_SYSTEM_PLAN.md b/popdocs/archive/POP_V4_CONSTRAINT_SYSTEM_PLAN.md new file mode 100644 index 00000000..a0331e73 --- /dev/null +++ b/popdocs/archive/POP_V4_CONSTRAINT_SYSTEM_PLAN.md @@ -0,0 +1,760 @@ +# 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* diff --git a/docs/pop/PROJECT_ARCHITECTURE.md b/popdocs/archive/PROJECT_ARCHITECTURE.md similarity index 100% rename from docs/pop/PROJECT_ARCHITECTURE.md rename to popdocs/archive/PROJECT_ARCHITECTURE.md diff --git a/popdocs/archive/RESPONSIVE_DESIGN_GUIDE.md b/popdocs/archive/RESPONSIVE_DESIGN_GUIDE.md new file mode 100644 index 00000000..05cf044f --- /dev/null +++ b/popdocs/archive/RESPONSIVE_DESIGN_GUIDE.md @@ -0,0 +1,153 @@ +# POP 반응형 디자인 가이드 + +## 쉬운 요약 + +### 핵심 원칙: 3가지만 기억하세요 + +``` +1. 누르는 것 → 크기 고정 (최소 48px) +2. 읽는 것 → 범위 안에서 자동 조절 +3. 담는 것 → 화면에 맞춰 늘어남 +``` + +--- + +## 1. 터치 요소 (고정 크기) + +손가락 크기는 화면이 커져도 변하지 않습니다. + +| 요소 | 일반 | 산업현장(장갑) | +|------|-----|--------------| +| 버튼 | 48px | 60px | +| 아이콘 (누르는 용) | 48px | 60px | +| 체크박스 | 24px (터치영역 48px) | 24px (터치영역 60px) | +| 리스트 한 줄 높이 | 48px | 56px | +| 입력창 높이 | 40px | 48px | + +--- + +## 2. 텍스트 (범위 조절) + +화면 크기에 따라 자동으로 커지거나 작아집니다. + +| 용도 | 최소 | 최대 | +|------|-----|-----| +| 본문 | 14px | 18px | +| 제목 | 18px | 28px | +| 설명 | 12px | 14px | + +**CSS 예시**: +```css +font-size: clamp(14px, 1.5vw, 18px); +``` + +--- + +## 3. 레이아웃 (비율 기반) + +컨테이너는 화면에 맞춰 늘어납니다. + +| 요소 | 방식 | 예시 | +|------|-----|-----| +| 컨테이너 | 100% | 화면 전체 채움 | +| 카드 2열 | 48% + 48% | 화면 반씩 | +| 입력창 너비 | fill | 부모 채움 | +| 여백 | 8/16/24px | 화면 크기별 | + +--- + +## 4. 환경별 설정 + +### 일반 (실내) +- 터치: 48px +- 대비: 4.5:1 +- 폰트: 14-18px + +### 산업현장 (야외/장갑) +- 터치: 60px (+25%) +- 대비: 7:1 이상 +- 폰트: 18-22px (+25%) + +--- + +## 5. 리스트/테이블 반응형 + +화면이 좁아지면 컬럼을 줄입니다. + +``` +태블릿 (넓음) 모바일 (좁음) +┌──────┬──────┬──────┬──────┐ ┌──────┬──────┐ +│품번 │품명 │수량 │상태 │ │품번 │수량 │ +├──────┼──────┼──────┼──────┤ ├──────┼──────┤ +│A001 │나사 │100 │완료 │ │A001 │100 │ +└──────┴──────┴──────┴──────┘ └──────┴──────┘ + ↳ 터치하면 상세보기 +``` + +--- + +## 6. 폼 라벨 배치 + +| 화면 | 라벨 위치 | 이유 | +|------|----------|-----| +| 모바일 세로 | 위 | 입력창 너비 확보 | +| 태블릿 가로 | 옆 | 공간 여유 | + +``` +모바일 태블릿 +┌─────────────────┐ ┌─────────────────────────┐ +│ 이름 │ │ 이름: [입력____________] │ +│ [입력__________]│ └─────────────────────────┘ +└─────────────────┘ +``` + +--- + +## 7. 그림으로 보는 반응형 + +``` +8인치 태블릿 12인치 태블릿 +┌─────────────────┐ ┌───────────────────────┐ +│ [버튼 48px] │ │ [버튼 48px] │ ← 버튼 크기 동일! +│ │ │ │ +│ ┌─────────────┐ │ │ ┌─────────────────┐ │ +│ │ 입력창 │ │ │ │ 입력창 │ │ ← 너비만 늘어남 +│ └─────────────┘ │ │ └─────────────────┘ │ +│ │ │ │ +│ 글자 14px │ │ 글자 18px │ ← 글자만 커짐 +└─────────────────┘ └───────────────────────┘ +``` + +--- + +## 8. 색상 대비 (야외용) + +| 환경 | 최소 대비 | 권장 | +|------|----------|-----| +| 실내 | 4.5:1 | 7:1 | +| 야외 | 7:1 | 10:1+ | + +**좋은 조합**: +- 흰 배경 + 검정 글자 +- 검정 배경 + 흰 글자 +- 노랑 경고 + 검정 글자 + +**피해야 할 조합**: +- 연한 회색 + 밝은 회색 +- 빨강 + 녹색 (색맹 고려) + +--- + +## 체크리스트 + +### 컴포넌트 만들 때 확인 + +- [ ] 버튼/터치 요소 최소 48px인가? +- [ ] 폰트에 clamp() 적용했나? +- [ ] 색상 대비 4.5:1 이상인가? +- [ ] 모바일에서 라벨이 위에 있나? +- [ ] 리스트가 좁은 화면에서 컬럼 줄어드나? + +--- + +*최종 업데이트: 2026-02-03* diff --git a/popdocs/archive/SIZE_PRESETS.md b/popdocs/archive/SIZE_PRESETS.md new file mode 100644 index 00000000..99de0691 --- /dev/null +++ b/popdocs/archive/SIZE_PRESETS.md @@ -0,0 +1,205 @@ +# POP 크기 프리셋 가이드 + +## 컴포넌트별 기본 크기 + +컴포넌트를 만들면 자동으로 적용되는 크기입니다. + +--- + +## 버튼 (PopButton) + +| 사이즈 | 높이 | 최소 너비 | 폰트 | 용도 | +|-------|------|----------|------|-----| +| sm | 32px | 60px | 12px | 보조 버튼 | +| md | 40px | 80px | 14px | 일반 버튼 | +| **lg** | **48px** | **100px** | **16px** | **POP 기본** | +| xl | 56px | 120px | 18px | 주요 액션 | +| industrial | 60px | 140px | 20px | 장갑 착용 | + +```typescript +// POP에서는 lg가 기본 +확인 + +// 산업현장 +작업 완료 +``` + +--- + +## 입력창 (PopInput) + +| 사이즈 | 높이 | 폰트 | 용도 | +|-------|------|------|-----| +| md | 40px | 14px | 일반 | +| **lg** | **48px** | **16px** | **POP 기본** | +| xl | 56px | 18px | 강조 입력 | + +```typescript +// 입력창 너비는 항상 부모 채움 (fill) + +``` + +--- + +## 리스트 행 (PopListItem) + +| 사이즈 | 높이 | 폰트 | 용도 | +|-------|------|------|-----| +| compact | 40px | 14px | 많은 데이터 | +| **normal** | **48px** | **16px** | **POP 기본** | +| spacious | 56px | 18px | 여유로운 | +| industrial | 64px | 20px | 장갑 착용 | + +```typescript + + 작업지시 #1234 + +``` + +--- + +## 아이콘 (PopIcon) + +| 사이즈 | 크기 | 터치 영역 | 용도 | +|-------|-----|----------|-----| +| sm | 16px | 32px | 뱃지 안 | +| md | 20px | 40px | 텍스트 옆 | +| **lg** | **24px** | **48px** | **POP 기본** | +| xl | 32px | 56px | 강조 | + +```typescript +// 아이콘만 있는 버튼 + + +// 텍스트 + 아이콘 +저장 +``` + +--- + +## 카드 (PopCard) + +| 요소 | 크기 | +|------|-----| +| 패딩 | 16px | +| 제목 폰트 | 18px (heading) | +| 본문 폰트 | 16px (body) | +| 모서리 | 8px | +| 최소 높이 | 100px | + +```typescript + + 작업지시 + 내용 + +``` + +--- + +## 숫자패드 (PopNumberPad) + +| 요소 | 크기 | +|------|-----| +| 버튼 크기 | 60px x 60px | +| 버튼 간격 | 8px | +| 전체 너비 | 240px | +| 폰트 | 24px | + +``` +┌─────────────────────┐ +│ [ 123 ] │ ← 디스플레이 48px +├─────┬─────┬─────────┤ +│ 7 │ 8 │ 9 │ ← │ ← 각 버튼 60x60 +├─────┼─────┼─────────┤ +│ 4 │ 5 │ 6 │ C │ +├─────┼─────┼─────────┤ +│ 1 │ 2 │ 3 │ │ +├─────┼─────┼─────│ OK│ +│ 0 │ . │ +- │ │ +└─────┴─────┴─────────┘ +``` + +--- + +## 상태 표시 (PopStatusBox) + +| 사이즈 | 너비 | 높이 | 아이콘 | 용도 | +|-------|-----|------|-------|-----| +| sm | 80px | 60px | 24px | 여러 개 나열 | +| **md** | **120px** | **80px** | **32px** | **POP 기본** | +| lg | 160px | 100px | 40px | 강조 | + +```typescript + +``` + +--- + +## KPI 게이지 (PopKpiGauge) + +| 사이즈 | 너비 | 높이 | 용도 | +|-------|-----|------|-----| +| sm | 120px | 120px | 여러 개 나열 | +| **md** | **180px** | **180px** | **POP 기본** | +| lg | 240px | 240px | 강조 | + +--- + +## 간격 (Gap/Padding) + +| 이름 | 값 | 용도 | +|------|---|-----| +| xs | 4px | 아이콘-텍스트 | +| sm | 8px | 요소 내부 | +| **md** | **16px** | **컴포넌트 간** | +| lg | 24px | 섹션 간 | +| xl | 32px | 영역 구분 | + +--- + +## 반응형 조절 + +화면 크기에 따라 자동 조절되는 값들: + +| 요소 | 8인치 태블릿 | 12인치 태블릿 | +|------|------------|--------------| +| 본문 폰트 | 14px | 18px | +| 제목 폰트 | 18px | 28px | +| 컨테이너 패딩 | 12px | 24px | +| 카드 간격 | 12px | 16px | + +**고정되는 값들 (변하지 않음)**: +- 버튼 높이: 48px +- 입력창 높이: 48px +- 리스트 행 높이: 48px +- 터치 최소 영역: 48px + +--- + +## 적용 예시 + +```typescript +// 컴포넌트 내부에서 자동 적용 +function PopButton({ size = "lg", ...props }) { + const sizeStyles = { + sm: { height: 32, minWidth: 60, fontSize: 12 }, + md: { height: 40, minWidth: 80, fontSize: 14 }, + lg: { height: 48, minWidth: 100, fontSize: 16 }, // POP 기본 + xl: { height: 56, minWidth: 120, fontSize: 18 }, + industrial: { height: 60, minWidth: 140, fontSize: 20 }, + }; + + return ( +