182 lines
5.2 KiB
Markdown
182 lines
5.2 KiB
Markdown
|
|
# ADR 005: 브레이크포인트 재설계 (기기 기반)
|
||
|
|
|
||
|
|
**날짜**: 2026-02-06
|
||
|
|
**상태**: 채택
|
||
|
|
**의사결정자**: 시스템 아키텍트
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 상황 (Context)
|
||
|
|
|
||
|
|
### 문제 1: 뷰어에서 모드 전환 불일치
|
||
|
|
|
||
|
|
```
|
||
|
|
브라우저 수동 리사이즈 시:
|
||
|
|
- useResponsiveMode 훅: 768px 이상 → "tablet" 판정
|
||
|
|
- GRID_BREAKPOINTS: 768~839px → "mobile_landscape" (6칸)
|
||
|
|
|
||
|
|
결과: 768~839px 구간에서 모드 불일치 발생
|
||
|
|
```
|
||
|
|
|
||
|
|
### 문제 2: 기존 브레이크포인트 근거 부족
|
||
|
|
|
||
|
|
```
|
||
|
|
기존 설정:
|
||
|
|
- mobile_portrait: ~599px
|
||
|
|
- mobile_landscape: 600~839px
|
||
|
|
- tablet_portrait: 840~1023px
|
||
|
|
|
||
|
|
문제: 실제 기기 뷰포트와 맞지 않음
|
||
|
|
- iPad Mini 세로: 768px (mobile_landscape로 분류됨)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 사용자 요구사항
|
||
|
|
|
||
|
|
> "현장 모바일 기기가 최소 8인치 ~ 최대 14인치,
|
||
|
|
> 핸드폰은 아이폰 미니 ~ 갤럭시 울트라 사이즈"
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 연구 (Research)
|
||
|
|
|
||
|
|
### 실제 기기 CSS 뷰포트 조사 (2026년 기준)
|
||
|
|
|
||
|
|
| 기기 | 화면 크기 | CSS 뷰포트 너비 |
|
||
|
|
|------|----------|----------------|
|
||
|
|
| iPhone SE | 4.7" | 375px |
|
||
|
|
| iPhone 16 Pro | 6.3" | 402px |
|
||
|
|
| Galaxy S25 Ultra | 6.9" | 440px |
|
||
|
|
| iPad Mini 7 | 8.3" | 768px |
|
||
|
|
| iPad Pro 11 | 11" | 834px (세로), 1194px (가로) |
|
||
|
|
| iPad Pro 13 | 13" | 1024px (세로), 1366px (가로) |
|
||
|
|
|
||
|
|
### 업계 표준 브레이크포인트
|
||
|
|
|
||
|
|
| 프레임워크 | 모바일/태블릿 경계 | 태블릿/데스크톱 경계 |
|
||
|
|
|-----------|------------------|-------------------|
|
||
|
|
| Tailwind CSS | 768px | 1024px |
|
||
|
|
| Bootstrap 5 | 768px | 992px |
|
||
|
|
| Material Design 3 | 600px | 840px |
|
||
|
|
|
||
|
|
**공통점**: 768px, 1024px가 거의 표준
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 결정 (Decision)
|
||
|
|
|
||
|
|
### 채택: 기기 기반 브레이크포인트
|
||
|
|
|
||
|
|
| 모드 | 너비 범위 | 변경 전 | 근거 |
|
||
|
|
|------|----------|--------|------|
|
||
|
|
| mobile_portrait | 0~479px | 0~599px | 스마트폰 세로 최대 440px |
|
||
|
|
| mobile_landscape | 480~767px | 600~839px | 스마트폰 가로, 767px까지 |
|
||
|
|
| tablet_portrait | 768~1023px | 840~1023px | iPad Mini 768px 포함 |
|
||
|
|
| tablet_landscape | 1024px+ | 동일 | 대형 태블릿 가로 |
|
||
|
|
|
||
|
|
### 핵심 변경
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// pop-layout.ts - GRID_BREAKPOINTS
|
||
|
|
mobile_portrait: { maxWidth: 479 } // was 599
|
||
|
|
mobile_landscape: { minWidth: 480, maxWidth: 767 } // was 600, 839
|
||
|
|
tablet_portrait: { minWidth: 768, maxWidth: 1023 } // was 840, 1023
|
||
|
|
tablet_landscape: { minWidth: 1024 } // 동일
|
||
|
|
|
||
|
|
// pop-layout.ts - detectGridMode()
|
||
|
|
if (viewportWidth < 480) return "mobile_portrait"; // was 600
|
||
|
|
if (viewportWidth < 768) return "mobile_landscape"; // was 840
|
||
|
|
if (viewportWidth < 1024) return "tablet_portrait";
|
||
|
|
|
||
|
|
// useDeviceOrientation.ts - BREAKPOINTS
|
||
|
|
TABLET_MIN: 768 // was 840
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 구현 (Implementation)
|
||
|
|
|
||
|
|
### 수정 파일
|
||
|
|
|
||
|
|
| 파일 | 변경 내용 |
|
||
|
|
|------|----------|
|
||
|
|
| `pop-layout.ts` | GRID_BREAKPOINTS 값 수정, detectGridMode() 조건 수정 |
|
||
|
|
| `useDeviceOrientation.ts` | BREAKPOINTS.TABLET_MIN = 768 |
|
||
|
|
| `PopCanvas.tsx` | VIEWPORT_PRESETS width 값 조정 |
|
||
|
|
| `page.tsx (뷰어)` | detectGridMode() 사용으로 일관성 확보 |
|
||
|
|
|
||
|
|
### 뷰어 모드 감지 방식 변경
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// 변경 전: useResponsiveModeWithOverride만 사용
|
||
|
|
const currentModeKey = getModeKey(deviceType, isLandscape);
|
||
|
|
|
||
|
|
// 변경 후: 프리뷰 모드와 일반 모드 분리
|
||
|
|
const currentModeKey = isPreviewMode
|
||
|
|
? getModeKey(deviceType, isLandscape) // 프리뷰: 수동 선택
|
||
|
|
: detectGridMode(viewportWidth); // 일반: 너비 기반
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 결과 (Consequences)
|
||
|
|
|
||
|
|
### 긍정적 효과
|
||
|
|
|
||
|
|
| 효과 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| **기기 커버리지** | 아이폰 SE ~ 갤럭시 울트라, 8~14인치 태블릿 모두 포함 |
|
||
|
|
| **업계 표준 호환** | 768px, 1024px는 거의 모든 프레임워크 기준점 |
|
||
|
|
| **일관성 확보** | GRID_BREAKPOINTS와 detectGridMode() 완전 일치 |
|
||
|
|
| **직관적 매핑** | 스마트폰 세로/가로, 태블릿 세로/가로 자연스럽게 분류 |
|
||
|
|
|
||
|
|
### 트레이드오프
|
||
|
|
|
||
|
|
| 항목 | 설명 |
|
||
|
|
|------|------|
|
||
|
|
| **기존 데이터 영향** | 600~767px 구간이 6칸→6칸 (영향 없음) |
|
||
|
|
| **768~839px 변경** | 기존 6칸→8칸 (태블릿으로 재분류) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 세로 자동 확장 (추가 결정)
|
||
|
|
|
||
|
|
### 배경
|
||
|
|
|
||
|
|
> "세로는 신경쓸 필요가 없는 것 맞지?
|
||
|
|
> 그렇다면 캔버스도 세로 무한 스크롤이 가능해야겠네?"
|
||
|
|
|
||
|
|
### 결정
|
||
|
|
|
||
|
|
1. **뷰포트 프리셋에서 height 제거** (width만 유지)
|
||
|
|
2. **캔버스 높이 동적 계산** (컴포넌트 배치 기준)
|
||
|
|
3. **항상 여유 행 3개 유지** (추가 배치 공간)
|
||
|
|
4. **뷰어에서 터치 스크롤** 지원
|
||
|
|
|
||
|
|
### 구현
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// PopCanvas.tsx
|
||
|
|
const MIN_CANVAS_HEIGHT = 600;
|
||
|
|
const CANVAS_EXTRA_ROWS = 3;
|
||
|
|
|
||
|
|
const dynamicCanvasHeight = useMemo(() => {
|
||
|
|
const maxRowEnd = visibleComps.reduce((max, comp) => {
|
||
|
|
return Math.max(max, comp.row + comp.rowSpan);
|
||
|
|
}, 1);
|
||
|
|
|
||
|
|
const totalRows = maxRowEnd + CANVAS_EXTRA_ROWS;
|
||
|
|
return Math.max(MIN_CANVAS_HEIGHT, totalRows * rowHeight);
|
||
|
|
}, [layout.components, ...]);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 관련 문서
|
||
|
|
|
||
|
|
- [003-v5-grid-system.md](./003-v5-grid-system.md) - v5 그리드 시스템 채택
|
||
|
|
- [006-auto-wrap-review-system.md](./006-auto-wrap-review-system.md) - 자동 줄바꿈
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**결론**: 실제 기기 뷰포트 기반 브레이크포인트로 일관성 확보 + 세로 무한 스크롤로 UX 개선
|