[GitPulse #2] 구조 설계: GitHub 조직 API 연동
[GitPulse #2] 구조 설계: GitHub 조직 API 연동
✅ 오늘의 목표
- GitHub 조직 API 연동을 위한 React Query 커스텀 훅 작성
- 사용자 username(localStorage) 기반으로 조직 리스트 불러오기
- 사이드바 내 반복 코드 제거 및 동적 렌더링
🧩 문제 상황 / 배경
다른 팀원이 Github 로그인 기능을 구현한 상태였고, 사용자 인증은 JWT 기반으로 처리되고 있었습니다.
로그인 후 사용자의 조직 정보를 불러오고 사이드바에 동적으로 반영하려는 과정에서 다음과 같은 필요가 있었습니다:
- GitHub API를 통해 사용자 조직 리스트를 가져와야 했습니다.
- 로그인 후 받은 JWT 토큰에서
username
을 추출해 스토리지에 저장해야 했습니다. - 사이드바 메뉴가 반복적인 코드로 구성되어 있어, 동적으로 렌더링할 수 있도록 개선이 필요했습니다.
🛠️ 해결 과정 / 코드 정리
🔸 1. GitHub 조직 정보 불러오기 (React Query)
src/apis/useOrganizationApi.js
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
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
const BASE_URL = "https://api.github.com";
// 사용자 이름으로 조직 불러오기
export const getOrganizationsByUser = async (username) => {
try {
const res = await axios.get(`${BASE_URL}/users/${username}/orgs`);
return res.data;
} catch (error) {
console.log("조직 가져오기 실패", error);
}
};
export const useOrganizationList = (username) => {
return useQuery({
queryKey: ["OrganizationList", username],
queryFn: async () => {
try {
const data = username && (await getOrganizationsByUser(username));
return data.reverse(); // 최신순 반영
} catch (err) {
console.log("", err);
}
},
staleTime: 1000 * 60 * 10, // 10분 동안 캐시된 데이터를 사용함
retry: 1,
});
};
username
을 기반으로 조직 목록을 가져오는 함수 작성staleTime
을 10분으로 설정해 캐시 유지.reverse()
를 사용해 최신 조직부터 렌더링
✅ React Query를 사용한 이유는 ??
기존에
axios
만 사용한다면 다음과 같은 추가 구현이 필요했다:
- 로딩 상태 관리 (
isLoading
)- 에러 처리 (
try/catch
)- 동일 요청에 대한 캐싱 / 리패칭 제어
- refetch, prefetch, stale time 등 다양한 서버 상태 관리
반면, React Query는 이러한 서버 상태 로직을 선언적으로 처리할 수 있어 코드가 간결해지고 유지보수도 쉬워진다. 특히 GitHub API처럼 빈번하게 데이터를 가져오는 경우 자동 캐싱 및 리패칭 제어 기능이 매우 유용했다.
💡 React Query는 기본적으로 메모리에만 캐시를 저장하지만, persistQueryClient 플러그인을 사용하면 캐시를 영구 저장소(localStorage, sessionStorage 등)에 유지할 수 있다
GitHub Organizations 응답 데이터 구조
🔸 2. JWT 토큰에서 사용자 이름 추출 및 저장
src/pages/LoginPage.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
useEffect(() => {
const handleMessage = (event) => {
const token = event.data;
if (typeof token === "string" && token.split(".").length === 3) {
localStorage.setItem("jwt", token);
const payload = JSON.parse(atob(token.split(".")[1]));
localStorage.setItem("username", payload.login);
setSocialUser(payload);
navigate("/");
}
};
window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, []);
로그인 로직 내에서 JWT가 브라우저에 전달되면, username을 추출해 localStorage에 저장하도록 처리
- GitHub 로그인 후 메시지로 전달된 JWT 토큰을 수신
- 토큰의 payload에서 login 값을 추출해 username으로 저장
🔸 3. 반복적인 사이드바 메뉴 동적 렌더링으로 개선 - src/common/SideBar.jsx
기존 코드 (하드코딩 방식)
1
2
3
4
<CustomNavLink to={"/organization"} label={"Organization_1"} icon={"bi-people-fill"} />
<CustomNavLink to={"/organization2"} label={"Organization_2"} icon={"bi-people-fill"} />
<CustomNavLink to={"/organization3"} label={"Organization_3"} icon={"bi-people-fill"} />
...
개선된 코드
1
2
const username = localStorage.getItem("username");
const { data: groupList, isLoading, isError } = useOrganizationList(username);
1
2
3
4
5
6
7
8
{groupList?.map((group) => (
<CustomNavLink
key={group.id}
to={`/org/${group.id}/${group.login}`}
label={group.login}
icon={"bi-people-fill"}
/>
))}
- groupList를 기반으로 메뉴를 동적으로 생성 -> 반복 제거 및 유지보수 용이성 향상
🖼️ 작업 결과물
💡 개발 인사이트
1. React Query 캐싱은 새로고침 시 초기화된다
- React Query는 기본적으로 메모리 기반 캐싱을 제공한다.
- 따라서 브라우저를 새로고침하면 메모리가 초기화되면서 다시 API 요청이 발생한다.
1
2
// 해결 방법
import { persistQueryClient } from '@tanstack/react-query-persist-client';
지속 가능한 캐싱을 원한다면,
persistQueryClient
플러그인을 통해 localStorage, sessionStorage 등에 캐시를 저장할 수 있다.
2. username 비어있을 때 쿼리 방지: enabled
옵션 활용
useQuery
에서username
이 없는 경우에도 불필요한 요청이 발생할 수 있다.- 이런 경우
enabled
옵션을 활용하면 깔끔하게 방지할 수 있다.
1
2
3
4
5
useQuery({
queryKey: ["OrganizationList", username],
queryFn: () => getOrganizationsByUser(username),
enabled: !!username, // username이 있을 때만 호출
});
React Query는 불필요한 요청을 방지하고, 조건부 요청을 명확하게 선언할 수 있는 구조를 제공한다.
3. GitHub API는 퍼블릭 조직만 반환한다
- 현재 연동된 GitHub API (
GET /users/:username/orgs
)는 퍼블릭 조직 정보만 반환한다. - 비공개 조직 정보를 받기 위해서는 사용자 인증 시
read:org
범위의 OAuth 토큰이 필요합니다.
향후 비공개 조직 데이터가 필요할 경우, 백엔드에서 인증 범위를 추가하거나 토큰 권한 관리가 필요 !!
결과 및 느낀 점
- React Query를 활용해 비동기 호출에 따른 로딩/에러 처리를 고려하는 것의 편리함을 느낄 수 있었습니다.
- 사이드바 구성 코드를 반복 없이 동적으로 생성하면서 컴포넌트 구조의 중요성을 다시 한번 느꼈습니다.
- GitHub REST API는 직관적인 URL 구조와 잘 정리된 문서 덕분에 처음 사용하는 입장에서도 큰 어려움 없이 연동할 수 있었습니다.
- 사용자 기반 조직 리스트를 불러오는 과정에서 API 요청 결과가 어떤 구조로 오는지를 확인하고, 필요한 필드만 추려내어 사용하는 경험이 유익했습니다.
Reference
이 포스트는 아래 게시글의 정보 및 이미지가 사용되었습니다.
END
This post is licensed under CC BY 4.0 by the author.