D3.js, Chart.js ๋ฐ์ดํฐ ์๊ฐํ๋ฅผ ์ํ ๋ฆฌ์กํธ ์ฐจํธ ์ปดํฌ๋ํธ ๊ฐ๋ฐ
ํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ฐ์ดํฐ๋ฅผ ๋จ์ํ ํ ์คํธ๋ ํ ํํ๋ก ๋ณด์ฌ์ฃผ๋ ๊ฒ์ ๋์ด์, ์๊ฐ์ ์์๋ฅผ ํตํด ๋ณด๋ค ์ง๊ด์ ์ผ๋ก ์ ๋ณด๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด ์ค์ํด์ก์ต๋๋ค. ์ด๋ฅผ ์ํด D3.js, Chart.js ๋ฑ ๊ฐ๋ ฅํ ๋ฐ์ดํฐ ์๊ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฆฌ์กํธ๋ฅผ ๊ฒฐํฉํ์ฌ, ์ค์๊ฐ ๋ฐ์ดํฐ ๋ชจ๋ํฐ๋ง ๋ฐ ํต๊ณ ์ ๋ณด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํํํ ์ ์๋ ์ฐจํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ฐํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํน์ง, ๋ฆฌ์กํธ์์ ๊ฒฐํฉ ๋ฐฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ฝ๋ ์์ ๋ฅผ ํตํด ๋ฐ์ดํฐ ์๊ฐํ ์ฐจํธ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ์ ๋ต์ ์์ธํ ์ค๋ช ํฉ๋๋ค.
๋ฐ์ดํฐ ์๊ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ์
๋ฐ์ดํฐ ์๊ฐํ๋ฅผ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ฐ๊ธฐ ์ฅ๋จ์ ๊ณผ ํ์ฉ ๋ฐฉ์์ด ๋ค๋ฆ ๋๋ค. ๋ํ์ ์ผ๋ก D3.js์ Chart.js๊ฐ ์์ผ๋ฉฐ, ์ด ๋์ ๋ฆฌ์กํธ์ ํจ๊ป ์ฌ์ฉํ์ฌ ์ฐจํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ฐํ ์ ์์ต๋๋ค.
- D3.js: ๋ฐ์ดํฐ ๊ธฐ๋ฐ์ DOM ์กฐ์์ ๊ฐ๋ฅํ๊ฒ ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ๋ณต์กํ ์๊ฐํ์ ์ปค์คํ ์ธํฐ๋์ ์ ๊ตฌํํ๋ ๋ฐ ๊ฐ์ ์ ์ง๋๋๋ค. D3.js๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ, ์ค์ผ์ผ, ์ถ ์์ฑ ๋ฑ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ฉฐ, SVG, Canvas, HTML ๋ฑ ๋ค์ํ ๋ ๋๋ง ๋ฐฉ์์ ์ง์ํฉ๋๋ค. ๋ค๋ง, ์ฌ์ฉ๋ฒ์ด ๋ค์ ๋ณต์กํ ์ ์์ด ๊ฐ๋ฐ์๊ฐ ์ธ๋ฐํ ์ ์ด๋ฅผ ์ํ ๋ ์ ํฉํฉ๋๋ค.
- Chart.js: ์ง๊ด์ ์ด๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์ด ์ฐจํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ๋ค์ํ ๊ธฐ๋ณธ ์ฐจํธ(๋ง๋, ์ , ๋๋, ํ์ด ๋ฑ)๋ฅผ ๋น ๋ฅด๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. Chart.js๋ ์ ๋๋ฉ์ด์ ํจ๊ณผ์ ๋ฐ์ํ ๋์์ธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํ๋ฉฐ, ์ค์ ๋ง์ผ๋ก๋ ๊น๋ํ ์๊ฐํ๋ฅผ ๊ตฌํํ ์ ์์ด ์ด๋ณด์๋ถํฐ ์๋ จ๋ ๊ฐ๋ฐ์๊น์ง ๋๋ฆฌ ์ฌ์ฉ๋ฉ๋๋ค.
์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ฆฌ์กํธ์์ ๊ฒฐํฉ์ ํตํด ์ปดํฌ๋ํธ ๋จ์๋ก ๊ด๋ฆฌ๋๊ณ , ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ์ ๋ฐ๋ผ ์ฐจํธ๊ฐ ๋์ ์ผ๋ก ๋ฐ์ํ ์ ์๋๋ก ๊ตฌํํ ์ ์์ต๋๋ค.
๋ฆฌ์กํธ์ D3.js์ ๊ฒฐํฉ
D3.js๋ฅผ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ ํจ๊ป ์ฌ์ฉํ๋ฉด, ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ๊ณผ SVG ๊ธฐ๋ฐ์ ์ฐจํธ ๋ ๋๋ง์ ์์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. ๋ฆฌ์กํธ๋ UI ์ํ ๊ด๋ฆฌ์ ์ง์คํ๊ณ , D3.js๋ ์ค์ DOM ์์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฉํ๋ ์ญํ ์ ๋ถ๋ฆฌํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด ๋, ๋ฆฌ์กํธ์ useEffect ํ ์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ํ์ D3.js ์ฝ๋๊ฐ ์คํ๋๋๋ก ํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ ๋๋ค.
D3.js๋ฅผ ํ์ฉํ ์ ํ ์ฐจํธ ์์
์๋ ์์ ๋ ๊ฐ๋จํ ์ ํ ์ฐจํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
// LineChart.jsx
import React, { useRef, useEffect } from 'react';
import * as d3 from 'd3';
const LineChart = ({ data, width = 600, height = 300 }) => {
const svgRef = useRef();
useEffect(() => {
// SVG ์์ ์ ํ ๋ฐ ์ด๊ธฐํ
const svg = d3.select(svgRef.current);
svg.selectAll('*').remove(); // ์ด์ ์ ๋ ๋๋ง๋ ๋ด์ฉ์ ๋ชจ๋ ์ ๊ฑฐ
// ์ฐจํธ ์ฌ๋ฐฑ ๋ฐ ๋ด๋ถ ์์ญ ์ค์
const margin = { top: 20, right: 30, bottom: 30, left: 40 };
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
// x์ถ ์ค์ผ์ผ ์์ฑ (๋ฐ์ดํฐ ์ธ๋ฑ์ค ๊ธฐ๋ฐ)
const xScale = d3.scaleLinear()
.domain([0, data.length - 1])
.range([0, innerWidth]);
// y์ถ ์ค์ผ์ผ ์์ฑ (๋ฐ์ดํฐ ๊ฐ ๊ธฐ๋ฐ)
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.nice()
.range([innerHeight, 0]);
// ์ ์์ฑ ํจ์ ์ ์
const lineGenerator = d3.line()
.x((d, i) => xScale(i))
.y(d => yScale(d))
.curve(d3.curveMonotoneX);
// SVG ๊ทธ๋ฃน ์์ ์์ฑ ๋ฐ ๋ณํ ์ ์ฉ
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// x์ถ ๊ทธ๋ฆฌ๊ธฐ
g.append('g')
.attr('transform', `translate(0,${innerHeight})`)
.call(d3.axisBottom(xScale).ticks(data.length));
// y์ถ ๊ทธ๋ฆฌ๊ธฐ
g.append('g')
.call(d3.axisLeft(yScale));
// ์ ๊ทธ๋ฆฌ๊ธฐ
g.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', '#1976d2')
.attr('stroke-width', 2)
.attr('d', lineGenerator);
}, [data, height, width]);
return (
<svg ref={svgRef} width={width} height={height}></svg>
);
};
export default LineChart;
์ด ์์ ์์๋ useRef๋ก SVG ์์๋ฅผ ์ฐธ์กฐํ ํ, useEffect๋ฅผ ํตํด ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ๋ ๋๋ง๋ค D3.js๋ฅผ ์ด์ฉํ์ฌ ์ ํ ์ฐจํธ๋ฅผ ๊ทธ๋ฆฝ๋๋ค. ๋ฐ์ดํฐ์ ๋ณํ์ ๋ฐ๋ผ ์ค์ผ์ผ๊ณผ ์ ์ ๋ค์ ๊ณ์ฐํ๊ณ , SVG ๋ด๋ถ์ x์ถ๊ณผ y์ถ, ๊ทธ๋ฆฌ๊ณ ์ ์ ๋ ๋๋งํฉ๋๋ค.
๋ฆฌ์กํธ์ Chart.js์ ๊ฒฐํฉ
Chart.js๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ก ์ฝ๊ฒ ๋ํํ ์ ์์ผ๋ฉฐ, ๊ธฐ๋ณธ์ ์ผ๋ก ๋ค์ํ ์ฐจํธ๋ฅผ ์ง์ํฉ๋๋ค. ๋ฆฌ์กํธ์ Chart.js๋ฅผ ๊ฒฐํฉํ๋ ๊ฐ์ฅ ํํ ๋ฐฉ๋ฒ์ react-chartjs-2์ ๊ฐ์ ๋ํผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋ฆฌ์กํธ์ ์ํ ๋ณํ์ ๋ฐ๋ผ ์ฐจํธ๋ฅผ ๋์ ์ผ๋ก ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค.
Chart.js๋ฅผ ํ์ฉํ ๋ง๋ ์ฐจํธ ์์
์๋ ์์ ๋ react-chartjs-2๋ฅผ ์ฌ์ฉํ์ฌ ๋ง๋ ์ฐจํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค.
// BarChart.jsx
import React from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
const BarChart = ({ data, labels }) => {
const chartData = {
labels: labels,
datasets: [
{
label: '๋งค์ถ',
data: data,
backgroundColor: 'rgba(25, 118, 210, 0.6)',
borderColor: 'rgba(25, 118, 210, 1)',
borderWidth: 1,
},
],
};
const options = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
tooltip: {
enabled: true,
},
},
};
return (
<div style={{ maxWidth: '700px', margin: '0 auto' }}>
<Bar data={chartData} options={options} />
</div>
);
};
export default BarChart;
์ ์์ ์์๋ react-chartjs-2์ Bar ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ๊ฐ๋จํ ๋ง๋ ์ฐจํธ๋ฅผ ์์ฑํฉ๋๋ค. props๋ก ์ ๋ฌ๋ฐ์ ๋ฐ์ดํฐ์ ๋ผ๋ฒจ์ ๊ธฐ๋ฐ์ผ๋ก ์ฐจํธ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์ฑํ๋ฉฐ, Chart.js์ ์ต์ ์ ํตํด ๋ฐ์ํ ์ฐจํธ, ๋ฒ๋ก ์์น, ํดํ ๋ฑ์ ์ค์ ํฉ๋๋ค. ์ด ๋ฐฉ์์ ๋ณต์กํ ์ค์ ์์ด ๋น ๋ฅด๊ฒ ์ฐจํธ๋ฅผ ๊ตฌํํ ์ ์๋ ์ฅ์ ์ด ์์ต๋๋ค.
์ค์๊ฐ ๋ฐ์ดํฐ ๋ชจ๋ํฐ๋ง๊ณผ ์ ๋ฐ์ดํธ ์ ๋ต
์ค์๊ฐ ๋ฐ์ดํฐ ์๊ฐํ๋ ๋ฐ์ดํฐ๊ฐ ์ง์์ ์ผ๋ก ๋ณํํ๋ ํ๊ฒฝ์์ ๋งค์ฐ ์ ์ฉํฉ๋๋ค. ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์์๋ ์ํ ๊ด๋ฆฌ์ ๋ผ์ดํ์ฌ์ดํด ํ ์ ํ์ฉํ์ฌ, ์๋ฒ์์ ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ณ ์ด๋ฅผ ์ฐจํธ์ ๋ฐ์ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, WebSocket์ด๋ API ํด๋ง์ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฐ์์ค๊ณ , ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ฐจํธ ์ปดํฌ๋ํธ์ props๋ก ์ ๋ฌํ์ฌ ๋ฆฌ๋ ๋๋ง์ ์ ๋ํ ์ ์์ต๋๋ค.
์๋๋ ๊ฐ๋จํ ํด๋ง ์์ ์ ๋๋ค.
// RealTimeChart.jsx
import React, { useState, useEffect } from 'react';
import LineChart from './LineChart';
const RealTimeChart = () => {
const [data, setData] = useState([10, 20, 15, 30, 25]);
useEffect(() => {
// 5์ด๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธํ๋ ํด๋ง ๋ก์ง
const interval = setInterval(() => {
// ์ค์ ํ๊ฒฝ์์๋ API ํธ์ถ ๋ฑ์ ํตํด ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ ์ ์์ต๋๋ค.
setData(prevData => {
const newValue = Math.floor(Math.random() * 40) + 10;
return [...prevData.slice(1), newValue];
});
}, 5000);
return () => clearInterval(interval);
}, []);
return (
<div>
<h2>์ค์๊ฐ ์ ํ ์ฐจํธ</h2>
<LineChart data={data} width={700} height={350} />
</div>
);
};
export default RealTimeChart;
์ด ์์ ์์๋ 5์ด๋ง๋ค ๋ฐ์ดํฐ ๋ฐฐ์ด์ ์ฒซ ๋ฒ์งธ ๊ฐ์ ์ ๊ฑฐํ๊ณ ์๋ก์ด ์์์ ๊ฐ์ ์ถ๊ฐํ์ฌ, ์ ํ ์ฐจํธ๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ค์๊ฐ ๋ฐ์ดํฐ ๋ชจ๋ํฐ๋ง์ด ๊ฐ๋ฅํ๋ฉฐ, ์ฌ์ฉ์์๊ฒ ์ง์์ ์ผ๋ก ์ต์ ์ ๋ณด๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
D3.js์ Chart.js๋ ๊ฐ๊ฐ ๊ฐ๋ ฅํ ๋ฐ์ดํฐ ์๊ฐํ ๋๊ตฌ๋ก, ๋ฆฌ์กํธ์ ๊ฒฐํฉํ์ฌ ๋ค์ํ ํํ์ ์ฐจํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ๋ฐํ ์ ์์ต๋๋ค. D3.js๋ ๋ณต์กํ ์ปค์คํ ์๊ฐํ์ ์ ๋ฐํ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ์ ํฉํ๋ฉฐ, Chart.js๋ ์ง๊ด์ ์ธ ์ค์ ๊ณผ ๊ธฐ๋ณธ ์ฐจํธ ๊ตฌ์ฑ์ด ์ฉ์ดํ์ฌ ๋น ๋ฅธ ํ๋กํ ํ์ ์ ์์ ์ ๋ฆฌํฉ๋๋ค. ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชจ๋ ๋ฆฌ์กํธ์ ์ํ ๊ด๋ฆฌ์ ๋ผ์ดํ์ฌ์ดํด ํ ์ ํ์ฉํ๋ฉด, ์ค์๊ฐ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ์ ์ฌ์ฉ์ ์ธํฐ๋์ ์ ๋ฐ๋ผ ๋์ ์ผ๋ก ๋ณํ๋ ์ฐจํธ๋ฅผ ์์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
ํ๋ก์ ํธ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์ ์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํํ๊ณ , ์ฐจํธ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ, ์ ๋ฐ์ดํธ ์ฃผ๊ธฐ, ๋ฐ์ํ ๋์์ธ ๋ฑ์ ๊ณ ๋ คํ์ฌ ์ปดํฌ๋ํธ๋ฅผ ์ค๊ณํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๋ํ, ์ค์๊ฐ ๋ฐ์ดํฐ ๋ชจ๋ํฐ๋ง์ ์ํด WebSocket, API ํด๋ง ๋ฑ์ ๊ธฐ์ ์ ๋ณํํ๋ฉด, ์ฌ์ฉ์์๊ฒ ์ต์ ์ ๋ณด์ ์๊ฐ์ ์ธ์ฌ์ดํธ๋ฅผ ์ ๊ณตํ๋ ํจ๊ณผ์ ์ธ ๋ฐ์ดํฐ ์๊ฐํ ์๋ฃจ์ ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
์์ผ๋ก ๋ฆฌ์กํธ ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐ์ดํฐ ์๊ฐํ๊ฐ ์ค์ํ ์ญํ ์ ํ๋ค๋ฉด, ์ด๋ฒ ํฌ์คํ ์์ ์๊ฐํ ๋ค์ํ ์ฐจํธ ์ปดํฌ๋ํธ ๊ฐ๋ฐ ๊ธฐ๋ฒ๊ณผ ์ค์๊ฐ ์ ๋ฐ์ดํธ ์ ๋ต์ ์ ๊ทน ๋์ ํด ๋ณด์๊ธธ ๋ฐ๋๋๋ค. ์ด๋ฅผ ํตํด ๋ณต์กํ ํต๊ณ ์ ๋ณด๋ฅผ ์ง๊ด์ ์ด๊ณ ์ธํฐ๋ํฐ๋ธํ๊ฒ ํํํ์ฌ, ์ฌ์ฉ์ ๊ฒฝํ๊ณผ ์์ฌ๊ฒฐ์ ์ ๊ธ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น ์ ์์ ๊ฒ์ ๋๋ค.
๋๊ธ