본문 바로가기

프로젝트 개발일지/플케어

[에러 해결] 평가 그래프 상 인원이 너무 많은 문제 해결

관리 페이지에서 평가 탭에서는 중간 / 최종 평가 그래프를 보여준다고 했다.

그래프를 보여주는 기능은 recharts 라이브러리를 사용해서 구현을 했다.

 

처음에 그래프를 보여주는 기능 자체에는 문제가 없었는데, 인원수가 너무 많아지니까 한 그래프로 전체 인원 평가 그래프를 보여주기에는 무리가 있었다.

그래서, 스크롤하면서 넘겨볼 수 있는 기능이 있으면 좋겠다고 생각해서, recharts 공식 문서도 찾아보고 구글링도 해봤지만, 해결 방법을 찾을 수 없었다.

아마 recharts 측에서 공식적으로 지원하지 않는 기능인 것 같았다.

 

그러다가, 팀원 중 한 분이 인원 수가 일정 기준을 넘어가면 카드 슬라이드처럼 넘기면서 볼 수 있도록 하는 것은 어떤 지 의견을 내주셨다.

물론, 한 그래프로 전체 인원의 차트 현황을 보는 것이 가장 좋지만, 지금 상황에서는 그게 가장 좋은 해결 방법 같았다.

그래서, 인원 수가 너무 많아지면 카드 슬라이드처럼 그래프를 볼 수 있게 구현해 봤다.

 

코드는 다음과 같다.

import { BarChart, Bar, XAxis, YAxis, Tooltip } from "recharts";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";

import { useState } from "react";

const CustomTooltip = ({ isCompleted, active, payload, label }) => {
  if (active && payload && payload.length) {
    return (
      <div className="custom-tooltip">
        {isCompleted ? (
          <>
            <p className="label">
              {`${label}`} <br />
              {`성실도: ${payload[0].value}`}점<br />
              {`시간 엄수: ${payload[1].value}`}점<br />
              {`업무 수행 능력: ${payload[2].value}`}점<br />
              {`의사 소통: ${payload[3].value}`}점
            </p>
          </>
        ) : (
          <>
            <p className="label">
              {`${label}`} <br />
              {`아이디어 뱅크: ${payload[0].value}`}개<br />
              {`열정적인 참여자: ${payload[1].value}`}개<br />
              {`최고의 서포터: ${payload[2].value}`}개<br />
              {`탁월한 리더: ${payload[3].value}`}개
            </p>
          </>
        )}
      </div>
    );
  }

  return null;
};

const settings = {
  dots: true,
  infinite: true,
  speed: 500,
  slidesToShow: 1,
  slidesToScroll: 1,
};

const evaluateBadgeMaxNumRounded = (data) => {
  let maxBadgeNum = 0;

  data.forEach((memberData) => {
    for (let evaluationData in memberData.evaluation) {
      if (memberData.evaluation[evaluationData] > maxBadgeNum) {
        maxBadgeNum = memberData.evaluation[evaluationData];
      }
    }
  });

  return maxBadgeNum + (5 - (maxBadgeNum % 5));
};

const chartIndices = (data, chunkSize) => {
  let indexArray = [];

  for (let i = 0; i < data.length / chunkSize; i++) {
    indexArray.push(i);
  }

  return indexArray;
};

const ShowEvaluationChart = ({ chartData = null, isCompleted }) => {
  const data = chartData.map((memberData) => {
    const evaluation = memberData.evaluation[0];

    return isCompleted
      ? {
          name: memberData.name,
          communication: evaluation.communication,
          jobPerformance: evaluation.jobPerformance,
          punctuality: evaluation.punctuality,
          sincerity: evaluation.sincerity,
        }
      : {
          name: memberData.name,
          ideaBankBadge: memberData.evaluation["아이디어_뱅크"],
          bestSupporterBadge: memberData.evaluation["최고의_서포터"],
          goodLeaderBadge: memberData.evaluation["탁월한_리더"],
          enthusiasticParticipantBadge:
            memberData.evaluation["열정적인_참여자"],
        };
  });

  const [chartDataNum, setChartDataNum] = useState(
    data.length < 9 ? data.length : Math.ceil(data.length / 3)
  );

  return chartData && isCompleted ? (
    <div className="evaluation-management-chart-wrapper">
      <h1>최종 평가 차트</h1>
      <div className="evaluation-management-show-evaluation-chart">
        <Slider className="slider" {...settings}>
          {chartIndices(data, chartDataNum).map((i) => (
            <div key={i}>
              <BarChart
                className="chart"
                width={495}
                height={320}
                data={data.slice(i * chartDataNum, (i + 1) * chartDataNum)}
              >
                <Tooltip
                  content={<CustomTooltip isCompleted={isCompleted} />}
                />
                <XAxis dataKey="name" tickSize={8} />
                <YAxis
                  dataKey="evaluation"
                  domain={[0, evaluateBadgeMaxNumRounded(chartData)]}
                />
                <Bar dataKey="sincerity" fill="#01E89E" />
                <Bar dataKey="punctuality" fill="#00AA72" />
                <Bar dataKey="jobPerformance" fill="#D7D7D7" />
                <Bar dataKey="communication" fill="#01E89E" />
              </BarChart>
            </div>
          ))}
        </Slider>
      </div>
    </div>
  ) : (
    <div className="evaluation-management-chart-wrapper">
      <h1>배지 차트</h1>
      <div className="evaluation-management-show-evaluation-chart">
        <Slider className="slider" {...settings}>
          {chartIndices(data, chartDataNum).map((i) => (
            <div key={i}>
              <BarChart
                className="chart"
                width={495}
                height={320}
                data={data.slice(i * chartDataNum, (i + 1) * chartDataNum)}
              >
                <Tooltip content={<CustomTooltip />} />
                <XAxis dataKey="name" tickSize={8} />
                <YAxis
                  dataKey="evaluation"
                  domain={[0, evaluateBadgeMaxNumRounded(chartData)]}
                />
                <Bar dataKey="ideaBankBadge" fill="#01E89E" />
                <Bar dataKey="enthusiasticParticipantBadge" fill="#00AA72" />
                <Bar dataKey="bestSupporterBadge" fill="#D7D7D7" />
                <Bar dataKey="goodLeaderBadge" fill="#01E89E" />
              </BarChart>
            </div>
          ))}
        </Slider>
      </div>
    </div>
  );
};

export default ShowEvaluationChart;

 

로직에 대해서 조금 더 알아보자.

중요한 부분은 아래 코드이다.

const [chartDataNum, setChartDataNum] = useState(
    data.length < 9 ? data.length : Math.ceil(data.length / 3)
  );

차트 상에 보여주어야 하는 데이터가 9개 이상이면 세 차트로 나눠서 볼 수 있게 설정했다.

8개 이하이면 그냥 한 차트로 볼 수 있게 설정해 놓았다.

 

이렇게 chartDataNum을 이용해서 한 번에 보여줄 차트 데이터 개수를 정하고 이를 사용해서 서버에서 불러온 차트 데이터를 슬라이싱해서 화면에 보여주게 된다.

 

그렇게 구현된 차트 사진은 다음과 같다.