ERP-node/popdocs/decisions/006-auto-wrap-review-system.md

221 lines
6.2 KiB
Markdown

# ADR 006: v5.1 자동 줄바꿈 + 검토 필요 시스템
**날짜**: 2026-02-06
**상태**: 채택
**의사결정자**: 시스템 아키텍트
---
## 상황 (Context)
v5 반응형 레이아웃에서 "화면 밖" 개념으로 컴포넌트를 처리했으나, 다음 문제가 발생했습니다:
### 문제 1: 정보 손실
```
12칸 모드:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ A │ B (col=5, 6칸) │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
4칸 모드 (기존):
┌────┬────┬────┬────┐ 화면 밖:
│ A │ │ - B
└────┴────┴────┴────┘
↑ A만 보임 ↑ 뷰어에서 안 보임!
```
### 문제 2: 사용자 의도 불일치
사용자가 기대한 "화면 밖" 역할:
- ❌ 컴포넌트 숨김 (현재 동작)
- ✅ "이 컴포넌트 검토 필요" 알림
---
## 결정 (Decision)
### 채택: 자동 줄바꿈 + 검토 필요 시스템
```
col > maxCol → 자동으로 맨 아래에 배치 (줄바꿈)
오버라이드 없음 → "검토 필요" 알림
```
---
## 구현 (Implementation)
### 1. 자동 줄바꿈 로직
**파일**: `gridUtils.ts` - `convertAndResolvePositions()`
```typescript
// 단계별 처리:
1. 비율 변환 + 원본 col 보존
converted = components.map(comp => ({
id: comp.id,
position: convertPositionToMode(comp.position, targetMode),
originalCol: comp.position.col, // ⭐ 원본 보존
}))
2. 정상 vs 초과 분리
normalComponents = originalCol targetColumns
overflowComponents = originalCol > targetColumns
3. 초과 컴포넌트 자동 배치
maxRow = normalComponents의 최대 row
overflowComponents col=1, row=맨아래+1
4. 겹침 해결
resolveOverlaps([...normalComponents, ...wrappedComponents])
```
### 2. 검토 필요 판별
**파일**: `gridUtils.ts` - `needsReview()`
```typescript
function needsReview(
currentMode: GridMode,
hasOverride: boolean
): boolean {
// 12칸 모드는 기본 모드이므로 검토 불필요
if (GRID_BREAKPOINTS[currentMode].columns === 12) return false;
// 오버라이드가 있으면 이미 편집함 → 검토 완료
if (hasOverride) return false;
// 오버라이드 없으면 → 검토 필요
return true;
}
```
**판단 기준 (최종)**: "이 모드에서 편집했냐 안 했냐"
### 3. 검토 필요 패널
**파일**: `PopCanvas.tsx` - `ReviewPanel`
```typescript
// 필터링
const reviewComponents = visibleComponents.filter(comp => {
const hasOverride = !!layout.overrides?.[currentMode]?.positions?.[comp.id];
return needsReview(currentMode, hasOverride);
});
// UI
<ReviewPanel
components={reviewComponents}
onSelectComponent={onSelectComponent} // 클릭 선택
/>
```
**변경 사항**:
- 기존: `OutOfBoundsPanel` (주황색, 드래그로 복원)
- 변경: `ReviewPanel` (파란색, 클릭으로 선택)
---
## 결과 (Consequences)
### 긍정적 효과
| 효과 | 설명 |
|------|------|
| **정보 손실 방지** | 모든 컴포넌트가 항상 그리드 안에 표시됨 |
| **사용자 부담 감소** | 자동 배치를 먼저 제공, 필요시에만 편집 |
| **의도 명확화** | "숨김" ≠ "검토 필요" (기능 분리) |
| **뷰어 호환** | 자동 배치가 뷰어에도 적용됨 |
### 트레이드오프
| 항목 | 설명 |
|------|------|
| **스크롤 증가** | 아래로 자동 배치되면 페이지가 길어질 수 있음 |
| **자동 배치 품질** | 사용자가 원하지 않는 위치에 배치될 수 있음 |
---
## 사용자 시나리오
### 시나리오 1: 수용 (자동 배치 그대로)
```
1. 12칸에서 컴포넌트 A, B, C 배치
2. 4칸 모드로 전환
3. 시스템: 자동 배치 + "검토 필요 (3개)" 알림
4. 사용자: 확인 → "괜찮네" → 아무것도 안 함
5. 결과: 자동 배치 유지 (오버라이드 없음)
```
### 시나리오 2: 편집 (오버라이드 저장)
```
1. 12칸에서 컴포넌트 A, B, C 배치
2. 4칸 모드로 전환
3. 시스템: 자동 배치 + "검토 필요 (3개)" 알림
4. 사용자: A 클릭 → 드래그/리사이즈
5. 결과: A 오버라이드 저장 → A 검토 완료
6. "검토 필요 (2개)" (B, C만 남음)
```
### 시나리오 3: 보류 (나중에)
```
1. 12칸에서 컴포넌트 A, B, C 배치
2. 4칸 모드로 전환
3. 시스템: 자동 배치 + "검토 필요 (3개)" 알림
4. 사용자: 다른 모드로 전환 또는 저장
5. 결과: 자동 배치 유지, 나중에도 "검토 필요" 표시
```
---
## 기능 비교
| 구분 | 역할 | 뷰어에서 | 판단 기준 |
|------|------|---------|----------|
| **검토 필요** | 자동 배치 알림 | **보임** | 오버라이드 없음 |
| **숨김** | 의도적 숨김 | **안 보임** | hidden 배열에 ID |
---
## 대안 (Alternatives Considered)
### A안: 완전 자동 (채택 ✅)
- 모든 초과 컴포넌트 자동 배치
- "검토 필요" 알림으로 확인 유도
- 업계 표준 (Webflow, Retool)
### B안: 선택적 자동 (미채택)
- 첫 전환 시만 자동 배치
- 사용자가 원하면 "화면 밖"으로 드래그
- 복잡성 증가
### C안: 수동 배치 유지 (미채택)
- 기존 "화면 밖" 패널 유지
- 사용자가 모든 모드 수동 편집
- 사용자 부담 과다
---
## 참고 자료
### 업계 표준 (2026년 기준)
- **Grafana, Tableau**: Masonry Layout (조적식)
- **Retool, PowerApps**: Vertical Stacking (수직 스택)
- **Webflow, Framer**: CSS Grid Auto-Placement
**공통점**: "Fluid Reflow (유동적 재배치)" - 정보 손실 방지
---
## 관련 파일
| 파일 | 변경 내용 |
|------|----------|
| `gridUtils.ts` | convertAndResolvePositions, needsReview 추가 |
| `PopCanvas.tsx` | ReviewPanel로 변경 |
| `PopRenderer.tsx` | isOutOfBounds import 제거 |
| `pop-layout.ts` | 타입 변경 없음 (기존 구조 유지) |
---
**결론**: 자동 줄바꿈 + 검토 필요 시스템으로 정보 손실 방지 및 사용자 부담 최소화