Vanilla JS로 막대 그래프 만들기
데이터 시각화는 정보를 효과적으로 전달하는 강력한 도구입니다. 이번 포스팅에서는 웹 개발에서 가장 기본적인 요소 중 하나인 **막대 그래프(Bar Chart)**를 외부 라이브러리 없이 **순수 자바스크립트(Vanilla JS)**와 HTML5의 **<canvas>
API**만을 사용하여 직접 구현하는 방법을 상세히 알아보겠습니다. 데이터 준비부터 좌표 계산, 막대 그리기, 축 및 라벨 추가까지 모든 과정을 직접 코딩하며 <canvas>
의 활용 능력을 높여보세요.
🧩 사용 기술
- HTML5
<canvas>
: 웹 페이지에 픽셀 단위의 그래픽을 그릴 수 있는 요소입니다. JavaScript와 함께 사용하여 동적인 데이터 시각화를 구현합니다. - 자바스크립트 기본 문법: 배열, 객체, 반복문(
forEach
) 등을 사용하여 데이터를 처리하고 캔버스에 그리는 로직을 구성합니다. - 캔버스 2D 렌더링 컨텍스트:
fillRect()
,fillText()
,beginPath()
,moveTo()
,lineTo()
등 막대 그래프를 그리는 데 필요한 다양한 2D 그리기 메서드를 활용합니다. - 코드 하이라이트 (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="barChartCodeExample" width="600" height="400">브라우저가 캔버스를 지원하지 않습니다.</canvas>
<script>
const canvas = document.getElementById("barChartCodeExample");
const ctx = canvas.getContext("2d");
const data = [
{ label: "1월", value: 50, color: "#4CAF50" },
{ label: "2월", value: 70, color: "#2196F3" },
{ label: "3월", value: 40, color: "#FFC107" },
{ label: "4월", value: 90, color: "#9C27B0" },
{ label: "5월", value: 60, color: "#F44336" }
];
const chartWidth = canvas.width - 100; // 좌우 여백 제외
const chartHeight = canvas.height - 80; // 상하 여백 제외
const startX = 60; // X축 시작 위치
const startY = canvas.height - 40; // Y축 시작 위치 (캔버스 하단에서 여백 위)
const maxValue = Math.max(...data.map(item => item.value));
const barWidth = chartWidth / data.length - 10; // 막대 간 간격 포함
// 축 그리기
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(startX, startY - chartHeight); // Y축
ctx.lineTo(startX + chartWidth, startY); // X축
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
// 데이터 그리기
data.forEach((item, index) => {
const barHeight = (item.value / maxValue) * chartHeight;
const barX = startX + index * (chartWidth / data.length) + 5; // 막대 시작 X 위치
const barY = startY - barHeight; // 막대 시작 Y 위치 (바닥부터 위로)
// 막대 그리기
ctx.fillStyle = item.color;
ctx.fillRect(barX, barY, barWidth, barHeight);
// 라벨 (X축 아래)
ctx.fillStyle = "#333";
ctx.font = "12px sans-serif";
ctx.textAlign = "center";
ctx.fillText(item.label, barX + barWidth / 2, startY + 20);
// 값 (막대 위)
ctx.fillText(item.value, barX + barWidth / 2, barY - 10);
});
// Y축 값 라벨 (간단하게 최대값만 표시)
ctx.textAlign = "right";
ctx.textBaseline = "middle";
ctx.fillText(maxValue, startX - 10, startY - chartHeight);
</script>
</body>
</html>
🖼️ 결과 미리보기
위 코드의 원리를 적용하면 아래와 같이 간단한 막대 그래프가 웹 페이지에 출력됩니다. 코드가 제대로 동작하는지 직접 확인해 보세요.
📌 코드 설명
HTML5 <canvas>
와 순수 JavaScript로 막대 그래프를 그리는 과정은 크게 HTML 구조 준비와 JavaScript 로직 구현으로 나눌 수 있습니다. 각 부분의 주요 구성 요소와 동작 원리를 상세히 설명합니다.
1. HTML 구조
<canvas id="barChartCodeExample" width="600" height="400">
: 웹 페이지에 막대 그래프가 그려질 영역을 정의하는 캔버스 요소입니다.id
속성을 통해 JavaScript에서 이 요소를 참조할 수 있으며,width
와height
로 캔버스의 크기를 지정합니다. 이 캔버스는 코드 예시의 일부로, 실제 차트 렌더링은 '결과 미리보기' 섹션의 캔버스에서 이루어집니다.
2. JavaScript 로직
(1) 데이터 준비 및 캔버스 설정
data
배열: 차트에 표시할 각 막대의 정보를 담고 있는 객체 배열입니다. 각 객체는label
(항목 이름),value
(값),color
(막대 색상)를 포함합니다.- 캔버스 및 컨텍스트 가져오기:
document.getElementById()
로 캔버스 요소를 가져오고,getContext("2d")
로 2D 렌더링 컨텍스트를 얻습니다. - 차트 영역 및 축 계산: 캔버스 전체 크기에서 여백을 제외한 실제 차트가 그려질
chartWidth
,chartHeight
를 정의합니다.startX
와startY
는 X축과 Y축이 만나는 원점의 좌표를 설정합니다. maxValue
계산: 데이터 값 중 가장 큰 값을 찾아 막대 높이를 스케일링하는 기준점으로 사용합니다.Math.max()
와map()
함수를 활용합니다.barWidth
및barSpacing
계산: 전체 차트 너비와 데이터 개수를 고려하여 각 막대의 너비와 막대 사이의 간격을 계산합니다.
(2) 축 그리기
ctx.beginPath()
,ctx.moveTo()
,ctx.lineTo()
: 새로운 경로를 시작하고, X축과 Y축의 선을 그립니다.lineTo()
는 현재 위치에서 지정된 좌표까지 선을 그립니다.ctx.strokeStyle
&ctx.lineWidth
: 축의 색상과 두께를 설정합니다.ctx.stroke()
: 정의된 경로(여기서는 X, Y 축 선)를 그립니다.
(3) 막대 그리기
- 데이터 반복 (
forEach
):data
배열의 각 항목에 대해 막대를 그리는 작업을 반복합니다. barHeight
계산: 각 막대의 높이는 해당 항목의value
를maxValue
로 나눈 비율에chartHeight
를 곱하여 계산합니다.barX
,barY
계산: 각 막대의 시작 X, Y 좌표를 계산합니다. Y 좌표는 캔버스 하단(startY
)을 기준으로 막대 높이만큼 위로 올라가는 방식으로 설정합니다.ctx.fillStyle
&ctx.fillRect()
:fillStyle
로 막대의 색상을 설정하고,fillRect(x, y, width, height)
메서드를 사용하여 직사각형 형태의 막대를 채워 그립니다.
(4) 라벨 및 값 추가
ctx.fillText()
: 막대 아래에 X축 라벨(항목 이름)을, 막대 위에 해당 값(숫자)을 그립니다.ctx.font
,ctx.textAlign
,ctx.textBaseline
: 텍스트의 폰트, 수평 정렬, 수직 정렬을 설정하여 라벨과 값이 보기 좋게 표시되도록 합니다.- Y축 값 라벨:
maxValue
와 '0'을 Y축 옆에 표시하여 차트의 스케일을 직관적으로 보여줍니다.
3. 사용된 주요 Canvas 메서드 및 속성
ctx.getContext("2d")
: 캔버스에 2D 그래픽을 그리기 위한 렌더링 컨텍스트를 반환합니다.ctx.fillRect(x, y, width, height)
: 지정된 좌표와 크기로 채워진 직사각형을 그립니다. 막대를 그릴 때 사용됩니다.ctx.beginPath()
: 새로운 경로를 시작합니다. 독립적인 도형을 그릴 때마다 호출합니다.ctx.moveTo(x, y)
: 현재 드로잉 위치를 (x, y)로 이동시킵니다.ctx.lineTo(x, y)
: 현재 드로잉 위치에서 (x, y)까지 직선을 그립니다. 축을 그릴 때 사용됩니다.ctx.stroke()
: 현재 경로를 따라 선을 그립니다.ctx.fillText(text, x, y)
: 지정된 좌표에 텍스트를 그립니다. 라벨과 값을 표시할 때 사용됩니다.ctx.fillStyle
/ctx.strokeStyle
: 도형을 채우거나 선을 그릴 때 사용할 색상을 설정합니다.ctx.font
: 텍스트의 폰트 스타일, 크기, 서체를 설정합니다.ctx.textAlign
/ctx.textBaseline
: 텍스트의 수평 및 수직 정렬 방식을 설정합니다.
4. 좌표 계산의 중요성
- 캔버스 좌표계 이해: 캔버스의 (0,0)은 좌측 상단입니다. Y축은 아래로 갈수록 값이 증가합니다. 따라서 막대 그래프를 바닥부터 위로 그리려면 Y 좌표 계산에 유의해야 합니다.
- 스케일링: 데이터의 값 범위가 캔버스 높이에 맞춰 잘 표시될 수 있도록
maxValue
를 기준으로 높이를 스케일링하는 것이 중요합니다. - 여백 및 간격: 차트가 캔버스 가장자리에 붙지 않고, 막대들이 적절한 간격을 유지하도록 여백(
startX
,startY
)과 막대 너비/간격(barWidth
,barSpacing
)을 잘 계산해야 합니다.
🚀 응용 팁
위 예제를 기반으로 막대 그래프를 더욱 다채롭고 기능적으로 만들 수 있는 몇 가지 응용 팁입니다:
- 마우스 호버(hover) 시 강조 효과: 마우스가 특정 막대 위에 올라갔을 때 해당 막대의 색상을 변경하거나 그림자 효과를 추가하여 시각적인 피드백을 제공할 수 있습니다.
isPointInPath()
또는 직접 좌표 계산을 통해 구현 가능합니다. - 애니메이션 효과 추가: 차트가 로드될 때 막대가 바닥에서부터 서서히 올라오거나, 값이 변경될 때 부드럽게 전환되는 애니메이션을
requestAnimationFrame
을 사용하여 구현할 수 있습니다. - 동적 데이터 업데이트: 외부 API나 사용자 입력으로부터 데이터를 받아와 그래프를 실시간으로 업데이트하는 기능을 추가할 수 있습니다.
- 다양한 막대 그래프 형태: 스택형 막대 그래프, 그룹형 막대 그래프 등 복잡한 형태의 그래프도 이 기본 원리를 응용하여 구현할 수 있습니다.
- 데이터 툴팁(Tooltip): 마우스를 막대에 올렸을 때 해당 막대의 자세한 정보(정확한 값, 퍼센트 등)를 보여주는 툴팁을 구현하여 사용자에게 더 많은 정보를 제공할 수 있습니다.
- 반응형 디자인: 윈도우 크기 변화에 따라 캔버스 크기와 그래프 요소들을 동적으로 조정하여 모바일 환경에서도 최적화된 뷰를 제공합니다.
🛠️ 주의사항 및 개선 포인트
순수 JavaScript와 Canvas API를 사용하여 막대 그래프를 구현할 때 고려해야 할 몇 가지 주의사항과 개선할 수 있는 부분들입니다.
- 축 라벨 스케일: 현재 예제는 Y축에 최대값과 0만 표시하지만, 실제 그래프에서는 눈금(tick)과 눈금 라벨(tick label)을 추가하여 Y축 스케일을 더 상세하게 표현하는 것이 좋습니다.
- 데이터 범위 동적 조정: 데이터 값이 매우 크거나 작아질 때, Y축의 최대값을 자동으로 조정하여 그래프가 항상 적절한 비율로 표시되도록 하는 로직이 필요할 수 있습니다.
- 성능 최적화: 데이터 포인트가 많아지거나 복잡한 애니메이션을 적용할 경우, 캔버스 렌더링 성능이 저하될 수 있습니다. 불필요한 재그리기를 피하고, 필요한 부분만 업데이트하는 기법을 고려해야 합니다.
- 접근성 (Accessibility): 캔버스에 그려진 시각적 정보는 시각 장애인에게 접근하기 어렵습니다. 그래프 데이터를 HTML 테이블 형태로 별도 제공하거나, ARIA 속성을 활용하여 웹 접근성을 높이는 것을 고려해야 합니다.
댓글 없음:
댓글 쓰기