# ๋ทฐ์—์„œ D3 ์‚ฌ์šฉํ•˜๊ธฐ

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

# ์ผ๋ฐฉ์ ์ธ D3 ์‚ฌ์šฉ

์•ž์˜ ์ฑ•ํ„ฐ์—์„œ ์ง„ํ–‰ํ•œ ์˜ˆ์ œ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€์„œ ๋ทฐ์—์„œ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‹คํ–‰ํ•ด๋ณด๊ธฐ (opens new window)



ย 












ย 




ย 


























<!-- VueLineChart.vue -->
<template>
  <svg ref="line"></svg>
</template>

<script>
import * as d3 from "d3";
export default {
  data() {
    return {
      data: [90, 72, 75, 25, 10, 92],
    };
  },
  mounted() {
    const svg = d3
      .select(this.$refs.line)
      .attr("width", 500)
      .attr("height", 300);

    svg
      .append("path")
      .datum(this.data)
      .attr("fill", "none")
      .attr("stroke", "#76BF8A")
      .attr("stroke-width", 3)
      .attr("d", this.line);
  },
  computed: {
    line() {
      return d3
        .line()
        .x((d, i) => this.xScale(i))
        .y((d) => this.ySclae(d));
    },
    xScale() {
      return d3
        .scaleLinear()
        .range([20, 480])
        .domain(d3.extent(this.data, (d, i) => i));
    },
    ySclae() {
      return d3.scaleLinear().range([280, 20]).domain([0, 100]);
    },
  },
};
</script>

์œ„ ์ฝ”๋“œ๋Š” select, append, datum ์™€ ๊ฐ™์€ D3์˜ Selections API๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋”์— ์ ‘๊ทผํ•˜๊ณ  ์ˆ˜์ •ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋”์— ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ ํ•˜์—ฌ ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๋ทฐ์˜ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ, ๋ Œ๋”๋ง(rendering) ์˜์—ญ๊ณผ ๊ฒน์ณ์„œ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

# ์กฐํ™”๋กœ์šด D3 ์‚ฌ์šฉ

์•ž์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ ๋ Œ๋”๋ง ์˜์—ญ์€ ๋ทฐ ํ…œํ”Œ๋ฆฟ(template)์—์„œ ๋‹ด๋‹นํ•˜๊ณ  ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•œ ์ˆ˜ํ•™์  ์—ฐ์‚ฐ์—๋งŒ D3 ์œ ํ‹ธ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ด ์˜ˆ์ œ์—์„œ๋Š” mounted ํ›…์— ์ž‘์„ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ path ์š”์†Œ์— ๊ฒฐํ•ฉ์‹œํ‚ค๋Š” ์ฝ”๋“œ๋ฅผ ๋ทฐ ํ…œํ”Œ๋ฆฟ์—์„œ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด computed ์†์„ฑ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    // computed
    .
    .
    pathData() {
      return this.line(this.data);
    },

์ด์ œ mounted ํ›…์„ ์ง€์šฐ๊ณ  ๋ทฐ ํ…œํ”Œ๋ฆฟ์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ ๋ Œ๋”๋ง ์š”์†Œ๋ฅผ ๋งˆํฌ์—…์œผ๋กœ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

<template>
  <svg :width="width" :height="height">
    <path fill="none" stroke="#76BF8A" stroke-width="3" :d="pathData"></path>
  </svg>
</template>
.
.
  • ์ „์ฒด ์ฝ”๋“œ

์‹คํ–‰ํ•ด๋ณด๊ธฐ (opens new window)

<template>
  <svg :width="width" :height="height">
    <path fill="none" stroke="#76BF8A" stroke-width="3" :d="path"></path>
  </svg>
</template>

<script>
import * as d3 from "d3";
export default {
  data() {
    return {
      data: [90, 72, 75, 25, 10, 92],
      width: 500,
      height: 300,
      padding: 20,
    };
  },
  computed: {
    path() {
      return this.line(this.data);
    },
    line() {
      return d3
        .line()
        .x((d, i) => this.xScale(i))
        .y((d) => this.ySclae(d));
    },
    xScale() {
      return d3
        .scaleLinear()
        .range([this.padding, this.width - this.padding])
        .domain(d3.extent(this.data, (d, i) => i));
    },
    ySclae() {
      return d3.scaleLinear().range([this.height - this.padding, this.padding]).domain([0, 100]);
    },
  },
};
</script>

๋ณ€๊ฒฝ๋œ ์ฝ”๋“œ๋Š” ๋ทฐ์˜ ๊ฐ€์ƒ ๋”์˜ ํŠน์„ฑ์„ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•˜๊ณ  ๊ณ„์‚ฐ๊ณผ ๋ Œ๋”๋ง(rendering) ์˜์—ญ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.