본문으로 건너뛰기

안동민 개발노트

본문 시작

SVG와 캔버스 기초

DOM으로 다루는 벡터 SVG와 픽셀을 그리는 Canvas를 비교하고 2D 컨텍스트로 간단한 애니메이션을 만듭니다.

지금까지 우리는 HTML을 사용하여 웹 페이지의 구조를 만들고, 텍스트, 링크, 일반 이미지(<img>), 그리고 비디오/오디오와 같은 멀티미디어 콘텐츠를 삽입하는 방법을 학습했습니다.

웹 그래픽을 다루는 두 방식인 SVG와 Canvas를 비교합니다.

HTML5는 웹 표준만으로도 복잡하고 동적인 그래픽을 웹 페이지에 구현할 수 있는 길을 열었습니다.

이 장에서는 벡터 기반 SVG(Scalable Vector Graphics)와 픽셀 기반 Canvas의 기본 개념, 장단점, 사용법을 정리합니다.

이 두 기술은 웹 애플리케이션에서 다이어그램, 차트, 게임, 인터랙티브 애니메이션 등 다양한 시각 요소를 구현하는 데 필수적인 기반이 됩니다.

SVG와 Canvas 렌더링 상태

SVG는 도형 객체가 DOM에 남는 retained mode이고, Canvas는 픽셀을 다시 그리는 immediate mode에 가깝다.

비교 축SVGCanvas판단 기준
상태 보관도형이 DOM 요소로 남음그린 결과만 bitmap에 남음나중에 개별 요소를 선택해야 하는가
이벤트요소별 클릭과 hover 가능좌표 hit-test 직접 구현개별 도형 상호작용이 많은가
접근성title, desc, role 연결 쉬움대체 DOM이나 설명 필요그래픽 의미를 읽어야 하는가
성능요소 수가 많으면 DOM 비용 증가반복 렌더링과 픽셀 처리 유리프레임마다 다시 그리는가
해상도벡터라 확대가 선명devicePixelRatio 대응 필요고해상도 화면에서 흐려지지 않는가

SVG: 확장 가능한 벡터 그래픽

SVG는 XML 기반의 벡터 그래픽을 웹에 표현하기 위한 마크업 언어입니다.

벡터 그래픽이라는 것은 그림을 점, 선, 면 등의 수학적인 형태로 정의한다는 의미입니다.

JPG, PNG와 같은 비트맵 그래픽(픽셀 기반)과는 근본적으로 다릅니다.

SVG의 특징 및 장점

확장성 (Scalable): 가장 큰 특징이자 장점입니다.

벡터 방식이므로 아무리 확대해도 이미지가 깨지거나 흐려지지 않습니다.

로고, 아이콘, 다이어그램 등 다양한 해상도의 기기에서 선명하게 보여야 하는 그래픽에 매우 적합합니다.

작은 파일 크기: 복잡하지 않은 벡터 그래픽의 경우, 비트맵 이미지보다 파일 크기가 훨씬 작아 로딩 속도를 개선할 수 있습니다.

편집 용이성: SVG 코드는 XML 기반의 텍스트이므로, 텍스트 편집기나 SVG 편집 소프트웨어에서 쉽게 수정할 수 있습니다.

또한 CSS와 자바스크립트를 사용하여 색상, 크기, 위치 등을 동적으로 제어하고 애니메이션을 적용할 수 있습니다.

웹 접근성: SVG 요소는 의미론적인 정보(예: <title>, <desc>)를 포함할 수 있어, 스크린 리더와 같은 보조 기술이 그래픽의 내용을 이해하는 데 도움을 줍니다.

검색 가능: SVG는 텍스트 기반이므로, 검색 엔진이 이미지 내부의 텍스트를 인식할 수 있어 SEO에 유리할 수 있습니다.

SVG 기본 문법과 활용

SVG는 HTML 문서 내에 <svg> 태그를 사용하여 직접 삽입하거나, .svg 파일로 저장하여 <img> 태그나 CSS의 background-image 속성을 통해 참조할 수 있습니다.

여기서는 HTML 내에 직접 삽입하는 방식을 위주로 살펴봅니다.

  • <svg> 태그: SVG 그래픽을 그릴 캔버스 역할을 합니다. widthheight 속성으로 영역을 지정합니다.

    <svg width="200" height="200">
    </svg>
  • 기본 도형 그리기
    • <rect> (직사각형): x, y, width, height, fill, stroke, stroke-width 등의 속성을 사용합니다.
      <svg width="200" height="100">
          <rect x="10" y="10" width="80" height="80"
                fill="blue" stroke="black" stroke-width="3" />
      </svg>
    • <circle> (원): cx, cy (중심점 좌표), r (반지름), fill, stroke 등의 속성을 사용합니다.
      <svg width="200" height="100">
          <circle cx="50" cy="50" r="40" fill="red" stroke="purple" stroke-width="2" />
      </svg>
    • <line> (선): x1, y1 (시작점), x2, y2 (끝점), stroke, stroke-width 등의 속성을 사용합니다.
      <svg width="200" height="100">
          <line x1="10" y1="10" x2="190" y2="90" stroke="green" stroke-width="4" />
      </svg>
    • <polygon> (다각형): points 속성에 점들의 좌표를 공백으로 구분하여 나열합니다.
      <svg width="200" height="200">
          <polygon points="100,10 190,90 10,90" fill="orange" stroke="brown" stroke-width="2" />
      </svg>
    • <text> (텍스트): x, y (텍스트 시작 좌표), fill, font-size 등의 속성을 사용합니다.
      <svg width="200" height="100">
          <text x="10" y="50" fill="darkblue" font-size="20">Hello SVG!</text>
      </svg>
  • CSS와 자바스크립트를 이용한 제어: SVG 요소는 HTML 요소와 마찬가지로 CSS로 스타일링하거나 자바스크립트로 조작할 수 있습니다.

    <style>
        .my-circle {
            fill: #ffd700; /* 금색 */
            stroke: #cc0000;
            stroke-width: 5;
            transition: fill 0.3s ease; /* 호버 시 색상 변화 애니메이션 */
        }
        .my-circle:hover {
            fill: #ff4500; /* 오렌지색 */
        }
    </style>
    
    <svg width="100" height="100">
        <circle cx="50" cy="50" r="40" class="my-circle" id="interactiveCircle" />
    </svg>
    
    <script>
        const circle = document.getElementById('interactiveCircle');
        circle.addEventListener('click', () => {
            alert('SVG 원을 클릭했습니다!');
            circle.setAttribute('r', '50'); // 반지름 변경
        });
    </script>

아래 다이어그램은 SVG가 문서 안에서 요소 단위로 남기 때문에 가능한 조작 흐름을 정리한 것입니다.

SVG는 그림이면서 HTML처럼 조작되는 요소 모음

SVG 도형은 DOM에 남아 CSS, 이벤트, 접근성 설명을 각 요소 단위로 연결할 수 있다.

SVG 구성가능한 조작좋은 사용처주의점
rect/circle/path도형별 선택과 스타일 변경아이콘, 작은 차트, 다이어그램요소 수가 너무 많으면 무거움
CSSfill, stroke, class로 상태 표시테마와 hover 효과색만으로 상태를 구분
Event도형별 click, focus 처리대화형 지도와 그래프키보드 접근 누락
Accessibilitytitle, desc, role 제공정보 그래픽 설명화면에 보이지만 읽히지 않음
Scale해상도 독립 벡터로고와 선명한 UI 그래픽사진 같은 픽셀 작업에는 부적합

Canvas: 비트맵 그래픽 그리기

Canvas는 HTML5에서 도입된 <canvas> 태그와 자바스크립트 API를 사용하여 비트맵(픽셀) 기반의 2D 그래픽을 웹 페이지에 직접 그릴 수 있도록 하는 기술입니다.

캔버스라는 이름처럼, 마치 그림을 그리는 도화지와 같습니다.

Canvas의 특징 및 장점

픽셀 기반: 이미지를 픽셀 단위로 직접 조작합니다.

복잡한 그래픽, 이미지 필터, 게임 그래픽 등 픽셀 단위의 정교한 제어가 필요할 때 적합합니다.

자바스크립트 제어: 모든 그리기는 자바스크립트 코드를 통해 이루어집니다.

이를 통해 매우 동적이고 인터랙티브한 그래픽을 생성할 수 있습니다.

고성능: 복잡한 애니메이션이나 게임처럼 빠른 업데이트가 필요한 경우, Canvas는 웹 브라우저의 GPU 가속을 활용하여 높은 성능을 발휘할 수 있습니다.

다양한 활용: 차트, 그래프, 사진 편집기, 게임, 웹 기반 드로잉 툴 등 다양한 웹 애플리케이션에서 활용됩니다.

Canvas는 화면에 객체를 쌓아 두는 방식이 아니라, 현재 상태를 바탕으로 픽셀 결과를 계속 다시 그리는 방식으로 이해하면 쉽습니다.

픽셀 화면은 매 프레임 다시 그립니다

Canvas는 요소를 쌓는 방식이 아니라 그리기 명령의 결과를 비트맵으로 남기므로 상태와 접근성을 따로 관리해야 한다.

단계하는 일핵심 코드/값주의점
SizeCSS 크기와 실제 픽셀 크기 결정width, height, devicePixelRatio고해상도 화면에서 흐림
Context그리기 도구 가져오기getContext("2d")컨텍스트 없음 처리 누락
State색, 선, 폰트, 변환 설정fillStyle, strokeStyle, transform이전 상태가 다음 그림에 영향
Draw loop지우고 다시 그리기clearRect, requestAnimationFrame불필요한 전체 redraw
Fallback의미와 조작 보완대체 텍스트, 별도 DOM control캔버스 안 정보가 읽히지 않음

Canvas 기본 문법과 활용

  • <canvas> 태그: 그림을 그릴 영역을 정의합니다. widthheight 속성으로 캔버스의 크기를 지정합니다.

    <canvas id="myCanvas" width="400" height="200" style="border:1px solid #000;"></canvas>
    • <canvas> 태그 자체는 비어 있습니다. 모든 그리기 작업은 자바스크립트를 통해 이루어집니다.
    • style 속성은 단순히 캔버스 요소 자체의 시각적 스타일을 지정할 뿐, 캔버스 내부의 그림에는 영향을 주지 않습니다.
  • 자바스크립트를 이용한 그리기

    캔버스 요소 가져오기: document.getElementById()를 사용하여 <canvas> 요소를 가져옵니다.

    렌더링 컨텍스트 얻기: 캔버스에 그림을 그리기 위한 컨텍스트 객체를 얻습니다.

    2D 그래픽의 경우 getContext('2d')를 사용합니다.

    그리기 명령 실행: 컨텍스트 객체의 메서드를 사용하여 도형, 텍스트, 이미지 등을 그립니다.

    <canvas id="myDrawingCanvas" width="300" height="150" style="border:2px solid #ccc;"></canvas>
    <script>
        const canvas = document.getElementById('myDrawingCanvas');
        const ctx = canvas.getContext('2d'); // 2D 렌더링 컨텍스트 가져오기
        // 1. 사각형 그리기
        ctx.fillStyle = 'green'; // 채우기 색상 설정
        ctx.fillRect(10, 10, 100, 50); // (x, y, width, height)
        // 2. 원 그리기
        ctx.beginPath(); // 새로운 경로 시작
        ctx.arc(180, 75, 40, 0, 2 * Math.PI); // (x, y, 반지름, 시작각도, 끝각도)
        ctx.fillStyle = 'orange';
        ctx.fill(); // 채우기
        ctx.lineWidth = 3; // 선 두께
        ctx.strokeStyle = 'red'; // 선 색상
        ctx.stroke(); // 선 그리기
        // 3. 텍스트 그리기
        ctx.font = '24px Arial';
        ctx.fillStyle = 'darkblue';
        ctx.textAlign = 'center';
        ctx.fillText('Hello Canvas!', canvas.width / 2, 130); // (텍스트, x, y)
    </script>

SVG와 Canvas의 차이점 및 선택 기준

SVG와 캔버스 기초에서는 태그 의미, 브라우저 해석, 사용자에게 보이는 결과를 연결합니다.

SVG vs Canvas 선택 흐름도

그래픽 방식은 편집 대상인지, 요소별 의미가 필요한지, 픽셀을 얼마나 자주 다시 그리는지에 따라 달라진다.

질문SVG 쪽 답Canvas 쪽 답선택 이유
확대 품질이 중요한가벡터로 선명하게 유지픽셀 크기 조정 필요아이콘·차트는 SVG 우세
요소별 조작이 필요한가DOM 이벤트와 CSS 가능좌표 계산 필요클릭 가능한 도형은 SVG
매 프레임 갱신하는가DOM 업데이트 비용 증가픽셀 redraw에 강함게임·파티클은 Canvas
객체 수가 매우 많은가DOM 노드 수가 부담단일 surface에 그리기대량 시각화는 Canvas 후보
의미 설명이 필요한가title/desc 연결 쉬움대체 DOM 필요접근성 요구가 높으면 SVG 검토
특징SVG (Scalable Vector Graphics)Canvas
기반벡터 기반 (수학적 도형, XML 코드)비트맵/픽셀 기반 (래스터 이미지)
조작 방식각 도형이 DOM 요소로 존재, CSS/JS로 개별 조작 용이전체 캔버스를 하나의 비트맵으로 JS로 픽셀 조작
확장성무한히 확대해도 깨지지 않음 (해상도 독립적)확대하면 픽셀이 보이며 깨짐 (해상도 의존적)
성능복잡한 장면에서 많은 요소가 있으면 성능 저하 가능대량의 픽셀 조작 및 애니메이션에 고성능
주요 용도로고, 아이콘, 다이어그램, 일러스트, 인터랙티브 차트게임, 이미지 편집, 복잡한 데이터 시각화
접근성요소 자체가 의미를 가지므로 접근성 높음캔버스 자체는 이미지이므로 접근성 고려 필요
파일 크기단순한 그래픽은 작고, 복잡한 그래픽은 커질 수 있음해상도와 내용에 따라 다름
언제 무엇을 사용할까?
  • SVG
    • 로고, 아이콘, 지도를 포함한 확장성이 필요한 그래픽.
    • 데이터 변화에 따라 요소별로 색상이나 크기를 바꿔야 하는 인터랙티브 차트/다이어그램.
    • 간단한 애니메이션이나 상태 변화를 CSS/JS로 제어할 필요가 있는 경우.
    • 검색 엔진이나 스크린 리더가 그래픽 내용을 이해해야 하는 경우.
  • Canvas
    • 픽셀 단위의 정교한 그래픽 제어가 필요한 경우 (이미지 필터, 사진 편집기).
    • 높은 프레임 속도를 요구하는 게임이나 복잡한 애니메이션.
    • 수백, 수천 개의 요소가 동적으로 생성/소멸되어야 하는 대규모 데이터 시각화.
    • 그려진 결과물만 중요하고, 각 객체에 대한 개별적인 DOM 조작이 불필요한 경우.

아래 다이어그램은 SVG와 Canvas를 선택할 때 먼저 확인할 기준을 한 화면에 정리한 것입니다.

SVG와 Canvas 선택 체크

선명한 도형은 SVG, 빠른 픽셀 갱신은 Canvas가 어울린다은 SVG, Canvas, 접근성를 실제 브라우저 흐름에서 나눠 확인해야 구현 기준과 실패 대응이 흐려지지 않습니다.

  1. SVG

    아이콘, 차트, 반응형 벡터처럼 요소 단위 제어가 필요할 때 씁니다.

  2. Canvas

    게임, 파티클, 실시간 그리기처럼 빠른 재렌더링에 적합합니다.

  3. 접근성

    SVG는 의미를 담기 쉽고 Canvas는 별도 설명이 필요합니다.

  4. 성능

    요소 수, 애니메이션 빈도, 해상도에 따라 선택합니다.


실습: 간단한 Canvas 애니메이션 만들기

두 기술의 기본적인 차이를 직접 경험해 봅시다.

새 파일 준비
  • web-dev-practice 폴더 안에 graphics_playground.html 파일을 새로 생성합니다.
HTML 구조와 SVG, Canvas 코드 작성
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SVG와 Canvas 기초 실습</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 20px;
            background-color: #f8f8f8;
            color: #333;
            text-align: center;
        }
        .container {
            max-width: 900px;
            margin: 20px auto;
            background-color: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            display: flex;
            flex-wrap: wrap;
            justify-content: space-around;
            gap: 30px;
        }
        section {
            flex: 1 1 45%; /* 유연하게 공간 차지, 최소 너비 45% */
            min-width: 350px;
            padding: 20px;
            border: 1px solid #eee;
            border-radius: 8px;
            background-color: #fafafa;
        }
        h1 {
            color: #007bff;
            margin-bottom: 40px;
        }
        h2 {
            color: #0056b3;
            margin-top: 0;
        }
        svg {
            display: block;
            margin: 20px auto;
            border: 1px solid #ccc;
            background-color: #fff;
            border-radius: 5px;
        }
        .svg-icon-example {
            display: inline-block; /* 아이콘들이 한 줄에 있도록 */
            margin: 10px;
            padding: 10px;
            border: 1px solid #eee;
            border-radius: 5px;
            transition: transform 0.2s ease-out;
        }
        .svg-icon-example:hover {
            transform: scale(1.1);
            box-shadow: 0 0 8px rgba(0,0,0,0.1);
        }
        .svg-icon-example svg {
            border: none; /* 개별 아이콘은 보더 제거 */
            margin: 0;
        }
        canvas {
            border: 1px solid #ccc;
            background-color: #fff;
            border-radius: 5px;
            display: block;
            margin: 20px auto;
        }
        button {
            padding: 10px 20px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 1em;
            margin-top: 15px;
            transition: background-color 0.3s ease;
        }
        button:hover {
            background-color: #218838;
        }
    </style>
</head>
<body>
    <h1>SVG와 Canvas 기초 실습</h1>

    <div class="container">
        <section>
            <h2>SVG 아이콘 예시 (벡터 그래픽)</h2>
            <p>SVG는 확대해도 깨지지 않는 선명한 벡터 이미지를 제공합니다.</p>

            <div class="svg-icon-example" title="홈 아이콘">
                <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                    <polyline points="9 22 9 12 15 12 15 22"></polyline>
                </svg>
            </div>

            <div class="svg-icon-example" title="사용자 아이콘">
                <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
                    <circle cx="12" cy="7" r="4"></circle>
                </svg>
            </div>

            <div class="svg-icon-example" title="메일 아이콘">
                <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
                    <polyline points="22,6 12,13 2,6"></polyline>
                </svg>
            </div>

            <h3>간단한 SVG 도형 그리기</h3>
            <svg width="200" height="150">
                <rect x="20" y="20" width="60" height="60" fill="#ff7f50" stroke="#cd5b45" stroke-width="3">
                    <title>주황색 사각형</title>
                </rect>
                <circle cx="150" cy="75" r="50" fill="#6a5acd" stroke="#483d8b" stroke-width="3">
                    <title>보라색 원</title>
                </circle>
                <text x="100" y="140" text-anchor="middle" font-size="18" fill="#333">SVG 도형들</text>
            </svg>
        </section>

        <section>
            <h2>Canvas 애니메이션 예시 (비트맵 그래픽)</h2>
            <p>Canvas는 자바스크립트로 픽셀 단위의 동적인 그림을 그립니다.</p>

            <canvas id="animatedCanvas" width="350" height="200"></canvas>
            <button id="startButton">애니메이션 시작/정지</button>

            <script>
                const canvas = document.getElementById('animatedCanvas');
                const ctx = canvas.getContext('2d');
                const startButton = document.getElementById('startButton');

                let animationFrameId; // requestAnimationFrame ID
                let x = 50;
                let dx = 2; // x 방향 속도
                let radius = 20;

                function drawCircle() {
                    ctx.clearRect(0, 0, canvas.width, canvas.height); // 캔버스 지우기
                    ctx.beginPath();
                    ctx.arc(x, canvas.height / 2, radius, 0, Math.PI * 2);
                    ctx.fillStyle = 'blue';
                    ctx.fill();
                    ctx.closePath();
                }

                function animate() {
                    drawCircle();
                    x += dx;

                    // 벽에 부딪히면 방향 반전
                    if (x + radius > canvas.width || x - radius < 0) {
                        dx = -dx;
                    }
                    animationFrameId = requestAnimationFrame(animate); // 다음 프레임 요청
                }

                let isAnimating = false;
                startButton.addEventListener('click', () => {
                    if (isAnimating) {
                        cancelAnimationFrame(animationFrameId); // 애니메이션 정지
                        startButton.textContent = '애니메이션 시작';
                    } else {
                        animate(); // 애니메이션 시작
                        startButton.textContent = '애니메이션 정지';
                    }
                    isAnimating = !isAnimating;
                });

                // 페이지 로드 시 초기 상태 그리기
                drawCircle();
            </script>
        </section>
    </div>
</body>
</html>
결과 확인
  • Live Server를 통해 graphics_playground.html 파일을 열어보세요.
  • SVG 섹션에서는 다양한 아이콘과 도형이 선명하게 보일 것입니다. 브라우저에서 확대/축소해도 깨지지 않는지 확인해 보세요.
  • Canvas 섹션에서는 애니메이션 시작 버튼을 눌러 원이 좌우로 움직이는 간단한 애니메이션을 확인해 보세요.

SVG는 벡터 기반이라 확장성과 편집 용이성에 강점이 있고, Canvas는 픽셀 기반이라 복잡한 동적 그래픽과 고성능 렌더링에 적합합니다.

SVG와 Canvas 선택 판단

SVG는 의미 있는 벡터 요소를 문서처럼 다루고, Canvas는 픽셀을 빠르게 다시 그리는 작업에 어울린다.

상황추천이유확인할 점
아이콘·로고SVG확대해도 선명하고 CSS 제어 가능불필요하게 복잡한 path인지
소형 차트SVG축과 데이터 요소에 의미 부여 가능데이터 포인트 수가 적절한지
게임 화면Canvas매 프레임 전체 redraw에 유리상태와 충돌 판정을 직접 관리
이미지 처리Canvas픽셀 단위 조작 가능성능과 메모리 사용량 측정
혼합 UISVG + Canvas정적 UI와 고빈도 렌더 분리두 레이어의 좌표 동기화