개발일지/Next.js

[Konva.js] Next.js + TypeScript에서 그림판 만들기 - #1 (Fabric.js vs Konva.js)

ayeongjin 2025. 2. 19. 01:57

🎨 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 같은 브라우저 전용 라이브러리는 서버에서 실행되면 오류가 발생했다.

이를 해결하기 위해

  1. dynamic import를 사용하여 클라이언트에서만 KonvaCanvas를 로드
  2. next.config.mjs에서 konva를 서버에서 제외하는 설정 추가

이렇게 SSR 문제를 해결하는 방법을 직접 적용하면서 Next.js에서 클라이언트 전용 라이브러리를 다루는 법을 배웠다.

다음은 undo, redo 기능 추가와 펜 굵기, 종류 설정을 해야겠다.

 

짜잔 ~ 🌸