🎨 Next.js + TypeScript에서 그림판 만들기 (Fabric.js vs Konva.js)
1. 프로젝트 배경
Next.js에서 그림판 기능을 구현하려고 처음에는 fabric.js를 사용했다.
하지만 몇 가지 문제 때문에 리액트 친화적인 konva.js로 전환했다.
❗️ Fabric.js의 문제점
- 공식 문서에서 undo / redo 기능을 지원하지 않음 → _objects 같은 내부 비공식 API를 써야 해서 위험
- React에서 상태 관리하기 어려움 → 성능 최적화가 부족
- TypeScript 지원 부족 → 공식적으로 잘 문서화되지 않음
✅ Konva.js를 선택한 이유
- React 친화적 (react-konva 사용)
- Undo/Redo 상태관리가 쉬움 (State 기반으로 관리)
- Canvas 기반으로 HTML5 성능 최적화
2. Konva.js 설치
npm install react-konva konva
- react-konva는 Konva.js를 React에서 쉽게 사용하도록 만든 라이브러리다.
3. Konva.js를 활용한 그림판 구현
https://github.com/konvajs/react-konva/
GitHub - konvajs/react-konva: React + Canvas = Love. JavaScript library for drawing complex canvas graphics using React.
React + Canvas = Love. JavaScript library for drawing complex canvas graphics using React. - konvajs/react-konva
github.com
react-konva 깃허브에 들어가면 다양한 소스코드를 볼 수 있다.
✅ KonvaCanvas.tsx (그림판 로직)
"use client"; // Next.js에서 클라이언트 전용 컴포넌트로 설정
import React, { useState } from "react";
import { Stage, Layer, Line } from "react-konva";
interface KonvaCanvasProps {
selectedColor: string;
}
export default function KonvaCanvas({ selectedColor }: KonvaCanvasProps) {
// 그려진 선들을 저장하는 상태
const [lines, setLines] = useState<{ points: number[]; stroke: string }[]>([]);
const [isDrawing, setIsDrawing] = useState(false); // 현재 드로잉 상태
// 마우스를 눌렀을 때 (그리기 시작)
const handleMouseDown = (e: any) => {
setIsDrawing(true);
const pos = e.target.getStage().getPointerPosition();
setLines([...lines, { points: [pos.x, pos.y], stroke: selectedColor }]);
};
// 마우스를 움직일 때 (그리는 중)
const handleMouseMove = (e: any) => {
if (!isDrawing) return;
const stage = e.target.getStage();
const point = stage.getPointerPosition();
let lastLine = lines[lines.length - 1];
lastLine.points = [...lastLine.points, point.x, point.y];
setLines([...lines.slice(0, lines.length - 1), lastLine]);
};
// 마우스를 뗄 때 (그리기 종료)
const handleMouseUp = () => {
setIsDrawing(false);
};
return (
<Stage width={500} height={500} className="border"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
>
<Layer>
{lines.map((line, i) => (
<Line key={i} points={line.points} stroke={line.stroke} strokeWidth={5} lineCap="round" />
))}
</Layer>
</Stage>
);
}
Stage → HTML5 Canvas 영역 생성
Layer → 여러 개의 도형을 그룹화
Line → 실제 그림을 구성하는 선
✅ CanvasWrapper.tsx (부모 컴포넌트)
"use client";
import dynamic from "next/dynamic";
import { useState } from "react";
import ColorPalette from "../molecules/ColorPalette";
import Card from "@/components/common/atoms/Card";
const KonvaCanvas = dynamic(() => import("./KonvaCanvas"), { ssr: false });
export default function CanvasWrapper() {
const [selectedColor, setSelectedColor] = useState("#000000");
return (
<Card width="w-full">
<div className="w-full h-full flex flex-col items-center gap-4">
<KonvaCanvas selectedColor={selectedColor} />
<ColorPalette selectedColor={selectedColor} onSelectColor={setSelectedColor} />
</div>
</Card>
);
}
- KonvaCanvas.tsx를 dynamic import로 불러와서 SSR(서버 사이드 렌더링) 문제 방지해야한다.
4. Next.js에서 Konva.js SSR 문제 해결
Next.js는 기본적으로 서버에서 실행되는데, Konva.js는 브라우저에서만 동작한다.
그래서 서버에서 canvas를 찾으면서 "Module not found: Can't resolve 'canvas'" 오류가 발생했다.
✅ 해결 방법 (next.config.mjs 수정)
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
webpack: (config, { isServer }) => {
// 서버에서 'konva'를 실행하지 않도록 제외
if (isServer) {
config.externals = [...(config.externals || []), "konva"];
}
return config;
},
};
export default nextConfig;
- 이 설정을 추가하면 Next.js 서버에서 konva를 로드하지 않도록 차단된다.
5. 마무리
Next.js에서는 기본적으로 서버 사이드 렌더링(SSR) 이 실행되는데,
Konva.js 같은 브라우저 전용 라이브러리는 서버에서 실행되면 오류가 발생했다.
이를 해결하기 위해
- dynamic import를 사용하여 클라이언트에서만 KonvaCanvas를 로드
- next.config.mjs에서 konva를 서버에서 제외하는 설정 추가
이렇게 SSR 문제를 해결하는 방법을 직접 적용하면서 Next.js에서 클라이언트 전용 라이브러리를 다루는 법을 배웠다.
다음은 undo, redo 기능 추가와 펜 굵기, 종류 설정을 해야겠다.

짜잔 ~ 🌸
'개발일지 > Next.js' 카테고리의 다른 글
[SSR/CSR] Next.js에서 초기 스타일이 깜빡이는 FOUC 문제 해결하기 (0) | 2025.02.25 |
---|---|
[LocalStorage/TailwindCSS] Next.js에서 로컬스토리지에 저장하기 - #2 (폰트 및 폰트사이즈 적용) (0) | 2025.02.24 |
[LocalStorage/TailwindCSS] Next.js에서 로컬스토리지에 저장하기 - #1 (멀티 테마 적용) (0) | 2025.02.23 |
[Konva.js] Next.js + TypeScript에서 그림판 만들기 - #3 (그림 저장하기) (0) | 2025.02.21 |
[Konva.js] Next.js + TypeScript에서 그림판 만들기 - #2 (Undo/Redo & 지우개 기능 추가) (0) | 2025.02.20 |