๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Lect & Tip/React by GPT

D3.js, Chart.js ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•œ ๋ฆฌ์•กํŠธ ์ฐจํŠธ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ

by st๊ณต๊ฐ„ 2025. 6. 9.
๋ฐ˜์‘ํ˜•

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 ํด๋ง ๋“ฑ์˜ ๊ธฐ์ˆ ์„ ๋ณ‘ํ–‰ํ•˜๋ฉด, ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ์‹  ์ •๋ณด์™€ ์‹œ๊ฐ์  ์ธ์‚ฌ์ดํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํšจ๊ณผ์ ์ธ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ์†”๋ฃจ์…˜์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ๋ฆฌ์•กํŠธ ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๊ฐ€ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•œ๋‹ค๋ฉด, ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ ์†Œ๊ฐœํ•œ ๋‹ค์–‘ํ•œ ์ฐจํŠธ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ ๊ธฐ๋ฒ•๊ณผ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ ์ „๋žต์„ ์ ๊ทน ๋„์ž…ํ•ด ๋ณด์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ณต์žกํ•œ ํ†ต๊ณ„ ์ •๋ณด๋ฅผ ์ง๊ด€์ ์ด๊ณ  ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜์—ฌ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ ์˜์‚ฌ๊ฒฐ์ •์— ๊ธ์ •์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€