diff --git a/frontend/components/report/designer/CanvasComponent.tsx b/frontend/components/report/designer/CanvasComponent.tsx
index b6a16839..cf58991c 100644
--- a/frontend/components/report/designer/CanvasComponent.tsx
+++ b/frontend/components/report/designer/CanvasComponent.tsx
@@ -27,6 +27,7 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
const isSelected = selectedComponentId === component.id;
const isMultiSelected = selectedComponentIds.includes(component.id);
+ const isLocked = component.locked === true;
// 드래그 시작
const handleMouseDown = (e: React.MouseEvent) => {
@@ -34,6 +35,15 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
return;
}
+ // 잠긴 컴포넌트는 드래그 불가
+ if (isLocked) {
+ e.stopPropagation();
+ // Ctrl/Cmd 키 감지 (다중 선택)
+ const isMultiSelect = e.ctrlKey || e.metaKey;
+ selectComponent(component.id, isMultiSelect);
+ return;
+ }
+
e.stopPropagation();
// Ctrl/Cmd 키 감지 (다중 선택)
@@ -49,6 +59,12 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
// 리사이즈 시작
const handleResizeStart = (e: React.MouseEvent) => {
+ // 잠긴 컴포넌트는 리사이즈 불가
+ if (isLocked) {
+ e.stopPropagation();
+ return;
+ }
+
e.stopPropagation();
setIsResizing(true);
setResizeStart({
@@ -277,8 +293,16 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
return (
{renderContent()}
- {/* 리사이즈 핸들 (선택된 경우만) */}
- {isSelected && (
+ {/* 잠금 표시 */}
+ {isLocked && (
+
🔒
+ )}
+
+ {/* 리사이즈 핸들 (선택된 경우만, 잠금 안 된 경우만) */}
+ {isSelected && !isLocked && (
0 ? selectedComponentIds : ([selectedComponentId].filter(Boolean) as string[]);
- // 각 컴포넌트 이동
+ // 각 컴포넌트 이동 (잠긴 컴포넌트는 제외)
idsToMove.forEach((id) => {
const component = components.find((c) => c.id === id);
- if (!component) return;
+ if (!component || component.locked) return;
let newX = component.x;
let newY = component.y;
@@ -132,12 +132,20 @@ export function ReportDesignerCanvas() {
return;
}
- // Delete 키: 삭제
+ // Delete 키: 삭제 (잠긴 컴포넌트는 제외)
if (e.key === "Delete") {
if (selectedComponentIds.length > 0) {
- selectedComponentIds.forEach((id) => removeComponent(id));
+ selectedComponentIds.forEach((id) => {
+ const component = components.find((c) => c.id === id);
+ if (component && !component.locked) {
+ removeComponent(id);
+ }
+ });
} else if (selectedComponentId) {
- removeComponent(selectedComponentId);
+ const component = components.find((c) => c.id === selectedComponentId);
+ if (component && !component.locked) {
+ removeComponent(selectedComponentId);
+ }
}
}
diff --git a/frontend/components/report/designer/ReportDesignerToolbar.tsx b/frontend/components/report/designer/ReportDesignerToolbar.tsx
index f1dcde6f..3b637011 100644
--- a/frontend/components/report/designer/ReportDesignerToolbar.tsx
+++ b/frontend/components/report/designer/ReportDesignerToolbar.tsx
@@ -26,6 +26,8 @@ import {
ChevronsDown,
ChevronsUp,
ChevronUp,
+ Lock,
+ Unlock,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
@@ -78,6 +80,9 @@ export function ReportDesignerToolbar() {
sendToBack,
bringForward,
sendBackward,
+ toggleLock,
+ lockComponents,
+ unlockComponents,
} = useReportDesigner();
const [showPreview, setShowPreview] = useState(false);
const [showSaveAsTemplate, setShowSaveAsTemplate] = useState(false);
@@ -357,6 +362,37 @@ export function ReportDesignerToolbar() {
+ {/* 잠금 드롭다운 */}
+
+
+
+
+
+
+
+ 토글 (잠금/해제)
+
+
+
+
+ 잠금 설정
+
+
+
+ 잠금 해제
+
+
+
+