본문 바로가기

프로젝트/웹페이지

웹 개발 1도 모르는 사람이 웹페이지 만들기 프로젝트 6일차

728x90
반응형

썸네일 - 뤼튼 생성 AI 이미지

 

6일차에는 차트를 추가해보도록 하겠습니다.

 

미리 보는 오늘의 목표

 

📊 차트 라이브러리 설치하기

 

차트를 추가하기 위한 라이브러리를 설치합니다.

install chart.js react-chartjs-2 // 필수
install chartjs-plugin-datalabels // 선택

 

 

https://react-chartjs-2.js.org/

 

react-chartjs-2 | react-chartjs-2

React components for Chart.js

react-chartjs-2.js.org

 

라이브러리 공홈에 들어가면 다양한 차트 예제들을 확인할 수 있습니다.

오늘의 아래 이미지와 나와있는 원형 차트를 사용하겠습니다.

 

 

🖼️ 원형 차트 구현하기

 

원형 차트를 구현하기 전에 필요한 작업이 생겼습니다!

현재 page.js에는 노션 API를 호출하는 코드가 있어 서버 사이드에서 실행되고 있습니다.

하지만 차트는 브라우저에서 렌더링되기 때문에 클라이언트 사이드에서 작동되어야 합니다.

따라서 page.js에서 클라이언트 사이드에서 작동되어야 하는 코드는 따로 분리하겠습니다.

 

'use client' // 추가

export default function MainClient({ memberData }) { // 매개변수는 서버에서 받아 오는 데이터
    return (
        <section className="text-gray-500 body-font">
            <div className="container px-5 py-24 mx-auto">
                <div className="flex flex-col text-center w-full mb-20">
                    <h1 className="sm:text-3xl text-2xl font-medium title-font mb-4 text-gray-900">
                        짭알못이란?
                    </h1>
                    <p className="lg:w-2/3 mx-auto leading-relaxed text-base">
                        과거에 '금요일을 알차게보내는건 못참지'라는 금요일마다 자기계발을 하는 모임이 있었습니다.
                        <br />
                        저희는 그 모임과 별개로 매주 목요일마다 자기계발 모임을 하였습니다.
                        <br />
                        어느날 누군가 '짭알못이다!' 라고 외친 이후로 그렇게 불리게되었습니다...
                        <br />
                        하지만 지금은 우리가 찐 ㅋ😎
                    </p>
                </div>
                <div className="flex flex-wrap -m-2">
                    {Array.from(data.entries()).map(([member, { memberName, totalTime, iconUrl, stateCounts }]) => (
                        <div key={member} className="p-4 lg:w-1/3 md:w-1/2 w-full">
                            <div className="h-full flex flex-col border-gray-200 border p-4 rounded-lg">
                                <div className="flex items-center">
                                    {iconUrl && (
                                        <img
                                            src={iconUrl}
                                            alt={`${member}의 아이콘`}
                                            className="w-12 h-12 mr-4 rounded-full"
                                        />
                                    )}
                                    <div>
                                        <h2 className="text-gray-900 title-font font-medium">{memberName}</h2>
                                        <p className="text-gray-500">총 {Math.floor(totalTime)} 시간</p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    ))}
                </div>
            </div>
        </section>
    );
}
.
.
.
// 데이터 객체화
const memberData = Array.from(memberTimeMap.entries()).map(([memberName, data]) => ({
    memberName,
    ...data,
  }));

  return (
    <Layout>
      <MainClient memberData={memberData} /> // 분리한 클라이언트 코드로 데이터 넘겨줌
    </Layout>
  );
 }

 

그리고 원형 차트를 구현 할 새로운 js도 만들어 줍니다.

pie.js로 만들었고 공홈에 있는 기본 예제를 추가하고 조금 수정하겠습니다.

 

import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
import { Pie } from 'react-chartjs-2';

ChartJS.register(ArcElement, Tooltip, Legend);

const PieChart = () => {
  const data = {
    labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
    datasets: [
      {
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        backgroundColor: [
          'rgba(255, 99, 132, 0.2)',
          'rgba(54, 162, 235, 0.2)',
          'rgba(255, 206, 86, 0.2)',
          'rgba(75, 192, 192, 0.2)',
          'rgba(153, 102, 255, 0.2)',
          'rgba(255, 159, 64, 0.2)',
        ],
        borderColor: [
          'rgba(255, 99, 132, 1)',
          'rgba(54, 162, 235, 1)',
          'rgba(255, 206, 86, 1)',
          'rgba(75, 192, 192, 1)',
          'rgba(153, 102, 255, 1)',
          'rgba(255, 159, 64, 1)',
        ],
        borderWidth: 1,
      },
    ],
  };

  return <Pie data={data} />;
};

export default PieChart; // 메인 클라이언트 코드에서 사용하기 위해 수정

 

그리고 메인 클라이언트 코드의 멤버별 구역에 원형 차트를 추가합니다.

<div className="h-full flex flex-col border-gray-200 border p-4 rounded-lg">
    <div className="flex items-center">
        {iconUrl && (
            <img
                src={iconUrl}
                alt={`${member}의 아이콘`}
                className="w-12 h-12 mr-4 rounded-full"
            />
        )}
        <div>
            <h2 className="text-gray-900 title-font font-medium">{memberName}</h2>
            <p className="text-gray-500">{stateCounts ? Object.values(stateCounts).reduce((sum, count) => sum + count, 0) : 0}일 동안 {Math.floor(totalTime)} 시간</p>
        </div>
    </div>
    <div>
        <PieChart /> // 원형 차트 구역 추가
    </div>
</div>

 

멤버별 구역에 원형 차트가 추가되었습니다!

 

 

 

🛠️ 원형 차트 데이터 수정하기

 

원형 차트에 넣을 데이터는 각 활동 기록의 멤버들이 표시한 상태입니다.

상태는 '좋음', '보통', '나쁨'으로 나누어져 있습니다.

우선 노션 API로 받아올 때 상태 데이터도 추가해줍니다.

allPages = [
    ...allPages,
    ...response.results.map((page) => {
      // 생략
      const state = page.properties["상태"]?.select?.name || "좋음"; // 추가

      return { iconUrl, memberName, time, state }; // 수정
    }),
];

allPages.forEach((page) => {
  const { iconUrl, memberName, time, state } = page; // 수정

  if (!memberTimeMap.has(memberName)) {
    memberTimeMap.set(memberName, {
      totalTime: 0,
      iconUrl,
      stateCounts: { 좋음: 0, 보통: 0, 나쁨: 0 }, // 추가
    });
  }

  const currentData = memberTimeMap.get(memberName);
  memberTimeMap.set(memberName, {
    ...currentData,
    totalTime: currentData.totalTime + time,
    stateCounts: {
      ...currentData.stateCounts,
      [state]: (currentData.stateCounts[state] || 0) + 1,// 추가
    },
  });
});

 

메인 클라이언트에서 pie에 상태들의 데이터를 넘겨줍니다.

<div>
    <PieChart
        stateCounts={stateCounts} // 상태 정보
    />
</div>

 

pie 코드를 수정합니다.

const PieChart = ({ stateCounts }) => { // 받아온 데이터
    const counts = stateCounts || { 좋음: 0, 보통: 0, 나쁨: 0 }; // 데이터 초기화
    const data = {
        labels: ["좋음", "보통", "나쁨"], // label 이름 수정
        datasets: [
            {
                data: [counts.좋음, counts.보통, counts.나쁨], // 데이터 입력
                backgroundColor: [
                    "rgba(75, 192, 192, 0.2)",
                    "rgba(255, 205, 86, 0.2)",
                    "rgba(255, 99, 132, 0.2)",
                ],
                borderColor: [
                    "rgba(75, 192, 192, 1)",
                    "rgba(255, 205, 86, 1)",
                    "rgba(255, 99, 132, 1)",
                ],
                borderWidth: 1,
            },
        ],
    };

    return <Pie data={data} />;
};

export default PieChart;

 

짜잔! 실제 데이터가 적용된 모습입니다.

그런데 크기도 너무 큰거 같고 차트를 hover 했을 때 나오는 툴팁도 수정하고 싶어졌습니다.

 

 

🔌 차트 플러그인

 

맨 처음에 선택으로 설치하라고 했던 차트 플러그인을 사용하겠습니다.

툴팁에 현재 상태가 전체의 몇 퍼센트를 차지하는지 나타나게했습니다.

또한 크기 조절하는 코드도 추가하고 '총 며칠 중 몇 시간'인지 수정했습니다.

또또... 비동기로 데이터를 받아오기 때문에 클라이언트에서 데이터 로딩이 끝나면 렌더링하도록 추가했습니다.

const options = {
        plugins: {
            legend: { position: "bottom" },
            tooltip: {
                enabled: true,
                callbacks: {
                    label: function (tooltipItem) {
                        const percentage = ((tooltipItem.raw / total) * 100).toFixed(2);
                        return `${tooltipItem.label}: ${percentage}%`;
                    },
                },
            },
        },
        maintainAspectRatio: false,
    };

    return (
        <div className="relative w-full h-full"> // 크기 조절
            <Pie data={data} options={options} />
        </div>
    );

// 여기부터는 메인 클라이언트
// 로딩 상태 확인
const [data, setData] = useState(null);

useEffect(() => {
    if (memberData) {
        console.log(memberData);
        setData(memberData);
    }
}, [memberData]);

if (!data) {
    return <div>Loading...</div>;
}

// 총 며칠인지 추가
<p className="text-gray-500">{stateCounts ? Object.values(stateCounts).reduce((sum, count) => sum + count, 0) : 0}일 동안 {Math.floor(totalTime)} 시간</p>

 

차트에 hover하면 툴팁에 원하던 내용이 나타나는 걸 확인할 수 있습니다!

 

👉 참고하면 좋습니다.

https://www.chartjs.org/

 

Chart.js

Simple yet flexible JavaScript charting library for the modern web

www.chartjs.org

 

 

요즘 페이지를 만들며 새로운 기능을 하나씩 추가하는게 꽤나 재미집니다 후후...

내일도 얼른 다른 기능을 추가해야겠습니다!

 

728x90
반응형