Vanilla JS로 파이차트 만들기
안녕하세요! 웹 페이지에 데이터를 시각적으로 표현하는 것은 사용자 이해를 돕는 데 매우 중요합니다. 이번 포스팅에서는 외부 라이브러리(jQuery, Chart.js 등)의 도움 없이 순수 자바스크립트(Vanilla JS)와 HTML5의 강력한 <canvas>
API만 사용하여 동적인 파이차트를 직접 구현하는 방법을 자세히 알아보겠습니다. 데이터 비율 계산부터 원 그리기, 각 조각에 대한 라벨 출력까지 모든 과정을 직접 코딩하며 <canvas>
의 기본기를 다져보세요. 이 글은 <canvas>
를 활용한 그래픽 구현의 좋은 예제가 될 것입니다.
🧩 사용 기술
- HTML5
<canvas>
: 웹 페이지에 그림을 그릴 수 있는 그래픽 요소입니다. JavaScript와 함께 사용하여 동적인 그래픽을 렌더링하며, 픽셀 단위의 정교한 제어가 가능합니다. - 자바스크립트의
Math
함수: 파이차트 조각의 각도를 정확하게 계산하는 데 필요한 원주율(Math.PI
), 사인(Math.sin
), 코사인(Math.cos
) 함수 등을 활용합니다. - 코드 하이라이트 (Prism.js 또는 Highlight.js 권장): 본문에 포함된 HTML 및 JavaScript 코드 블록의 가독성을 높이기 위해 코드 강조 라이브러리를 활용합니다. 참고: 이 기능은 블로그 템플릿의
<head>
또는<body>
태그 내에 해당 라이브러리의 CSS 및 JavaScript 파일을 직접 추가해야만 작동합니다. 추가하지 않으면 코드에 색상이 입혀지지 않고 단순히 텍스트로 보일 수 있습니다.
💻 코드 예제
먼저, 파이차트를 그릴 기본적인 HTML 구조와 전체 JavaScript 코드를 살펴보겠습니다. 이 코드를 통해 캔버스에 데이터를 기반으로 한 차트가 어떻게 그려지는지 이해할 수 있습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vanilla JS 파이차트 예제</title>
</head>
<body>
<h1>간단한 파이차트</h1>
<!-- 이 캔버스는 코드 예제 블록 안에 있는 HTML 구조를 보여주기 위한 것입니다. -->
<canvas id="pieChartCodeExample" width="300" height="300">브라우저가 캔버스를 지원하지 않습니다.</canvas>
<script>
// 아래 JavaScript 코드는 코드 예제 블록의 일부로, 실제 블로그 게시물에서 실행되는 것이 아닙니다.
// 실제 실행되는 차트는 '결과 미리보기' 섹션을 참조하세요.
// 1. Canvas 요소와 2D 렌더링 컨텍스트 가져오기
// const canvasActual = document.getElementById("pieChartCodeExample");
// const ctxActual = canvasActual.getContext("2d");
// 2. 차트 데이터 정의: 각 항목의 라벨, 값, 색상
/*
const dataActual = [
{ label: "HTML", value: 30, color: "#f16529" },
{ label: "CSS", value: 20, color: "#2965f1" },
{ label: "JavaScript", value: 50, color: "#f7df1e" }
];
*/
// 3. 전체 데이터 값의 합계 계산 (총 비율을 구하기 위함)
// const totalActual = dataActual.reduce((sum, item) => sum + item.value, 0);
// 4. 파이차트 조각의 시작 각도 초기화 (0은 3시 방향)
// let startAngleActual = 0;
// 5. 각 데이터 항목별로 파이차트 조각 그리기
/*
dataActual.forEach(item => {
const sliceAngleActual = (item.value / totalActual) * 2 * Math.PI;
ctxActual.beginPath();
ctxActual.moveTo(150, 150);
ctxActual.arc(150, 150, 100, startAngleActual, startAngleActual + sliceAngleActual);
ctxActual.closePath();
ctxActual.fillStyle = item.color;
ctxActual.fill();
const textAngleActual = startAngleActual + sliceAngleActual / 2;
const xActual = 150 + Math.cos(textAngleActual) * 70;
const yActual = 150 + Math.sin(textAngleActual) * 70;
ctxActual.fillStyle = "#000";
ctxActual.font = "14px sans-serif";
ctxActual.textAlign = "center";
ctxActual.textBaseline = "middle";
ctxActual.fillText(item.label, xActual, yActual);
startAngleActual += sliceAngleActual;
});
*/
</script>
</body>
</html>
🖼️ 결과 미리보기
위 코드의 원리를 적용하면 아래와 같이 간단한 파이차트가 웹 페이지에 출력됩니다. 코드가 제대로 동작하는지 직접 확인해 보세요.
📌 코드 설명
HTML5 <canvas>
와 순수 JavaScript로 파이차트를 그리는 과정은 크게 HTML 구조 준비와 JavaScript 로직 구현으로 나눌 수 있습니다. 각 부분의 주요 구성 요소와 동작 원리를 상세히 설명합니다.
1. HTML 구조
<canvas id="pieChartCodeExample" width="300" height="300">
: 웹 페이지에 파이차트가 그려질 영역을 정의하는 캔버스 요소입니다.id
속성을 통해 JavaScript에서 이 요소를 쉽게 참조할 수 있으며,width
와height
로 캔버스의 크기를 픽셀 단위로 지정합니다. 캔버스 태그 사이에 있는 텍스트는 브라우저가 캔버스를 지원하지 않을 때 표시되는 대체 메시지입니다. 이 캔버스는 코드 예시의 일부로, 실제 차트 렌더링은 '결과 미리보기' 섹션의 캔버스에서 이루어집니다.
2. JavaScript 로직
(1) 데이터 준비
data
배열: 차트에 표시할 각 항목의 정보를 담고 있는 객체 배열입니다. 각 객체는label
(항목 이름),value
(값),color
(표시 색상)를 포함합니다.total
변수:data
배열에 있는 모든value
값의 합을 계산합니다. 이 총합은 각 항목의 비율을 계산하는 데 사용됩니다.Array.prototype.reduce()
메서드를 활용하여 간결하게 합계를 구할 수 있습니다.
(2) 파이차트 조각 그리기
sliceAngle
계산: 각 데이터 항목이 파이차트에서 차지할 각도를 라디안 단위로 계산합니다.(item.value / total) * 2 * Math.PI
공식을 사용하는데, 여기서2 * Math.PI
는 원 전체의 각도(360도)를 라디안으로 나타낸 것입니다.ctx.beginPath()
: 새로운 그림 경로를 시작합니다. 이전 경로의 영향을 받지 않고 완전히 새로운 도형을 그릴 때마다 호출하는 것이 좋습니다.ctx.moveTo(150, 150)
: 캔버스에서 펜을 (150, 150) 좌표(캔버스 중앙)로 이동시킵니다. 이 지점부터 파이 조각의 선이 시작됩니다.ctx.arc(150, 150, 100, startAngle, startAngle + sliceAngle)
: 파이차트의 핵심 부분인 원호(arc)를 그립니다. 중심 좌표 (150, 150), 반지름 100px로startAngle
부터startAngle + sliceAngle
까지의 호를 그립니다.ctx.closePath()
: 현재 경로를 시작점(moveTo
로 이동했던 중심점)으로 되돌려 경로를 닫습니다. 이를 통해 파이 조각이 부채꼴(삼각형) 형태로 완성됩니다.ctx.fillStyle
&ctx.fill()
:fillStyle
로 현재 파이 조각에 채울 색상을 설정하고,fill()
메서드를 사용하여 해당 경로 내부를 설정된 색상으로 채웁니다.
(3) 라벨 추가
textAngle
계산: 각 파이 조각의 중간 각도를 계산하여 라벨이 해당 조각의 중앙에 위치하도록 합니다.Math.cos
와Math.sin
활용: 삼각 함수를 이용하여 원주상에 라벨을 배치할 X, Y 좌표를 계산합니다. 여기서는 반지름 70px 지점(차트 내부, 중심에서 조금 떨어진 곳)에 라벨을 배치하도록 설정했습니다.ctx.fillText()
: 계산된 X, Y 좌표에 항목의label
텍스트를 그립니다.ctx.font
,ctx.fillStyle
,ctx.textAlign
,ctx.textBaseline
속성을 사용하여 텍스트의 스타일과 정렬을 조절할 수 있습니다.
3. 사용된 주요 Canvas 메서드 및 속성
canvas.getContext("2d")
:<canvas>
요소에 2D 그래픽을 그리기 위한 렌더링 컨텍스트를 반환합니다. 모든 그리기 작업은 이 컨텍스트 객체를 통해 이루어집니다.ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
: 지정된 중심 좌표, 반지름, 시작 및 끝 각도를 사용하여 원호(arc)를 그립니다.counterclockwise
는 선택 사항이며,true
이면 반시계 방향으로 그립니다.ctx.moveTo(x, y)
: 현재 펜의 위치를 (x, y)로 이동시킵니다. 이전에 그려진 선과 연결되지 않고 새로운 선을 시작할 때 유용합니다.ctx.closePath()
: 현재 하위 경로의 시작점과 끝점을 직선으로 연결하여 경로를 닫습니다.ctx.fill()
: 현재 경로 내부를fillStyle
에 설정된 색상으로 채웁니다.ctx.stroke()
: 현재 경로를strokeStyle
에 설정된 색상으로 선을 그립니다. (이 예제에서는 사용하지 않음)ctx.fillStyle
/ctx.strokeStyle
: 도형을 채우거나 선을 그릴 때 사용할 색상, 패턴 또는 그라데이션을 설정합니다.
4. 수학적 계산의 중요성
- 360도($2\pi$ 라디안)를 데이터 비율로 나누기: 캔버스 API에서 각도는 라디안 단위를 사용합니다. 원 전체는 $2\pi$ 라디안($360^\circ$)이므로, 각 데이터 항목의 값($\text{value}$)을 전체 값($\text{total}$)으로 나눈 비율에 $2\pi$를 곱하여 해당 항목의 라디안 각도($\text{sliceAngle}$)를 정확하게 계산합니다.
- 라벨 위치 계산 ($ \text{cos}, \text{sin}$): 원형 차트에서 라벨을 조각의 중간에 배치하려면 삼각 함수(코사인, 사인)가 필수적입니다. 라벨을 배치할 반지름과 각도를 이용하여 원주상의 X, Y 좌표를 계산합니다.
🚀 응용 팁
위 예제를 기반으로 파이차트를 더욱 다채롭고 기능적으로 만들 수 있는 몇 가지 응용 팁입니다:
- 마우스 호버(hover) 시 강조 효과 추가: 캔버스에
mousemove
이벤트 리스너를 추가하고, 마우스 좌표가 어떤 파이 조각 내부에 있는지 판별하는 로직을 구현하여 해당 조각의 색상을 변경하거나 약간 확대하여 시각적인 피드백을 제공할 수 있습니다. - 차트 중앙에 총합 또는 퍼센트 표시: 파이차트의 중앙에
(item.value / total) * 100
와 같은 계산을 통해 전체 값의 합계나 특정 항목의 퍼센티지를 텍스트로 표시하여 정보를 더 명확하게 전달할 수 있습니다.ctx.fillText()
를 활용합니다. - 외부 데이터(JSON) 불러오기:
fetch
API나XMLHttpRequest
를 사용하여 외부 API나 JSON 파일로부터 데이터를 비동기적으로 불러와 차트를 동적으로 렌더링할 수 있습니다. 이는 실제 웹 애플리케이션에서 다양한 데이터를 기반으로 차트를 구현할 때 매우 유용합니다. - 애니메이션 적용:
requestAnimationFrame
을 사용하여 차트가 로드될 때 각 조각이 부드럽게 나타나거나, 데이터가 변경될 때 자연스러운 전환 효과를 주어 사용자 경험을 향상시킬 수 있습니다. 예를 들어, 각 조각의 반지름이 점진적으로 커지게 할 수 있습니다. - 반응형 디자인 구현: 윈도우 크기 변화에 따라 캔버스의 크기를 JavaScript로 동적으로 조정하고, 차트 요소들의 위치와 크기도 비례적으로 조절하여 모바일 환경을 포함한 다양한 화면 크기에서 잘 보이도록 만들 수 있습니다.
canvas.width = window.innerWidth * 0.8;
와 같은 방식을 사용할 수 있습니다.
🛠️ 주의사항 및 개선 포인트
순수 JavaScript와 Canvas API를 사용하여 파이차트를 구현할 때 고려해야 할 몇 가지 주의사항과 개선할 수 있는 부분들입니다.
- 라벨 겹침 문제: 데이터 항목이 많거나 조각의 크기가 작을 경우, 라벨 텍스트가 서로 겹쳐 가독성이 떨어질 수 있습니다. 이를 해결하기 위해 라벨을 원 외부에 배치하고 리더 라인(leader line)을 추가하거나, 특정 크기 이하의 조각은 라벨을 생략하는 등의 로직이 필요할 수 있습니다.
- 반응형 크기 조정: 현재 캔버스는 고정된
width
와height
를 가집니다. 다양한 디바이스에서 최적화된 뷰를 제공하려면 캔버스 크기를CSS
의width: 100%;
와height: auto;
로 설정하고, JavaScript를 통해 캔버스의 내부 해상도(canvas.width
,canvas.height
)를 동적으로 조정하는 것이 좋습니다. - 성능 최적화: 매우 많은 데이터를 그리거나 복잡한 애니메이션을 적용할 경우, 캔버스 렌더링 성능에 영향을 줄 수 있습니다. 불필요한 그리기 작업을 줄이고,
requestAnimationFrame
을 효율적으로 사용하여 부드러운 애니메이션을 구현하는 것이 중요합니다. - 접근성 (Accessibility): 캔버스에 그려진 차트는 시각 장애인에게 정보를 전달하기 어렵습니다. 시각적인 정보 외에 차트의 데이터를 표(table) 형태로 제공하거나, ARIA 속성을 활용하여 접근성을 높이는 것을 고려해야 합니다.
댓글 없음:
댓글 쓰기