본문 바로가기

React

[React] 조직도 라이브러리: React Google Charts(Org Chart)

React를 사용해 조직도를 구현하고자 했다. 

조직도에 들어갈 내용은 프로필 이미지와 텍스트. 다른 라이브러리(react-d3-tree)를 사용하고자 했는데 텍스트만 적용되고 이미지가 안들어가졌다. 

그래서 Google Charts로 진행하여 원하는 대로 UI를 구현할 수 있었다.

 

Org Chart | React Google Charts

Example of org chart in react-google-charts.

www.react-google-charts.com

 

 

1. 설치

npm i react-google-charts
npm i --legacy-peer-deps react-google-charts //의존성 충돌나서 이거로 설치함

 

2. import 

import { Chart } from 'react-google-charts';

 

3. 사용(아래는 기본 템플릿)

import React from "react";
import { Chart } from "react-google-charts";

export const data = [
  [
    {
      v: "Mike", //각 node의 value값(id값처럼)
      f: 'Mike<div style="color:red; font-style:italic">President</div>',//보여지는 부분
    },
    "", //상위부모(부모에 따라가서 선이 이어짐)
    "The President", //분류? 쓰이진 않았는데 나중에 분류할때 쓰일수도있어서 같은 부서명끼리 묶어놓긴함
  ],
  [
    {
      v: "Jim",
      f: 'Jim<div style="color:red; font-style:italic">Vice President</div>',
    },
    "Mike",
    "VP",
  ],
  ["Alice", "Mike", ""],
  ["Bob", "Jim", "Bob Sponge"],
  ["Carol", "Bob", ""],
];

export const options = {
  allowHtml: true,
};

export function App() {
  return (
    <Chart
      chartType="OrgChart"
      data={data}
      options={options}
      width="100%"
      height="400px"
    />
  );
}

 

4. 내 코드 (커스텀 적용) 

위와 같이 그대로 반영시에는 data안에 태그가 그대로 나열되어 서버에서 API통신할 때, 어렵다고 판단되었다.

그래서 data(orgData)는 배열로 만들어 데이터 파일로 만들었다. 그리고 조직도 안에 각 node 박스는 스타일이 반복되고 안에 각 사람에 대한 데이터만 바뀌기 때문에 map을 통해 반복시켰다.

또한 반복되는 태그를 계속 적기 보다는 컴포넌트로 만들어 Google Charts의 f 값에 적용시켰다.

아래는 사용한 전체 코드다.

▼ OrgCharTree.jsx

import ReactDOMServer from 'react-dom/server';

import { Chart } from 'react-google-charts';
import './orgChartTree.scss';
import { orgData } from './orgData';
import { OrgChartInterface } from '@interfaces/components/OrgChartInterface';

//같은 box로 반복되어 컴포넌트로 만들어줌
export function OrgNodeContent({
  name,
  department,
  team,
  position,
  rank,
  img,
}: OrgChartInterface.OrgNodeContentInterface) {
  return (
    <div
      className={
        !department && !team
          ? `orgNodeWrap departmentWrap ${rank === '대표' ? 'ceoBox' : ''}`
          : `orgNodeWrap${
              position === '팀장' ? ' borderBox': ''
            }`
      }
    > //클래스명에 따라 box색깔 다르게 변경하느라 설정
      {img && (
        <div
          className='orgImgBox'
          style={{
            background: `no-repeat center/cover url('${img}')`,
          }}
        ></div>
      )}

      <div className='orgContentBox'>
        <div className='orgName'>{name}</div>
        {department ||
          (team && (
            <div>
              {department && <div>{department}</div>}
              {team && <div>{team}</div>}
            </div>
          ))}

        {rank && <div>직급: {rank}</div>}
        {position && <div>직책: {position}</div>}
      </div>
    </div>
  );
}

export default function OrgChartTree() {
  const chartOptions = {
    allowHtml: true, //내가 쓴 html로 적용
    allowCollapse: false,
  };

  const chartData = orgData.map((item) => [
    {
      v: item.v, //value값(다 달라야함)
      f: `${ReactDOMServer.renderToString(
        <OrgNodeContent
          name={item.name}
          department={item.department}
          team={item.team}
          position={item.position}
          rank={item.rank}
          img={item.img}
        />
      )}`, //f: 보여지는 html(reactDOMServer.renderToString은 컴포넌트를 문자열형태로 변환한것)
    },
    item.managerId, //상위부모
    item.rank, //쓰이진않지만 추후 분류를 위한 부서명
  ]);

  return (
    <Chart
      chartType='OrgChart'
      data={chartData}
      options={chartOptions}
      graph_id='OrgChart'
      /*  chartEvents={chartEvents} */
    />
  );
}

 

▼ orgData.js

이런 객체가 반복된 데이터 배열이다. 처음 객체는 UI로 안보이기에 그냥 세팅한거고 ceo부터 나타남

v(구글 차트 node value)
name(내가 설정한 UI중 name)
department(내가 설정한 UI중부서)
team(내가 분류하느라 설정한 팀명)
position(내가 설정한 UI중 직책)
img(내가 설정한 UI중 프로필 사진)
managerId(구글 차트에 적용되는 상위부모 node 설정)
rank(구글차트에 있었는데 실사용은 안됐음, 추후 부서별 분리될 수 있을때를 대비 설정해놓긴함)

 

export const orgData = [
  {
    v: 'office',
    name: 'office',
    department: '',
    team: '',
    position: '',
    img: '',
    managerId: '',
    rank: 'office',
  },
  {
    v: 'ceo',
    name: '대표이름',
    department: '',
    team: '',
    position: '대표이사',
    img: '이미지주소~',
    managerId: '',
    rank: 'VP',
  },
  /*아래 빈값들만 준 이유: 조직도 위에 바로 사람으로 이어지지 않고 title박스 배치하느라*/
  {
    v: 'dev',
    name: '개발본부',
    department: '',
    team: '',
    position: '',
    img: '',
    managerId: 'ceo',
    rank: 'dev',
  },
  {
    v: 'devDirector',
    name: '홍길동',
    department: '개발본부',
    team: '',
    position: '이사',
    img: '이미지주소~',
    managerId: 'dev',
    rank: 'dev',
  },
 ...