5.5 KiB
5.5 KiB
대시보드 그리드 시스템
개요
대시보드 캔버스는 12 컬럼 그리드 시스템을 사용하여 요소를 정렬하고 배치합니다. 모든 요소는 드래그 또는 리사이즈 종료 시 자동으로 그리드에 스냅됩니다.
그리드 설정
기본 설정 (gridUtils.ts)
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)
시각적 피드백
그리드 배경
캔버스 배경에 그리드 라인이 표시됩니다:
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'
요소 테두리
- 선택 안 됨: 회색 테두리
- 선택됨: 파란색 테두리 + 리사이즈 핸들 표시
- 드래그/리사이즈 중: 트랜지션 비활성화 (부드러운 움직임)
사용 예시
기본 사용
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셀)
경계 체크와 함께
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 }
// }
그리드 인덱스
좌표 → 인덱스
import { getGridIndex } from "./gridUtils";
const colIndex = getGridIndex(150); // 2 (3번째 컬럼)
const rowIndex = getGridIndex(100); // 1 (2번째 행)
인덱스 → 좌표
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를 수정:
export const GRID_CONFIG = {
COLUMNS: 12,
CELL_SIZE: 80, // 60 → 80 (셀 크기 증가)
GAP: 16, // 8 → 16 (간격 증가)
SNAP_THRESHOLD: 20, // 15 → 20 (스냅 범위 증가)
} as const;
스냅 비활성화
특정 요소에서 스냅을 비활성화하려면:
// 드래그 종료 시 스냅하지 않고 그냥 업데이트
onUpdate(element.id, {
position: { x: rawX, y: rawY }, // snapToGrid 호출 안 함
});
성능 최적화
트랜지션 제어
드래그/리사이즈 중에는 CSS 트랜지션을 비활성화:
className={`
${(isDragging || isResizing) ? 'transition-none' : 'transition-all duration-150'}
`}
임시 상태 사용
마우스 이동 중에는 임시 위치/크기만 업데이트하고, 마우스 업 시에만 실제 스냅된 값으로 업데이트:
// 드래그 중
setTempPosition({ x: rawX, y: rawY });
// 드래그 종료
const snapped = snapToGrid(tempPosition.x);
onUpdate(element.id, { position: { x: snapped, y: snapped } });
문제 해결
요소가 스냅되지 않는 경우
snapToGrid함수가 호출되는지 확인SNAP_THRESHOLD값 확인 (너무 작으면 스냅 안 됨)- 임시 상태가 제대로 초기화되는지 확인
그리드가 보이지 않는 경우
- 캔버스의
backgroundImage스타일 확인 getCellWithGap()반환값 확인- 브라우저 개발자 도구에서 배경 스타일 검사
성능 문제
- 트랜지션이 비활성화되었는지 확인
- 불필요한 리렌더링 방지 (React.memo 사용)
- 마우스 이벤트 리스너가 제대로 제거되는지 확인
참고 자료
- 그리드 유틸리티:
gridUtils.ts - 캔버스 컴포넌트:
DashboardCanvas.tsx - 요소 컴포넌트:
CanvasElement.tsx - 디자이너:
DashboardDesigner.tsx