ERP-node/frontend/components/admin/dashboard/GRID_SYSTEM.md

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.tsGRID_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 } });

문제 해결

요소가 스냅되지 않는 경우

  1. snapToGrid 함수가 호출되는지 확인
  2. SNAP_THRESHOLD 값 확인 (너무 작으면 스냅 안 됨)
  3. 임시 상태가 제대로 초기화되는지 확인

그리드가 보이지 않는 경우

  1. 캔버스의 backgroundImage 스타일 확인
  2. getCellWithGap() 반환값 확인
  3. 브라우저 개발자 도구에서 배경 스타일 검사

성능 문제

  1. 트랜지션이 비활성화되었는지 확인
  2. 불필요한 리렌더링 방지 (React.memo 사용)
  3. 마우스 이벤트 리스너가 제대로 제거되는지 확인

참고 자료

  • 그리드 유틸리티: gridUtils.ts
  • 캔버스 컴포넌트: DashboardCanvas.tsx
  • 요소 컴포넌트: CanvasElement.tsx
  • 디자이너: DashboardDesigner.tsx