# 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 ``` **변경 사항**: - 기존: `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` | 타입 변경 없음 (기존 구조 유지) | --- **결론**: 자동 줄바꿈 + 검토 필요 시스템으로 정보 손실 방지 및 사용자 부담 최소화