[Tia 기업협업] Echart 라이브러리를 사용하여 그래프 그리기

front-end developer·2022년 11월 4일
0

1. 백엔드로부터 받아온 데이터 Echarts API에 맞게 가공하기

Echart에 데이터를 입력하는 방식은 Echarts 태그의 option 속성값으로 주면 된다.

<ECharts
        option={options}
        style={{ width: 'auto', height: '500px' }}
        opts={{ renderer: 'svg' }}
      />

그리고 이 option의 형식은 다음과 같다.

const [options, setOptions] = useState({
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: [],
    },
    yAxis: {
      type: 'value',
    },
    series: [
      {
        data: [],
        type: 'line',
        areaStyle: {},
      },
    ],

x축과 y축 그리고 series가 있는데, series는 쉽게 말해서 그래프의 타입을 나타내며 실질적인 데이터를 넣는 부분이기도 하다.

여기서, x축 data에는 x축 항목의 값이 들어가고 series에는 그래프에 사용될 데이터를 입력하면 되는데, 현재 백엔드 API로부터 데이터를 받아오는 형식은 다음과 같다.

먼저, daily_user_count 배열을 bucket이라는 state에 저장을 해주고, 이 bucket을 가공해주는 것으로 로직을 구현했다.

const addDataInChart = () => {
    const xAxisData = [];
    const seriesData = [];
    buckets.forEach((data) => {
      xAxisData.push(data.date);
      seriesData.push(data.completed);
    });
    setOptions({
      xAxis: {
        data: xAxisData,
      },
      series: [
        {
          data: seriesData,
        },
      ],
    });
  };

위 함수는 bucket state에 데이터가 저장되고 나서 실행시킬 함수로, 각 데이터를 배열에 담고 최종적으로 setOption 을 통해 데이터를 그래프에 입력시킨다.

useEffect(() => {
    fetchData();
  }, [fetchData]);

  useEffect(() => {
    addDataInChart();
  }, [buckets]);

그리고 두 useEffect Hook 구문을 통해, 위에서 addDataInChart()를 실행시키고 그래프에 데이터를 그릴 수 있게된다. fetchData를 실행시켜 받아온 데이터를 buckets에 넣어준 후, 2번째 useEffect에 의해서 그래프에 데이터를 넣게 된다. (2번째 의존 배열안에 buckets 변수가 들어갔기 떄문에, buckets에 데이터가 들어오면 실행된다.)

2개 이상의 데이터를 Echart에 그리기

앞서 구현한 그래프는 날짜별 사전예약 신청수를 나타낸다. 그런데, 사실 사전예약은 여러 채널을 통해 홍보가 됐고, 유저가 어떤 채널로 유입이 되었는지 파악이 가능해야 의미가 있다.

API에 데이터를 요청할 때 쿼리파리미터로 채널 정보를 담아 보내면, 백엔드에서 채널별 신청수를 보내주는 로직이 완성되었고, 이를 그래프에 반영해야하는 상황이 있었다.

그래프에 여러 항목의 데이터를 나타내기 위해서는 Echart의 option이라는 속성 값의 series에 객체를 추가하면 되는데, 말로 이해하기 어려울 것 같으니 예제를 살펴보겠다.

// Graph.js
const Graph = () => {
	const option = {
	  xAxis: {
	    type: 'category',
	    boundaryGap: false,
	    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
	  },
	  yAxis: {
	    type: 'value'
	  },
	  series: [
	    {
	      name: 'Email',
	      type: 'line',
	      stack: 'Total',
	      data: [120, 132, 101, 134, 90, 230, 210]
	    },
	    {
	      name: 'Union Ads',
	      type: 'line',
	      stack: 'Total',
	      data: [220, 182, 191, 234, 290, 330, 310]
	    },
	    {
	      name: 'Video Ads',
	      type: 'line',
	      stack: 'Total',
	      data: [150, 232, 201, 154, 190, 330, 410]
	    },
	    {
	      name: 'Direct',
	      type: 'line',
	      stack: 'Total',
	      data: [320, 332, 301, 334, 390, 330, 320]
	    },
	    {
	      name: 'Search Engine',
	      type: 'line',
	      stack: 'Total',
	      data: [820, 932, 901, 934, 1290, 1330, 1320]
	    }
	  ],
	}
	}
	return (
    <>
      <ECharts
        option={options}
        style={{ width: 'auto', height: '500px' }}
        opts={{ renderer: 'svg' }}
        notMerge={true}
      />
    </>
  );
};

Echar의 option 속성 중 series라는 항목의 배열에 5개의 객체가 추가되어 있으며, 각 객체가 다른 항목을 나타냄을 확인할 수 있다. 위 코드를 렌더시키면 다음과 같은 결과의 그래프가 출력된다.

즉, series 항목이 1개의 객체라면 그래프는 이전과 같이 1개의 항목만 나타내고 객체를 여러개 추가하면 그래프에 여러개 항목의 데이터를 출력할 수 있게 된다.

본론으로 돌아와서, 이제 사전예약 신청수를 채널별로 나타내기 위해 series에 채널별 데이터를 추가해야 하는데 그 전에 짚고 넘어가야할 부분이 있다. 관리자가 페이지에 처음 접속했을 때 뜨는 화면에서는 전체 신청수만을 나타내는 그래프가 출력되고, 관리자가 검색창에서 여러개의 채널을 클릭했을 때 채널별 데이터가 그래프에 출력되어야 한다.

  • 날짜별 전체 사전예약 신청수

  • 채널별 사전예약 신청수

따라서, 백엔드로부터 받아온 데이터를 option에 추가하는 함수 addDataInChart는 채널이 한가지인 경우와 여러개인 경우 2가지의 분기처리가 필요하다. 채널이 한가지인 경우는 기존 로직으로 데이터를 추가하면 되고 여러개인 경우의 로직을 구현해야 하는데, 이에 앞서 채널별 데이터를 어떻게 받아오는 지 콘솔에 나타내보자.

데이터를 요청할 때 채널이라는 쿼리파리미터 값에 값을 넣지 않으면 빨간박스영역 daily_channel_user_count 값이 비어있고, 값을 추가하게 되면 위와 같이 채널별 데이터가 담겨져온다.

위의 데이터를 바로 option 객체에 넣어 사용하면 좋겠지만, 데이터 형태와 option의 series에 들어가야할 형태와는 차이가 있어 데이터를 가공해야하는 작업이 필요했다.

(Echart4 버전부터 생긴 dataset이라는 속성을 사용하면 위 데이터 형태로 구현이 가능할 것 같아 좀 찾아봤지만, 최근 생겨난 속성이라 그런지 예제나 설명이 부족해 적용하지 못했다.)

const addDataInChart = () => {
    const users = chartData.daily_user_count;
    const usersByChannel = chartData.daily_channel_user_count;
    const xAxisData = [];
    const seriesData = [];

    if (filters.channels.length) {
      users.forEach((data) => {
        xAxisData.push(data.date);
      });
      filters.channels.split(',').forEach((channel) => {
        const dataByChannel = [];
        usersByChannel[channel].forEach((data) => {
          dataByChannel.push(data.completed);
        });
        seriesData.push({
          name: channel,
          type: 'line',
          data: dataByChannel,
        });
      });
      setOptions({
        ...options,
        xAxis: { data: xAxisData },
        legend: { data: filters.channels.split(',') },
        series: seriesData,
      });
    } 
		// 채널값이 입력된 경우

		else {
      users.forEach((data) => {
        xAxisData.push(data.date);
        seriesData.push(data.completed);
      });
      setOptions({
        ...options,
        xAxis: { data: xAxisData },
        legend: { data: filters.channels.split(',') },
        series: [
          {
            data: seriesData,
            type: 'line',
          },
        ],
      });
    }
		// 채널이 한가지인 경우
		  }

데이터를 받아와 가공한 후 chart에 넣어주는 함수 addDataInChart()를 위와 같이 수정하였다.

데이터를 요청할 때 params에 채널 정보를 담았는지를 기준으로 분기처리 하였고, 받아온 데이터를 Echart form에 맞도록 가공하는 과정을 더했다.

분기처리를 하는 부분을 filters.channels.length 기준으로 나눴는데, 여기서 filters란 데이터를 API에 요청할 때 보내는 params 객체를 의미한다. filters객체 내 channels 값에 접근하고 이 값이 있다면 채널 정보를 담아 데이터를 요청한 것이다.

채널의 값이 있는경우 forEach문안에 forEach문을 사용해, 이중배열 구조로 채널별 데이터를 Echart option 속성의 series 키 값 안에 넣을 수 있었다.

profile
학습한 지식을 개인적으로 정리하기 위해 만든 블로그입니다 :)

0개의 댓글