Canvas API/Canvas tutorial/Basic usage of canvas

김동현·2026년 3월 22일

안녕하세요! 캔버스(Canvas)의 세계로 본격적으로 들어오신 것을 환영합니다.

프론트엔드 개발을 하다 보면 직접 픽셀을 다뤄서 그래픽을 그려야 할 때가 반드시 오게 되죠. 여러분의 포트폴리오 프로젝트, 예를 들어 독후감 사이트에서 사용자의 독서 통계를 예쁜 커스텀 차트로 직접 그려서 보여줄 때 이 캔버스 기술이 정말 유용하게 쓰일 수 있습니다!

문서 내용 하나하나 빠짐없이, 그리고 실무에서 리액트나 넥스트제이에스(Next.js)와 함께 사용할 때의 꿀팁도 듬뿍 담아 번역해 드릴게요. 자, 시작해 볼까요?


캔버스의 기본 사용법 (Basic usage of canvas)

이전 (Previous) | 다음 (Next)

이 튜토리얼의 첫걸음으로 <canvas> HTML 요소 자체를 먼저 살펴보겠습니다. 이 페이지를 다 읽고 나면, 캔버스 2D 컨텍스트를 설정하는 방법을 알게 되고 브라우저에 첫 번째 예제 그림을 띄워보실 수 있을 거예요.


<canvas> 요소 (The <canvas> element)

<canvas id="canvas" width="150" height="150"></canvas>

처음 보면 <canvas>는 마치 <img> 요소와 비슷하게 생겼습니다. 유일하게 눈에 띄는 차이점이라면 srcalt 속성이 없다는 것뿐이죠. 실제로 <canvas> 요소는 오직 두 개의 속성, widthheight만을 가집니다. 이 두 속성은 모두 선택 사항이며, DOM 프로퍼티(properties)를 사용해서 자바스크립트로 설정할 수도 있습니다.

widthheight 속성을 따로 지정하지 않으면, 캔버스는 기본적으로 너비 300 픽셀, 높이 150 픽셀로 초기화됩니다. 이 요소는 CSS를 통해 여러분이 원하는 크기로 임의로 크기를 조정할 수 있습니다. 하지만 화면에 렌더링될 때는, 캔버스 내부에 그려진 원본 이미지가 CSS로 지정한 레이아웃 크기에 맞춰서 '확대/축소(scale)' 됩니다. 만약 CSS로 지정한 크기 비율이 캔버스의 초기 속성 비율과 다르다면, 그림이 찌그러져 보이게 됩니다.

참고:
여러분이 그린 그림이 찌그러져 보인다면, CSS로 크기를 제어하지 말고 <canvas> 태그의 widthheight 속성에 크기를 직접 명시해 보세요.

💡 강사의 핵심 팁:
이 부분은 프론트엔드 실무 면접이나 실제 개발에서 정말 자주 겪는 함정입니다! <canvas>의 HTML width/height 속성은 '실제 도화지의 픽셀 수(해상도)'를 의미하고, CSS의 width/height'화면에 보여질 돋보기의 크기'를 의미해요. 그래서 도화지는 150x150인데 CSS로 300x300을 주면, 작은 그림을 억지로 늘려놓은 것처럼 흐릿해지고 찌그러지는 거랍니다.

id 속성은 <canvas> 요소에만 있는 특별한 속성은 아니며, (예를 들어 class처럼) 어떤 HTML 요소에나 적용할 수 있는 전역 HTML 속성(global HTML attributes) 중 하나입니다. 하지만 스크립트에서 캔버스 요소를 훨씬 쉽게 찾을 수 있도록 항상 id를 부여하는 것이 좋은 습관입니다.

<canvas> 요소는 일반적인 이미지처럼 스타일을 입힐 수 있습니다 (margin, border, background 등). 하지만 이러한 CSS 스타일 규칙들이 캔버스 '내부'에 그려지는 실제 그림에 영향을 주지는 않습니다. 이 방법에 대해서는 이 튜토리얼의 전용 챕터에서 자세히 다룰 예정입니다. 캔버스에 아무런 스타일 규칙을 적용하지 않으면, 처음에는 완전히 투명한 상태로 보이게 됩니다.

접근성 있는 콘텐츠 (Accessible content)

<canvas> 요소 역시 <img>, <video>, <audio>, <picture> 요소들과 마찬가지로 접근성을 갖춰야 합니다. 미디어가 로드되지 않거나 사용자가 의도한 대로 미디어를 경험할 수 없을 때 표시될 '대체 텍스트(fallback text)'를 제공해야 하죠. 미디어 타입에 맞게 대체 콘텐츠, 자막, 그리고 대체 텍스트를 항상 제공하는 것이 좋습니다.

대체 콘텐츠를 제공하는 방법은 매우 간단합니다. 스크린 리더(screen readers), 검색 엔진 스파이더, 기타 자동화된 봇들이 접근할 수 있도록 <canvas> 요소의 여는 태그와 닫는 태그 사이에 대체할 콘텐츠를 텍스트나 다른 태그로 넣어주기만 하면 됩니다. 캔버스를 정상적으로 지원하는 브라우저는 이 컨테이너 안의 텍스트 내용을 기본적으로 무시하고 캔버스를 정상적으로 렌더링합니다. 반대로 브라우저가 <canvas>를 지원하지 않을 경우에만 그 안의 대체 콘텐츠가 화면에 나타납니다.

예를 들어, 캔버스 내용에 대한 텍스트 설명을 제공하거나, 동적으로 렌더링되는 콘텐츠 대신 보여줄 정적인 이미지 파일을 제공할 수 있습니다. 코드로 보면 아래와 같습니다:

<canvas id="stockGraph" width="150" height="150">
  현재 주가: $3.15 + 0.15
</canvas>

<canvas id="clock" width="150" height="150">
  <img src="images/clock.png" width="150" height="150" alt="시계 이미지" />
</canvas>

사용자에게 단순히 "캔버스를 지원하는 다른 브라우저를 사용하세요"라고 말하는 것은 캔버스를 읽을 수 없는 환경의 사용자에게 아무런 도움이 되지 않습니다. 유용한 대체 텍스트나 서브 DOM 요소들을 제공하면, 자칫 접근성이 떨어질 수 있는 요소에 훌륭한 접근성을 더해줄 수 있습니다.

필수적인 </canvas> 닫는 태그 (Required </canvas> tag)

이렇게 태그 사이에 대체 콘텐츠를 제공하는 방식 때문에, <img> 요소와는 다르게 <canvas> 요소는 반드시 닫는 태그(</canvas>)가 필요합니다. 만약 이 닫는 태그가 빠지면, 문서의 나머지 전체가 캔버스의 대체 콘텐츠로 간주되어 화면에 보이지 않게 되어버립니다.

만약 대체 콘텐츠가 굳이 필요하지 않은 순수하게 시각적이고 장식적인 목적의 캔버스라면, <canvas id="foo" role="presentation" ...></canvas>처럼 간단히 닫아주시면 됩니다. 이는 캔버스를 지원하는 모든 브라우저와 완벽하게 호환됩니다.


렌더링 컨텍스트 (The rendering context)

<canvas> 요소는 크기가 고정된 드로잉 표면을 만듭니다. 그리고 이 표면은 하나 이상의 렌더링 컨텍스트(rendering contexts)를 외부로 노출하는데, 우리는 이 컨텍스트를 사용해서 화면에 보여질 콘텐츠를 생성하고 조작하게 됩니다. 이 튜토리얼에서는 2D 렌더링 컨텍스트에 집중할 것입니다. 다른 컨텍스트들은 각기 다른 방식의 렌더링을 제공할 수 있습니다. 예를 들어, WebGLOpenGL ES를 기반으로 하는 3D 컨텍스트를 사용하죠.

캔버스는 처음에는 텅 빈 백지상태입니다. 무언가를 표시하려면 자바스크립트가 먼저 이 렌더링 컨텍스트에 접근해서 그 위에 그림을 그려야 합니다. <canvas> 요소에는 이를 위한 getContext()라는 메서드가 준비되어 있습니다. 이 메서드는 렌더링 컨텍스트와 그에 속한 다양한 그리기 함수들을 얻어오는 데 사용됩니다.
getContext()는 컨텍스트의 '타입'을 나타내는 매개변수 하나를 받습니다. 이 튜토리얼에서 다룰 2D 그래픽의 경우, "2d"를 인자로 넘겨주면 CanvasRenderingContext2D 객체를 얻을 수 있습니다.

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

스크립트의 첫 번째 줄은 document.getElementById() 메서드를 호출하여 DOM 트리에서 <canvas> 요소를 나타내는 노드를 찾아옵니다. 요소 노드를 성공적으로 가져오고 나면, 해당 요소의 getContext() 메서드를 사용해서 실제 드로잉 컨텍스트에 접근할 수 있게 됩니다.

💡 강사의 실무 팁:
리액트나 넥스트제이에스 환경에서 컴포넌트 주도 개발을 하실 때는 DOM에 직접 getElementById를 쓰기보다 useRef를 씁니다. 단, 주의할 점은 컴포넌트가 화면에 렌더링(마운트)되기 전에는 <canvas> 요소가 존재하지 않는다는 거예요!
따라서 캔버스에 무언가를 그리기 위해 getContext('2d')를 호출하는 작업은, 반드시 DOM이 모두 그려진 직후인 useEffect 훅(Hook) 안에서 실행해야 에러가 발생하지 않습니다.


브라우저 지원 여부 확인하기 (Checking for support)

대체 콘텐츠는 <canvas>를 아예 지원하지 않는 구형 브라우저에서만 화면에 표시됩니다. 하지만 자바스크립트 코드 내에서도 getContext() 메서드가 존재하는지 테스트함으로써 프로그래밍 방식으로 지원 여부를 확인할 수 있습니다. 위에서 작성했던 코드 조각에 이 검사 로직을 추가하면 아래와 같은 모양이 됩니다.

const canvas = document.getElementById("canvas");

if (canvas.getContext) {
  const ctx = canvas.getContext("2d");
  // 여기에 캔버스 그리기 코드를 작성합니다.
} else {
  // 캔버스를 지원하지 않는 브라우저를 위한 대체 코드를 여기에 작성합니다.
}

뼈대 템플릿 (A skeleton template)

자, 이제 앞으로 이어질 튜토리얼 예제들의 출발점이 되어줄 아주 심플한 뼈대(미니멀리즘) 템플릿을 만들어 보겠습니다.

참고:
HTML 문서 안에 <script>를 직접 끼워 넣는 것은 그리 좋은 관행은 아닙니다. 여기서는 예제 코드를 간결하게 보여드리기 위해 합쳐서 작성했습니다.

<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>Canvas tutorial</title>
    <style>
      canvas {
        border: 1px solid black;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas" width="150" height="150"></canvas>
    <script>
      function draw() {
        const canvas = document.getElementById("canvas");
        if (canvas.getContext) {
          const ctx = canvas.getContext("2d");
        }
      }
      draw();
    </script>
  </body>
</html>

이 스크립트는 draw()라는 함수를 포함하고 있으며, 페이지 로딩이 끝난 후 한 번 실행됩니다. 이를 위해 스크립트 태그를 HTML의 메인 <body> 콘텐츠가 끝나는 맨 아래쪽에 배치했습니다. 이 draw() 함수나 이와 유사한 초기화 함수들은, 페이지가 먼저 완벽하게 로드되었다는 전제하에 setTimeout(), setInterval()을 사용하거나 load 이벤트 핸들러에 연결하여 호출할 수도 있습니다.

이 코드를 실행해 보면 현재 시점에서는 캔버스 안에 아무것도 없는 하얀 빈 칸만 렌더링될 것입니다.


간단한 예제 (A simple example)

시작하는 의미로, 두 개의 직사각형이 겹쳐져 있고 그중 하나에 알파(alpha) 투명도가 적용된 예제를 한 번 살펴보겠습니다. 이 기능들이 정확히 어떻게 동작하는지는 다음 장의 예제들에서 더 깊이 탐구해 볼 테니 지금은 맛보기로 즐겨주세요.
앞서 만든 템플릿의 script 요소 안쪽 내용을 다음과 같이 수정해 보세요.

<canvas id="canvas" width="150" height="150"></canvas>
/* CSS 부분 */
canvas {
  border: 1px solid black;
}
// JavaScript 부분
function draw() {
  const canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    const ctx = canvas.getContext("2d");

    // 짙은 빨간색으로 첫 번째 직사각형을 그립니다.
    ctx.fillStyle = "rgb(200 0 0)";
    ctx.fillRect(10, 10, 50, 50);

    // 반투명한 파란색으로 두 번째 직사각형을 그립니다.
    ctx.fillStyle = "rgb(0 0 200 / 50%)";
    ctx.fillRect(30, 30, 50, 50);
  }
}
draw();

이 코드를 실행하면 아래와 같은 모습이 됩니다!

이전 (Previous) | 다음 (Next)


이렇게 캔버스의 기초인 도화지 셋팅과 첫 번째 붓칠을 마무리하셨습니다! 이제 이 위에 멋진 그래프나 도형들을 자유롭게 그려나갈 수 있어요.

profile
프론트에_가까운_풀스택_개발자

0개의 댓글