Post

[GitPulse #3] 조직별 커밋 수 시각화하기

[GitPulse #3] 조직별 커밋 수 시각화하기

✅ 오늘의 목표

  • GitHub 조직의 레포지토리와 멤버 목록 불러오기
  • 선택한 레포지토리의 커밋 내역 가져오기
  • 요일별 커밋 수 계산 및 시각화

🧩 문제 상황 / 배경

  • GitHub 조직의 협업 기여도를 주간 단위로 한눈에 파악하기 어려움
  • 단순 커밋 수만 보여주는 방식은 누가 언제 활동했는지 파악하기 힘들었음
  • 협업 활성화와 주간 기여도 확인을 위한 시각적 커밋 분석 도구 필요

🛠️ 해결 과정 / 코드 정리

🔸 GitHub 조직 정보 병렬 요청

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export const getOrgsInfo = async (org) => {
  try {
    const [members, repos] = await Promise.all([
      axios.get(`${BASE_URL}/orgs/${org}/members`),
      axios.get(`${BASE_URL}/orgs/${org}/repos`),
    ]);
    return {
      members: members.data,
      repos: repos.data,
    };
  } catch (error) {
    console.log("조직 정보 가져오기 실패", error);
  }
};
  • membersrepos는 서로 의존적이지 않으므로 Promise.all()을 사용하여 병렬로 요청
  • 병렬 요청은 전체 응답 대기 시간 단축과 UX 개선에 효과적.

Promise.all()을 사용한 이유는?

  • 독립적인 비동기 요청을 동시에 처리해 불필요한 대기 시간을 줄일 수 있습니다.
  • 모든 요청이 성공해야 결과를 반환하며, 하나라도 실패하면 전체가 실패(reject)되므로 적절한 에러 핸들링이 중요합니다.
  • 병렬 요청을 통해 성능 최적화 가능하며, 전체 응답 대기 시간을 줄일 수 있습니다.
1
2
3
Promise.all([p1, p2, p3])
   p1, p2, p3가 모두 resolve되면 [v1, v2, v3] 반환
   하나라도 reject되면 즉시 error throw

Promise.all()은 단순히 “동시에 요청”하는 것이 아니라, 여러 개의 Promise 객체를 배열 형태로 받아 모두 resolve될 때까지 기다린 후, 결과를 배열 형태로 반환함
[병렬 처리 조건]

  • 각 요청이 독립적일 것
  • 모든 요청의 결과가 동시에 필요한 경우에 적합

🔸 선택한 레포지토리의 주간 커밋 수 계산 로직

1. 조직 레포의 커밋 정보 불러오기

1
2
3
4
5
6
7
8
9
// 조직 repo 불러오기
export const getOrgsRepos = async (orgs, repo) => {
  try {
    const res = await axios.get(`${BASE_URL}/repos/${orgs}/${repo}/commits`);
    return res.data;
  } catch (error) {
    console.log("조직 정보 가져오기 실패", error);
  }
};
  • 주어진 org, repo를 기준으로 해당 저장소의 커밋 내역을 요청

GitHub Organizations Commit 응답 데이터 구조

조직 repo의 커밋 api 응답

2. 주간 커밋 수 계산 로직

2-1. 커밋 날짜 기준으로 주간 범위 생성

즉, 5월 13일 커밋한 경우 7일 ~ 13일까지의 데이터를 가져와야함.

1
2
3
4
5
6
7
8
9
const today = new Date(); // Tue May 27 2025 15:20:15 GMT+0900 (한국 표준시)
today.setUTCHours(0, 0, 0, 0);  // Tue May 27 2025 09:00:00 GMT+0900 (한국 표준시)

//  ['2025-05-21', '2025-05-22', '2025-05-23', '2025-05-24', '2025-05-25', '2025-05-26', '2025-05-27']
const dateArray = [...Array(7)].map((_, i) => {
  const d = new Date(today);
  d.setUTCDate(today.getUTCDate() - (6 - i));
  return d.toISOString().split("T")[0];
});

✅ 왜 UTC 기준으로 생성하나요?

  • GitHub의 commit.commit.author.date는 UTC 기반임
  • 클라이언트에서도 UTC 기준으로 일주일을 생성해야 정확한 날짜 매칭이 가능
  • today.setUTCHours(0,0,0,0)으로 시간을 자정(00:00:00)으로 초기화 → 날짜 비교에서 시간 요소 제거.

내가 헷갈려서 정리하는 date() 변수

1
2
console.log(d) // Wed May 21 2025 15:43:16 GMT+0900 (한국 표준시)
console.log(d.toISOString()); // 2025-05-21T06:43:16.454Z
  • setUTCDate(n): 날짜를 UTC 기준으로 n일로 설정
  • getUTCDate(): 현재 날짜 객체의 UTC 기준 일(day) 값을 반환

2-2. 요일별 커밋 수 누적을 위한 초기 상태 구성

1
2
3
4
const commitCountByDay = dateArray.reduce((acc, dateStr) => {
  acc[dateStr] = { total: 0, mine: 0 };
  return acc;
}, {});
  • 커밋 수를 날짜별로 누적 저장하기 위해, 날짜별 키와 초기값을 설정
  • reducecommitCountByDay = { '2025-05-21': { total: 0, mine: 0 }, ... } 형식으로 초기화.

2-3. 커밋 데이터를 순회하면서 날짜별, 유저별 누적

1
2
3
4
5
6
7
commit.forEach((commit) => {
  const dateStr = new Date(commit.commit.author.date).toISOString().split("T")[0];
  const authorLogin = commit.author?.login || commit.commit.author?.name || "anonymous";

  commitCountByDay[dateStr].total += 1;
  if (authorLogin === curUserLogin) commitCountByDay[dateStr].mine += 1;
});
  • 날짜(dateStr) 기준으로 커밋을 그룹화하고, 현재 로그인한 사용자(curUserLogin)의 커밋을 별도로 구분해야 함
  • 작성자 구분은 login → name → fallback 순으로 식별
  • total,mine 각각 날짜 별 커밋 수 누적

전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
useEffect(() => {
  if (!commits || !curUserLogin) return;

  // 커밋 날짜 기준으로 주간 범위 생성
  const today = new Date();
  today.setUTCHours(0, 0, 0, 0); // 시간 초기화

  // 요일 배열
  const dateArray = [...Array(7)].map((_, i) => {
      const d = new Date(today);
      d.setUTCDate(today.getUTCDate() - (6 - i));
      return d.toISOString().split("T")[0];
  });

  const commitCountByDay = dateArray.reduce((acc, dateStr) => {
    acc[dateStr] = { total: 0, mine: 0 };
    return acc;
  }, {});

    // 커밋 수 계산
    commit.forEach((commit) => {
      const dateStr = new Date(commit.commit.author.date).toISOString().split("T")[0];
      const authorLogin = commit.author?.login || commit.commit.author?.name || "anonymous";

      commitCountByDay[dateStr].total += 1;
      if (authorLogin === curUserLogin) {
        commitCountByDay[dateStr].mine += 1;
    }
  });

  setCommitCounts(commitCountByDay); // 집계 결과 상태에 반영
}, [commits, curUserLogin]);

🔸 커밋 수 차트로 시각화 (Recharts)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<ResponsiveContainer width="100%" height={300}>
  <LineChart
    data={chartData}
    margin=
  >
    <CartesianGrid strokeDasharray="3 3" />
    <XAxis dataKey="name" />
    <YAxis allowDecimals={false} />
    <Tooltip />
    <Legend />
    <Line type="monotone" dataKey="total" stroke="#5f41b2" name="전체 커밋 수" />
    <Line type="monotone" dataKey="mine" stroke="#545d69" name="내 커밋 수" />
  </LineChart>
</ResponsiveContainer>
  • totalmine 두 개의 라인을 통해 팀 전체와 개인 커밋 비교 가능
  • 사용자 인터랙션을 고려한 Tooltip, Legend 적용

✅ 결과 및 느낀 점

  • 일별 커밋 수를 시각화하면서 협업 흐름을 쉽게 파악할 수 있게 되었고, 개인의 기여도도 명확해졌음
  • API 요청, 데이터 전처리, 시각화까지 전체 흐름을 경험하면서 프론트엔드 데이터 처리 능력을 키울 수 있었음
  • Recharts의 직관적인 API 덕분에 해당 프로젝트를 진행하면서 나의 기여도를 확인할 수 있었음.

🖼️ 작업 결과물

요일별 커밋 수 시각화


Reference

이 포스트는 아래 게시글의 정보 및 이미지가 사용되었습니다.

  1. GitHub REST API: Commits
  2. Recharts 공식 문서
  3. JavaScript ISO Date Formatting

END

This post is licensed under CC BY 4.0 by the author.