[시각화] D3를 이용하여 간단한 노드관계 만들어보기

2023. 7. 10. 19:29·(3D)Dev Deep Dive/Frontend origin
728x90

프론트엔드를 함에 따라 기본적인 개발과정을 따를 수 있으면 한 번 씩은 바라보는 주제가 있습니다.

바로 시각화 입니다. 어떤 주제로 개발을 할 때, 개발에 사용했던 데이터를 이용하여 시각화를 하는 것은 꽤나 값진 경험이 될 것입니다.

 

그렇기에 오늘은 D3를 이용하여 간단한 노드를 연결하는 작업을 해 볼 것입니다.

우선, D3가 무엇인 지 알아보아야겠죠?

D3를 이전에 정리했던 글에서 살펴보실 수 있습니다.

 

 

[D3.js] 강력한 성능의 시각화 라이브러리, D3.js에 대해 알아보자

D3.js 라는 이름을 들어 보셨나요? "Data-Driven Documents"의 줄임말인 D3.js는 웹 표준을 이용해 데이터를 시각화하는 데 사용되는 강력한 자바스크립트 라이브러리입니다. 주로 프론트엔드 고도화의

time-map-installer.tistory.com


간단하게 D3만 써보기 위해 CRA(Create React App)을 이용하여 JS 리액트 프로젝트만 생성한 후

노드를 연결해보는 작업을 하겠습니다

 

리액트 프로젝트를 생성하고 기본적으로 형성되어있는 보일러플레이트 코드를 제거하는 과정은 생략하겠습니다.

 

1단계 : 필요한 라이브러리 설치

React 초기 세팅을 모두 마쳤으면 D3를 설치합니다.

npm install d3

 

2단계 : Mock Data 준비

노드, 그리고 노드 사이의 링크를 나타내기 위해 Mock data를 이용하여 구성 해보겠습니다.

뭔가 그럴싸해 보이게 하기 위해 10개 정도씩 준비해 보았습니다.

const nodes = [
    { id: "node1", name: "Node 1" },
    { id: "node2", name: "Node 2" },
    { id: "node3", name: "Node 3" },
    { id: "node4", name: "Node 4" },
    { id: "node5", name: "Node 5" },
    { id: "node6", name: "Node 6" },
    { id: "node7", name: "Node 7" },
    { id: "node8", name: "Node 8" },
    { id: "node9", name: "Node 9" },
    { id: "node10", name: "Node 10" },
  ];

  const links = [
    { source: "node1", target: "node2", name: "Link 1-2" },
    { source: "node1", target: "node3", name: "Link 1-3" },
    { source: "node1", target: "node4", name: "Link 1-4" },
    { source: "node2", target: "node3", name: "Link 2-3" },
    { source: "node2", target: "node4", name: "Link 2-4" },
    { source: "node2", target: "node5", name: "Link 2-5" },
    { source: "node3", target: "node4", name: "Link 3-4" },
    { source: "node3", target: "node5", name: "Link 3-5" },
    { source: "node3", target: "node6", name: "Link 3-6" },
    { source: "node4", target: "node5", name: "Link 4-5" },
    { source: "node4", target: "node6", name: "Link 4-6" },
    { source: "node4", target: "node7", name: "Link 4-7" },
    { source: "node5", target: "node6", name: "Link 5-6" },
  ];

 

3단계 : 그래프 구성요소 만들기

새로운 구성요소를 만들 것입니다. 구성요소가 마운트 될 때 useEffect 후크를 사용하여 그래프를 초기화합니다.

D3.js는 force-directed 그래프를 생성하는 데 사용할 수 있는 'forceSimulation'기능을 제공합니다.

여기에서는 useEffect Hooks에서 그래프를 생성하는 데 사용할 것입니다.
forceSimulation 기능은 force에 따라 노드의 위치를 자동으로 업데이트하는 시뮬레이션을 실행합니다.

이 시뮬레이션의 tick 이벤트를 수신하여 SVG에서 링크, 그리고 노드의 위치를 업데이트 할 수 있습니다.
여기서 노드의 x, y 속성과 링크의 x1, y1, x2, y2 속성을 노드의 현재 위치를 기준으로 설정합니다.

import React, { useRef, useEffect } from "react";
import * as d3 from "d3";

function Graph({ data }) {
	//...

  const svgRef = useRef();
  const width = 1600;
  const height = 1000;

  useEffect(() => {
    const simulation = d3
      .forceSimulation(nodes)
      .force(
        "link",
        d3
          .forceLink(links)
          .id((d) => d.id)
          .distance(100)
      )
      .force("charge", d3.forceManyBody())
      .force("center", d3.forceCenter(width / 2, height / 2));

    const svg = d3.select(svgRef.current);

    const link = svg
      .append("g")
      .attr("stroke", "#999")
      .attr("stroke-opacity", 0.6)
      .selectAll("line")
      .data(links)
      .join("line")
      .attr("stroke-width", (d) => Math.sqrt(d.value));

    const node = svg
      .append("g")
      .attr("stroke", "#fff")
      .attr("stroke-width", 1.5)
      .selectAll("circle")
      .data(nodes)
      .join("circle")
      .attr("r", 5)
      .attr("fill", "#69b3a2");

    const labels = svg
      .append("g")
      .selectAll("text")
      .data(nodes)
      .join("text")
      .attr("text-anchor", "middle")
      .attr("dominant-baseline", "central")
      .text((d) => d.name);

    function getConnectedNodes(node, depth = 0, nodeSet = new Set()) {
      if (depth > 2) return nodeSet;
      nodeSet.add(node.id);
      links.forEach((link) => {
        if (link.source === node.id && !nodeSet.has(link.target)) {
          getConnectedNodes(
            nodes.find((n) => n.id === link.target),
            depth + 1,
            nodeSet
          );
        }
        if (link.target === node.id && !nodeSet.has(link.source)) {
          getConnectedNodes(
            nodes.find((n) => n.id === link.source),
            depth + 1,
            nodeSet
          );
        }
      });
      return nodeSet;
    }

    node.on("click", (event, d) => {
      const connectedNodes = Array.from(getConnectedNodes(d));
      node.attr("fill", (n) =>
        connectedNodes.includes(n.id) ? "#69b3a2" : "#ddd"
      );
      link.style("stroke", (l) =>
        connectedNodes.includes(l.source.id) &&
        connectedNodes.includes(l.target.id)
          ? "#999"
          : "#ddd"
      );
    });

    simulation.on("tick", () => {
      link
        .attr("x1", (d) => d.source.x)
        .attr("y1", (d) => d.source.y)
        .attr("x2", (d) => d.target.x)
        .attr("y2", (d) => d.target.y);

      node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);

      labels.attr("x", (d) => d.x).attr("y", (d) => d.y);
    });
  }, []);

  return (
  	<svg ref={svgRef} width={width} height={height} />
      <g className="nodes" />
      <g className="links" />
    </svg>
  );
}

export default Graph;

SVG에서 링크와 노드를 그리면 아래와 같은 결과가 출력됩니다

 

 

이제, D3를 이용하여 여러분들의 상상의 나래를 펼쳐보아요!

728x90
저작자표시 비영리 변경금지 (새창열림)

'(3D)Dev Deep Dive > Frontend origin' 카테고리의 다른 글

프론트엔드에 적절한 폴더구조는 없다  (1) 2024.02.24
<TimeMap.dmg> WEBP 이미지를 통해 웹 렌더링 시간을 개선해보자  (1) 2023.12.18
[문득 떠오른 건데] 밥먹고 졸릴때 리팩토링 요소를 탐색하면 잠이 깰까? - 2편  (0) 2023.05.02
[문득 떠오른 건데] 밥먹고 졸릴때 동적 컴포넌트를 만들면 잠이 깰까? - 1편  (2) 2023.04.22
[HTML+CSS] 텍스트 슬라이드 기능(가로/세로)  (0) 2022.11.21
'(3D)Dev Deep Dive/Frontend origin' 카테고리의 다른 글
  • 프론트엔드에 적절한 폴더구조는 없다
  • <TimeMap.dmg> WEBP 이미지를 통해 웹 렌더링 시간을 개선해보자
  • [문득 떠오른 건데] 밥먹고 졸릴때 리팩토링 요소를 탐색하면 잠이 깰까? - 2편
  • [문득 떠오른 건데] 밥먹고 졸릴때 동적 컴포넌트를 만들면 잠이 깰까? - 1편
ThreeLight
ThreeLight
ThreeLight Studio의 블로그, TimeMap.exe에 오신 것을 환영합니다.
  • ThreeLight
    TimeMap.exe
    ThreeLight
  • 전체
    오늘
    어제
    • 분류 전체보기 (245)
      • Checkpoint (1)
      • (3D)Dev Deep Dive (0)
        • Templates & Guides (9)
        • Frontend origin (9)
        • Backend origin (1)
        • TroubleShootings (4)
      • Development Study (95)
        • Frontend (36)
        • Backend (21)
        • CS(Computer Science) (2)
        • Background Knowledges (11)
        • Algorithm (2)
        • Mobile (3)
        • AWS (6)
        • Python (6)
        • MSW(MapleStoryWorlds) (8)
      • Coding Test (59)
        • 문제.zip (1)
        • BaekJoon_JavaScript (0)
        • Programmers_JavaScript (9)
        • BaekJoon_Python (23)
        • Programmers_Python (10)
        • Undefined_Python (3)
        • Programmers_SQL (13)
      • 활동내역.zip (43)
        • 개인 (21)
        • Techeer (12)
        • Bootcamp (7)
        • Hackathon (1)
        • TeamProjects (2)
      • 여기 괜찮네??(사이트 | App) (5)
      • 재미있는 주제들 (8)
      • 개발 외 공부 저장소 (11)
        • 생산운영관리 (3)
        • 생활속의금융 (6)
        • 경영정보시스템 (2)
  • 링크

    • TimeMap.dmg (Portfolio)
    • GitHub 바로가기
    • 오픈프로필(카카오톡)
    • Medium 바로가기
    • Disquiet 바로가기
    • LinkedIn 바로가기
  • 인기 글

  • 태그

    Python
    CSS
    Baek Joon
    프로그래머스
    HTML
    TypeScript
    react
    programmers
    JavaScript
    SQL
  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.1
ThreeLight
[시각화] D3를 이용하여 간단한 노드관계 만들어보기
상단으로

티스토리툴바