diff --git a/frontend/components/report/designer/ReportDesignerCanvas.tsx b/frontend/components/report/designer/ReportDesignerCanvas.tsx
index 21b1335d..84629f9e 100644
--- a/frontend/components/report/designer/ReportDesignerCanvas.tsx
+++ b/frontend/components/report/designer/ReportDesignerCanvas.tsx
@@ -5,6 +5,7 @@ import { useDrop } from "react-dnd";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
import { ComponentConfig } from "@/types/report";
import { CanvasComponent } from "./CanvasComponent";
+import { Ruler } from "./Ruler";
import { v4 as uuidv4 } from "uuid";
export function ReportDesignerCanvas() {
@@ -27,6 +28,7 @@ export function ReportDesignerCanvas() {
pasteComponents,
undo,
redo,
+ showRuler,
} = useReportDesigner();
const [{ isOver }, drop] = useDrop(() => ({
@@ -201,62 +203,82 @@ export function ReportDesignerCanvas() {
{/* 캔버스 스크롤 영역 */}
-
{
- canvasRef.current = node;
- drop(node);
- }}
- className={`relative mx-auto bg-white shadow-lg ${isOver ? "ring-2 ring-blue-500" : ""}`}
- style={{
- width: `${canvasWidth}mm`,
- minHeight: `${canvasHeight}mm`,
- backgroundImage: showGrid
- ? `
- linear-gradient(to right, #e5e7eb 1px, transparent 1px),
- linear-gradient(to bottom, #e5e7eb 1px, transparent 1px)
- `
- : undefined,
- backgroundSize: showGrid ? `${gridSize}px ${gridSize}px` : undefined,
- }}
- onClick={handleCanvasClick}
- >
- {/* 정렬 가이드라인 렌더링 */}
- {alignmentGuides.vertical.map((x, index) => (
-
- ))}
- {alignmentGuides.horizontal.map((y, index) => (
-
- ))}
-
- {/* 컴포넌트 렌더링 */}
- {components.map((component) => (
-
- ))}
-
- {/* 빈 캔버스 안내 */}
- {components.length === 0 && (
-
-
왼쪽에서 컴포넌트를 드래그하여 추가하세요
+ {/* 눈금자와 캔버스를 감싸는 컨테이너 */}
+
+ {/* 좌상단 코너 + 가로 눈금자 */}
+ {showRuler && (
+
+ {/* 좌상단 코너 (20x20) */}
+
+ {/* 가로 눈금자 */}
+
)}
+
+ {/* 세로 눈금자 + 캔버스 */}
+
+ {/* 세로 눈금자 */}
+ {showRuler &&
}
+
+ {/* 캔버스 */}
+
{
+ canvasRef.current = node;
+ drop(node);
+ }}
+ className={`relative bg-white shadow-lg ${isOver ? "ring-2 ring-blue-500" : ""}`}
+ style={{
+ width: `${canvasWidth}mm`,
+ minHeight: `${canvasHeight}mm`,
+ backgroundImage: showGrid
+ ? `
+ linear-gradient(to right, #e5e7eb 1px, transparent 1px),
+ linear-gradient(to bottom, #e5e7eb 1px, transparent 1px)
+ `
+ : undefined,
+ backgroundSize: showGrid ? `${gridSize}px ${gridSize}px` : undefined,
+ }}
+ onClick={handleCanvasClick}
+ >
+ {/* 정렬 가이드라인 렌더링 */}
+ {alignmentGuides.vertical.map((x, index) => (
+
+ ))}
+ {alignmentGuides.horizontal.map((y, index) => (
+
+ ))}
+
+ {/* 컴포넌트 렌더링 */}
+ {components.map((component) => (
+
+ ))}
+
+ {/* 빈 캔버스 안내 */}
+ {components.length === 0 && (
+
+
왼쪽에서 컴포넌트를 드래그하여 추가하세요
+
+ )}
+
+
diff --git a/frontend/components/report/designer/ReportDesignerToolbar.tsx b/frontend/components/report/designer/ReportDesignerToolbar.tsx
index 3b637011..b554d191 100644
--- a/frontend/components/report/designer/ReportDesignerToolbar.tsx
+++ b/frontend/components/report/designer/ReportDesignerToolbar.tsx
@@ -28,6 +28,7 @@ import {
ChevronUp,
Lock,
Unlock,
+ Ruler as RulerIcon,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
@@ -83,6 +84,8 @@ export function ReportDesignerToolbar() {
toggleLock,
lockComponents,
unlockComponents,
+ showRuler,
+ setShowRuler,
} = useReportDesigner();
const [showPreview, setShowPreview] = useState(false);
const [showSaveAsTemplate, setShowSaveAsTemplate] = useState(false);
@@ -211,6 +214,16 @@ export function ReportDesignerToolbar() {
{snapToGrid && showGrid ? "Grid ON" : "Grid OFF"}
+