안녕하세요! 캔버스 튜토리얼을 아주 순조롭게 따라오고 계시네요. 색상과 스타일을 다루는 법을 익혔으니, 이번에는 캔버스 위에 '텍스트(Text) 그리기'를 배워볼 차례입니다.
만들고 계신 독후감 사이트에서 연간 독서량 통계를 차트로 그리거나, 웹 프로필 사이트에서 역량 게이지를 캔버스로 표현할 때 그 위에 숫자나 글자를 직접 새겨 넣어야 하는 경우가 생길 텐데요. 그럴 때 이 텍스트 그리기 API가 아주 핵심적인 역할을 하게 됩니다.
강사의 실무 팁과 함께 상세히 번역해 드릴 테니 잘 따라와 주세요!
이전 장에서 스타일과 색상을 적용하는 방법을 살펴본 데 이어, 이번에는 캔버스에 텍스트를 그리는 방법에 대해 알아보겠습니다.
캔버스 렌더링 컨텍스트는 텍스트를 렌더링하기 위해 다음 두 가지 메서드를 제공합니다.
fillText(text, x, y [, maxWidth])
주어진 텍스트를 지정된 (x, y) 좌표에 색을 채워서(fill) 그립니다. 선택적으로 최대 너비(maxWidth)를 지정할 수 있습니다.
strokeText(text, x, y [, maxWidth])
주어진 텍스트를 지정된 (x, y) 좌표에 테두리 선만(stroke) 그립니다. 선택적으로 최대 너비(maxWidth)를 지정할 수 있습니다.
💡 강사의 실무 팁:
선택 사항인maxWidth를 지정하면, 글자가 주어진 너비보다 길어질 경우 브라우저가 알아서 폰트 장평을 줄이거나 더 작게 압축해서 해당 너비 안에 욱여넣어 줍니다. 차트나 버튼 안의 텍스트가 컨테이너 밖으로 삐져나가는 것을 막고 싶을 때 아주 유용하게 쓰이는 방어 코드랍니다!
fillText 예제이 메서드는 텍스트를 현재 설정된 fillStyle 속성을 사용하여 칠합니다.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.font = "48px serif";
ctx.fillText("Hello world", 10, 50);
}
<canvas id="canvas" width="300" height="100"></canvas>
strokeText 예제이 메서드는 텍스트의 외곽선을 현재 설정된 strokeStyle 속성을 사용하여 그립니다. (마치 글씨 내부는 비어있고 테두리만 있는 형태가 됩니다.)
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.font = "48px serif";
ctx.strokeText("Hello world", 10, 50);
}
<canvas id="canvas" width="300" height="100"></canvas>
위의 예제들에서 우리는 이미 텍스트의 크기를 기본값보다 조금 더 크게 만들기 위해 font 속성을 사용해 보았습니다. 캔버스 위에 텍스트가 어떻게 그려질지를 세밀하게 조절할 수 있도록 해주는 추가적인 속성들이 몇 가지 더 있습니다.
font = value
텍스트를 그릴 때 사용될 폰트 스타일을 지정합니다. 이 문자열은 CSS의 font 속성과 완벽하게 동일한 문법을 사용합니다. 기본 폰트는 10px sans-serif입니다.
textAlign = value
텍스트의 정렬 방식을 설정합니다. 사용 가능한 값: start, end, left, right, center. 기본값은 start입니다.
textBaseline = value
텍스트의 베이스라인(기준선) 정렬 방식을 설정합니다. 사용 가능한 값: top, hanging, middle, alphabetic, ideographic, bottom. 기본값은 alphabetic입니다.
direction = value
텍스트가 쓰이는 방향을 설정합니다. 사용 가능한 값: ltr(좌에서 우로), rtl(우에서 좌로), inherit(부모 요소 상속). 기본값은 inherit입니다.
이전에 CSS를 다뤄보신 적이 있다면 이 속성들이 꽤 친숙하게 느껴지실 겁니다.
HTML 명세서(HTML spec)에서 가져온 다음 다이어그램은 textBaseline 속성으로 지원되는 다양한 기준선(baselines)들을 보여줍니다.
textBaseline 예제이 예제는 다양한 textBaseline 속성 값들이 텍스트를 어떻게 위치시키는지 시각적으로 보여줍니다.
더 자세한 정보와 예제는 CanvasRenderingContext2D.textBaseline 페이지에서 확인하실 수 있습니다.
<canvas id="canvas" width="400" height="100"></canvas>
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.font = "48px serif";
// 위쪽 기준선
ctx.textBaseline = "hanging";
ctx.strokeText("hanging", 10, 50);
// 중앙 기준선
ctx.textBaseline = "middle";
ctx.strokeText("middle", 250, 50);
// 글씨가 어떻게 정렬되는지 확인하기 위한 기준선 그리기
ctx.beginPath();
ctx.moveTo(10, 50);
ctx.lineTo(300, 50);
ctx.stroke();
}
💡 강사의 핵심 팁:
화면에 버튼이나 차트 툴팁을 캔버스로 직접 그릴 때, 사각형(도형)의 정확한 한가운데에 텍스트를 배치하고 싶다면 어떻게 해야 할까요?
ctx.textAlign = 'center';와ctx.textBaseline = 'middle';을 함께 설정한 뒤, 사각형 중심점의 x, y 좌표를fillText()에 넘겨주면 글자가 완벽하게 수평/수직 중앙에 딱 맞춰서 렌더링 됩니다. 실무에서 아주 유용하게 쓰이는 공식입니다!
그려질 텍스트에 대한 더 상세한 치수 정보(너비 등)를 알아야 할 때가 있습니다. 이럴 때 다음 메서드를 사용하여 텍스트를 측정할 수 있습니다.
measureText()
지정된 텍스트가 현재의 텍스트 스타일로 그려질 때 차지하게 될 너비를 픽셀 단위로 담고 있는 TextMetrics 객체를 반환합니다.
다음 코드 조각은 텍스트를 측정하여 그 너비 값을 가져오는 방법을 보여줍니다.
function draw() {
const ctx = document.getElementById("canvas").getContext("2d");
const text = ctx.measureText("foo"); // TextMetrics 객체를 반환
text.width; // 16 (설정된 폰트에 따라 픽셀 너비가 계산됨)
}
💡 강사의 실무 팁:
이measureText()는 동적인 UI를 만들 때 필수입니다. 예를 들어 사용자의 이름이나 동적으로 변하는 데이터를 말풍선 안에 그려야 한다고 가정해 볼까요? 글자가 얼마나 길어질지 미리 알 수 없기 때문에, 먼저measureText()로 글자의width를 계산한 다음, 그 너비에 패딩(여백)을 조금 더해서 말풍선 사각형의 크기를 동적으로 그리는 로직에 사용합니다.
<canvas> 요소는 본질적으로 그저 픽셀들의 묶음인 '비트맵(bitmap)'일 뿐이며, 그 안에 그려진 객체들에 대한 어떠한 의미론적(semantic) 정보도 제공하지 않습니다. 즉, 캔버스 위에 쓰인 텍스트는 시력 문제로 인해 화면 확대 프로그램(screen magnification)에 의존하는 사용자들에게 심각한 가독성 문제를 일으킬 수 있습니다.
캔버스 내의 픽셀들은 벡터(vector) 방식이 아니라 단순한 픽셀 조각의 모음이므로 화면 비율에 맞춰 깔끔하게 확장(scale)되지 못합니다. 따라서 화면을 확대하면 글자가 심하게 뭉개지거나 흐릿해집니다(blurry).
무엇보다 가장 큰 문제는 시맨틱 HTML과 달리 캔버스의 콘텐츠는 스크린 리더(Screen Readers)와 같은 접근성 도구(Accessibility tools)에 전혀 노출되지 않는다는 점입니다.
일반적으로, 웹 접근성이 중요한 웹사이트나 애플리케이션에서는 텍스트를 표현할 때 캔버스 사용을 피해야 합니다. 그 대신 캔버스 위에 투명한 일반 HTML 요소(예: <span>이나 <p>)를 절대 위치(absolute position)로 띄워서 배치하거나, SVG(Scalable Vector Graphics)를 사용하는 것이 훨씬 훌륭한 대안입니다.
이 페이지가 도움이 되었나요? (Was this page helpful to you?)
[ 예 (Yes) ][ 아니오 (No) ]
링크
수고하셨습니다! 프론트엔드 개발자로서 캔버스에 텍스트를 입히는 방법뿐만 아니라 접근성(a11y) 측면의 한계점까지 완벽하게 파악하셨네요. 동적인 측정이나 중앙 정렬 등 예제에 없는 팁들도 실제 컴포넌트 개발 시에 꼭 활용해 보세요!
혹시 다음 파트나 React 컴포넌트에 이 캔버스를 어떻게 올릴지 궁금한 점이 있으시다면 편하게 질문 남겨주세요.