[GitPulse #4] 토큰 기반 요청 & 주간 MVP 기능 구현하기
[GitPulse #4] 토큰 기반 요청 & 주간 MVP 기능 구현하기
✅ 오늘의 목표
- GitHub API 요청 시 JWT 토큰을 포함한 인증 방식 일괄 변경
- 사용자별 커밋 수 분석을 통해 이번 주 MVP 추출
- PR 설명에 Markdown 렌더링 적용
🧩 문제 상황 / 배경
현재 팀 프로젝트에서 GitHub API를 활용해 조직, 레포지토리, 커밋 데이터를 가져오고 있었습니다.
그러나 클라이언트에서 직접 GitHub API를 호출하는 방식은 다음과 같은 문제를 일으켰습니다.
- 비인증 요청이므로 접근할 수 있는 데이터에 한계가 있었고,
- 모든 팀원이 같은
GITHUB_CLIENT_ID
,GITHUB_CLIENT_SECRET
를 쓰다 보니 API 호출 횟수가 금방 초과되는 문제가 발생했습니다. - 보안상으로도 클라이언트에서 민감한 정보를 직접 다루는 구조는 취약했습니다.
추가로
- 팀원들의 기여도를 보다 직관적으로 파악할 수 있도록 커밋 수 기반 MVP 기능을 추가하고 싶었고,
- GitHub의 PR 메시지는 Markdown 포맷으로 작성되지만 UI상 렌더링이 제대로 되지 않아 가독성이 떨어지는 문제가 있었습니다.
🛠️ 해결 과정 / 코드 정리
🔸 GitHub API 요청 구조 개선 (JWT 인증 & 프록시 서버)
기존에는 axios.get(BASE_URL)
형식으로 클라이언트에서 GitHub API를 직접 호출했지만, jwt
토큰을 활용한 프록시 서버 경유 요청으로 변경
✅ fetchWithToken 함수 - frontend/src/apis/github.js
1
2
3
4
5
6
7
8
9
export const fetchWithToken = async (path, params = {}) => {
const token = localStorage.getItem("jwt");
const res = await axios.get(`${API_BASE}/github/proxy`, {
params: { path, ...params },
headers: { Authorization: `Bearer ${token}` },
withCredentials: true,
});
return res.data;
};
- 프론트에서 JWT 토큰을 포함한 요청을 프록시 서버에 보낸다.
- 프록시 서버가 GitHub API를 대신 호출해 데이터를 가져오고, 클라이언트에 전달한다.
❓ 프록시 서버란 ? 클라이언트와 서버 사이에서 중간 역할을 하는 서버
역할 | 설명 |
---|---|
1. 우회 요청 | 서버에 직접 접근하지 않고 프록시를 거쳐서 요청 |
2. CORS 우회 | 프론트엔드에서 API 호출 시 CORS 오류가 날 때, 프록시를 두고 우회함 |
3. 보안 | 클라이언트 IP를 숨길 수 있음 (대신 프록시의 IP로 요청) |
4. 캐싱 기능 | 자주 요청되는 데이터를 저장해두고 빠르게 응답 |
🔁 기존 코드 → 변경 코드
기존 GitHub API 요청 방식:
1
2
3
4
5
6
7
8
export const getOrganizationsByUser = async (username) => {
try {
const res = await axios.get(`${BASE_URL}/users/${username}/orgs`);
return res.data;
} catch (error) {
console.log("조직 가져오기 실패", error);
}
};
변경 후:
1
2
3
4
5
6
7
8
export const getOrganizationsByUser = async (username) => {
try {
const res = await fetchWithToken(`/users/${username}/orgs`);
return res;
} catch (error) {
console.log("조직 가져오기 실패", error);
}
};
Organizations의 멤버 및 레포 정보도 마찬가지로 fetchWithToken
으로 일괄 적용.
🔸 ReactMarkdown 적용 (PR 메시지 처리)
PR 메시지는 대부분 Markdown 포맷으로 작성되지만, 기존 UI에서는 일반 텍스트로 출력되어 가독성이 떨어짐
PR 메시지 예시
1
"body": "## 📝 변경 사항\r\n\r\n> 커밋 단위로 명시적으로 짧게 작성합니다.\r\n\r\n1.\r\n\r\n## 🔍 변경 사항 세부 설명\r\n\r\n> 세부 설명을 작성합니다.\r\n\r\n-\r\n\r\n## 🕵️\u200d♀️ 요청사항\r\n\r\n> 팀원들에게 테스트나 리뷰를 요청합니다.\r\n\r\n-\r\n\r\n## 📷 스크린샷 (선택)\r\n\r\n> UI 변경이 있는 경우 스크린샷이나 GIF를 첨부합니다.\r\n\r\n## ✅ 작성자 체크리스트\r\n\r\n- [x] 커밋 메시지 컨벤션에 맞게 작성했습니다.\r\n- [x] 변경 사항에 대한 테스트를 했습니다(버그 수정/기능에 대한 테스트)\r\n- [ ] Self-review: commit 이나 오타 없이 코드가 스스로 검토됨\r\n- [ ] 로컬에서 모든 기능이 정상 작동함(버그수정 /기능에 대한 테스트)\r\n- [ ] 린터 및 포맷터로 코드 정리됨\r\n"
도입한 해결책: react-markdown
라이브러리
1
npm install react-markdown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import ReactMarkdown from "react-markdown";
<div className={orgs.RecentlyItem}>
<h3>따끈따끈 PR 소식</h3>
{pulls && (
<div className={orgs.RecentPRItem}>
<div>{pulls.title}</div>
<div>{pulls.user.login}</div>
<div>
<ReactMarkdown>{pulls.body}</ReactMarkdown>
</div>
<div>{pulls.created_at}</div>
</div>
)}
</div>
ReactMarkdown 도입 후 UI
🔸 이번 주 커밋 MVP 찾기
커밋 데이터 분석을 통해 현재 유저를 기준으로 일주일 간의 커밋 현황을 파악하고, 커밋 수가 가장 많은 유저를 이번 주 MVP로 선정했다.
일주일 날짜 배열 생성
1
2
3
4
5
const dateArray = [...Array(7)].map((_, i) => {
const d = new Date(today);
d.setUTCDate(today.getUTCDate() - (6 - i));
return d.toISOString().split("T")[0];
});
커밋 수 집계 및 MVP 추출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const userCommitMap = {};
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";
if (commitCountByDay[dateStr]) {
commitCountByDay[dateStr].total += 1;
if (authorLogin === curUserLogin) {
commitCountByDay[dateStr].mine += 1;
}
userCommitMap[authorLogin] = (userCommitMap[authorLogin] || 0) + 1;
}
});
const topCommitter = Object.entries(userCommitMap).reduce(
(acc, [author, count]) => (count > acc.count ? { author, count } : acc),
{ author: "", count: 0 }
);
setTopCommit(topCommitter);
- 각 커밋의 작성자(author) 추출
commit.author?.login
또는commit.commit.author.name
을 활용해 커밋의 작성자를 식별합니다.
- 작성자별 커밋 수 누적 (userCommitMap 생성)
userCommitMap
객체에 작성자를 key로 등록하고, 커밋이 발견될 때마다 값을 1씩 증가시켜 누적합니다.
- MVP 선정 (topCommitter 계산)
userCommitMap
을 순회하며 가장 많은 커밋 수를 기록한 작성자를 MVP로 선정합니다.
- 선정된 MVP 정보 저장
setTopCommit(topCommitter)
를 통해 MVP 정보를 상태로 저장하고 UI에 반영합니다.
❓Object.entries()
란?
Object.entries(obj)
는 객체의 모든 열거 가능한 속성(own enumerable properties)을 [key, value]
형태의 2차원 배열로 반환합니다.
1
Object.entries(obj)
obj
: 객체- 반환값:
[ [key1, value1], [key2, value2], ... ]
형태의 배열
다른 관련 메서드들
메서드 | 설명 | 반환값 |
---|---|---|
Object.keys(obj) | 객체의 key 값들을 배열로 반환 | ["name", "age", "job"] |
Object.values(obj) | 객체의 value 값들을 배열로 반환 | ["Alice", 25, "Developer"] |
Object.entries(obj) | 객체를 [key, value] 배열로 반환 | [["name", "Alice"], ... ] |
MVP 시각화 UI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div className={orgs.RecentlyItem}>
<h3>이번 주 MVP</h3>
<div className={orgs.RankingItem}>
{topCommit && (
<>
<div>{topCommit.author}</div>
<img src="/img/topCommit.png" />
<div>
{topCommit.author}님의 이번주 커밋 수는
<strong>{topCommit.count}</strong>번 입니다
</div>
</>
)}
</div>
</div>
✅ 결과 및 느낀 점
- GitHub API 요청 시 JWT 토큰을 이용한 프록시 서버 방식으로 인증 문제와 보안 이슈를 해결
- PR 설명을 Markdown으로 제대로 렌더링하면서, 가독성 있는 UI와 개발자 친화적인 환경을 구축
- MVP 선정 기능을 통해 팀원 간의 커밋 활동을 게임화(gamification) 할 수 있었고, 실시간으로 기여도를 확인하는 재미도 생김.
🖼️ 작업 결과물
Reference
이 포스트는 아래 게시글의 정보 및 이미지가 사용되었습니다.
- GitHub REST API v3 Documentation
- JWT (JSON Web Tokens) - Introduction
- ReactMarkdown GitHub
- MDN Web Docs - Date 객체
END
This post is licensed under CC BY 4.0 by the author.