# 대시보드 그리드 시스템 ## 개요 대시보드 캔버스는 **12 컬럼 그리드 시스템**을 사용하여 요소를 정렬하고 배치합니다. 모든 요소는 드래그 또는 리사이즈 종료 시 자동으로 그리드에 스냅됩니다. ## 그리드 설정 ### 기본 설정 (`gridUtils.ts`) ```typescript GRID_CONFIG = { COLUMNS: 12, // 12 컬럼 CELL_SIZE: 60, // 60px x 60px 정사각형 셀 GAP: 8, // 셀 간격 8px SNAP_THRESHOLD: 15, // 스냅 임계값 15px }; ``` ### 실제 그리드 크기 - **셀 크기 (gap 포함)**: 68px (60px + 8px) - **전체 캔버스 너비**: 808px (12 \* 68px - 8px) - **셀 비율**: 1:1 (정사각형) ## 스냅 기능 ### 1. 위치 스냅 요소를 드래그하여 이동할 때: - **드래그 중**: 자유롭게 이동 (그리드 무시) - **드래그 종료**: 가장 가까운 그리드 포인트에 자동 스냅 - **스냅 계산**: `Math.round(value / 68) * 68` ### 2. 크기 스냅 요소의 크기를 조절할 때: - **리사이즈 중**: 자유롭게 크기 조절 - **리사이즈 종료**: 그리드 단위로 스냅 - **최소 크기**: 2셀 x 2셀 (136px x 136px) ### 3. 드롭 스냅 사이드바에서 새 요소를 드래그 앤 드롭할 때: - 드롭 위치가 자동으로 가장 가까운 그리드 포인트에 스냅 - 기본 크기: - 차트: 4 x 3 셀 (264px x 196px) - 위젯: 2 x 2 셀 (136px x 136px) ## 시각적 피드백 ### 그리드 배경 캔버스 배경에 그리드 라인이 표시됩니다: ```typescript backgroundImage: ` linear-gradient(rgba(59, 130, 246, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(59, 130, 246, 0.1) 1px, transparent 1px) `, backgroundSize: '68px 68px' ``` ### 요소 테두리 - **선택 안 됨**: 회색 테두리 - **선택됨**: 파란색 테두리 + 리사이즈 핸들 표시 - **드래그/리사이즈 중**: 트랜지션 비활성화 (부드러운 움직임) ## 사용 예시 ### 기본 사용 ```typescript import { snapToGrid, snapSizeToGrid } from "./gridUtils"; // 위치 스냅 const snappedX = snapToGrid(123); // 136 (가장 가까운 그리드) const snappedY = snapToGrid(45); // 68 // 크기 스냅 const snappedWidth = snapSizeToGrid(250); // 264 (4셀) const snappedHeight = snapSizeToGrid(180); // 196 (3셀) ``` ### 경계 체크와 함께 ```typescript import { snapBoundsToGrid } from "./gridUtils"; const snapped = snapBoundsToGrid( { position: { x: 123, y: 45 }, size: { width: 250, height: 180 }, }, canvasWidth, canvasHeight, ); // 결과: // { // position: { x: 136, y: 68 }, // size: { width: 264, height: 196 } // } ``` ## 그리드 인덱스 ### 좌표 → 인덱스 ```typescript import { getGridIndex } from "./gridUtils"; const colIndex = getGridIndex(150); // 2 (3번째 컬럼) const rowIndex = getGridIndex(100); // 1 (2번째 행) ``` ### 인덱스 → 좌표 ```typescript import { gridIndexToCoordinate } from "./gridUtils"; const x = gridIndexToCoordinate(0); // 0 (1번째 컬럼) const y = gridIndexToCoordinate(1); // 68 (2번째 행) const z = gridIndexToCoordinate(11); // 748 (12번째 컬럼) ``` ## 레이아웃 권장사항 ### 일반 차트 - **권장 크기**: 4 x 3 셀 (264px x 196px) - **최소 크기**: 2 x 2 셀 (136px x 136px) - **최대 크기**: 12 x 8 셀 (808px x 536px) ### 작은 위젯 - **권장 크기**: 2 x 2 셀 (136px x 136px) - **최소 크기**: 2 x 2 셀 - **최대 크기**: 4 x 4 셀 (264px x 264px) ### 큰 차트/대시보드 - **권장 크기**: 6 x 4 셀 (400px x 264px) - **풀 너비**: 12 셀 (808px) ## 커스터마이징 ### 그리드 크기 변경 `gridUtils.ts`의 `GRID_CONFIG`를 수정: ```typescript export const GRID_CONFIG = { COLUMNS: 12, CELL_SIZE: 80, // 60 → 80 (셀 크기 증가) GAP: 16, // 8 → 16 (간격 증가) SNAP_THRESHOLD: 20, // 15 → 20 (스냅 범위 증가) } as const; ``` ### 스냅 비활성화 특정 요소에서 스냅을 비활성화하려면: ```typescript // 드래그 종료 시 스냅하지 않고 그냥 업데이트 onUpdate(element.id, { position: { x: rawX, y: rawY }, // snapToGrid 호출 안 함 }); ``` ## 성능 최적화 ### 트랜지션 제어 드래그/리사이즈 중에는 CSS 트랜지션을 비활성화: ```typescript className={` ${(isDragging || isResizing) ? 'transition-none' : 'transition-all duration-150'} `} ``` ### 임시 상태 사용 마우스 이동 중에는 임시 위치/크기만 업데이트하고, 마우스 업 시에만 실제 스냅된 값으로 업데이트: ```typescript // 드래그 중 setTempPosition({ x: rawX, y: rawY }); // 드래그 종료 const snapped = snapToGrid(tempPosition.x); onUpdate(element.id, { position: { x: snapped, y: snapped } }); ``` ## 문제 해결 ### 요소가 스냅되지 않는 경우 1. `snapToGrid` 함수가 호출되는지 확인 2. `SNAP_THRESHOLD` 값 확인 (너무 작으면 스냅 안 됨) 3. 임시 상태가 제대로 초기화되는지 확인 ### 그리드가 보이지 않는 경우 1. 캔버스의 `backgroundImage` 스타일 확인 2. `getCellWithGap()` 반환값 확인 3. 브라우저 개발자 도구에서 배경 스타일 검사 ### 성능 문제 1. 트랜지션이 비활성화되었는지 확인 2. 불필요한 리렌더링 방지 (React.memo 사용) 3. 마우스 이벤트 리스너가 제대로 제거되는지 확인 ## 참고 자료 - **그리드 유틸리티**: `gridUtils.ts` - **캔버스 컴포넌트**: `DashboardCanvas.tsx` - **요소 컴포넌트**: `CanvasElement.tsx` - **디자이너**: `DashboardDesigner.tsx`