Uuno 에디터 개발 강지수
지난 포스팅에서 작성한 캔버스의 영역과 범위에 관한 기술적 고민에 대한 후속으로, 실제 구현 과정에서 발견한 문제점과 해결책에 대한 글
프로젝트 초기에는 사이드바가 열리고 닫힐 때 Stage의 크기를 동적으로 조절하는 방식을 선택했습니다. 이론적으로는 합리적인 접근법처럼 보였으나, 실제 구현 과정에서 여러 문제에 직면했습니다.
'use client';
import EditorCanvas from '@/components/editor/editor-canvas';
import EditorBottomTab from '@/components/editor/editor-ui/bottomTab/editor-bottom-tab';
import EditorSideBar from '@/components/editor/editor-ui/sidebar/editor-sidebar';
import EditorTopbar from '@/components/editor/editor-ui/topbar/editor-topbar';
import { sideBarStore } from '@/store/editor.sidebar.store';
import { useEffect, useRef, useState } from 'react';
const EditPage = () => {
const containerRef = useRef<HTMLDivElement>(null);
const sidebarStatus = sideBarStore((state) => state.sidebarStatus);
const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const updateSize = () => {
if (containerRef.current) {
const { width, height } = containerRef.current.getBoundingClientRect();
setCanvasSize({ width, height });
}
};
updateSize();
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, [sidebarStatus]);
return (
<div className='flex h-[calc(100vh-64px)] flex-row overflow-hidden'>
<EditorSideBar />
<div
ref={containerRef}
className='flex flex-1 flex-col bg-slate-400'
>
<EditorTopbar />
<EditorCanvas canvasSize={canvasSize} />
<EditorBottomTab />
</div>
</div>
);
};
export default EditPage;
이 접근법의 문제점:
문제를 해결하기 위해 미리캔버스와 같은 전문 디자인 툴의
구현 방식을 분석.
대부분의 디자인 툴은 Stage 크기를 고정하고 줌인/줌아웃으로 화면 배율을 조절하는 방식을 채택
다음과 같이 구조를 개선
const stageWidth = BASE_STAGE_WIDTH * zoom;
const stageHeight = BASE_STAGE_HEIGHT * zoom;
const containerWidth = BASE_CONTAINER_WIDTH * zoom;
const containerHeight = BASE_CONTAINER_HEIGHT * zoom;
const currentStageWidth = isHorizontal ? stageWidth : stageHeight;
const currentStageHeight = isHorizontal ? stageHeight : stageWidth;
const currentContainerWidth = isHorizontal ? containerWidth : containerHeight;
const currentContainerHeight = isHorizontal
? containerHeight
: containerWidth;
return (
<div
className={`flex flex-col items-center justify-center bg-white p-[18px]`}
style={{
boxShadow: '1px 1px 4px 1px rgba(0, 0, 0, 0.25)',
width: `${currentContainerWidth}px`,
height: `${currentContainerHeight}px`,
}}
>
<Stage
style={{ border: '1px dashed var(--Gray-60, #878A93)' }}
width={currentStageWidth}
height={currentStageHeight}
scale={{ x: zoom, y: zoom }}
onWheel={handleWheel}
onMouseDown={(e) => {
if (e.target === e.target.getStage()) {
setSelectedElementId(null);
setEditingElementId(null);
setSelectedElementType(null);
}
}}
className='bg-white'
>
transform : scle()
을 사용하는 방법scaleX
, scaleY
속성 활용<Stage
style={{ border: '1px dashed var(--Gray-60, #878A93)' }}
width={currentStageWidth}
height={currentStageHeight}
scale={{ x: zoom, y: zoom }}
>
캔버스 기반 에디터를 개발할 때 가장 중요한 결정 중 하나는 캔버스 크기 관리 전략입니다. 처음에는 동적으로 크기를 조절하는 방식이 유연해 보일 수 있지만, 실제로는 고정 크기 접근법이 많은 문제를 해결해주고 사용자 경험도 개선합니다.
미리캔버스나 Figma 같은 전문 디자인 툴이 이러한 접근법을 사용하는 데는 이유가 있습니다. 그들의 경험에서 배우고 적용함으로써, 우리의 에디터도 더 안정적이고 사용하기 쉬운 형태로 발전할 수 있었습니다.