두 번째 라이브러리 게시글입니다!
이 인터셉터도 이전의 모달과 마찬가지로 프로젝트를 진행할 떄마다 코드를 짜는게 너무 귀찮았어요.
거의 모든 프로젝트들이 JWT 토큰을 사용하기 때문에 인터셉터도 항상 같을 수밖에 없었어요. 그래서 이 라이브러리를 만들게 됐답니다.
깃허브 레포지토리 주소
https://github.com/nyeonseok/nyeonseok-interceptor
먼저 사용할 수 있는 환경에 대해 말해볼게요.
- JWT 토큰을 사용하는 환경
- access token, refresh token을 모두 사용하는 환경
- refresh token은 httpOnly 설정된 환경
이렇게 3 가지를 만족하면 이 라이브러리를 사용할 수 있어요!
그럼 어떻게 사용하는지 알아보겠습니다.
1. 설치
라이브러리를 사용하기 위해 먼저 라이브러리를 설치해야겠죠? 이번 라이브러리도 npm으로 설치해요!
다음 줄을 터미널에 작성합니다.
# 프로젝트에 axios 설치
npm install axios
# nyeonseok-interceptor 라이브러리 설치
npm install nyeonseok-interceptor
npm이 이미 설치되어 있다면 두 번째 줄만 작성하면 됩니다.
2. createApiClient import
createApiClient는 제가 만든 인터셉터의 함수 이름이에요. import 해줘야 쓸 수 있으니 인터셉터를 쓰려는 페이지 상단에서 다음 줄을 작성해주세요!
import { createApiClient } from "nyeonseok-interceptor";
3. 기본 설정
다들 인터셉터에 대해 알고 계시겠지만, 라이브러리 사용 이해를 위해 간략히 설명해볼게요.
JWT 기반 인증/인가 로직은 다음과 같습니다.
- 로그인 성공 시, 서버에서 access token, refresh token을 발급해줌.
- 클라이언트는 access token을 보통 localStorage, sessionStorage에 저장함. refresh token은 httpOnly 설정하여 쿠키로 저장 (XSS 공격 방지)
- 이후에 서버와 API 통신할 때마다 header에 access token을 담아서 보냄 => 인증
- 서버는 받은 access token을 가지고 해당 API/리소스에 접근할 권한이 있는지 판단 => 인가
- 만약 access token이 만료됐다면 access token을 갱신해야 함. 이때 refresh token을 보내서 access token 갱신 요청.
- 갱신 성공한다면 다시 access token을 보내서 인증 & 인가.
- 갱신 실패한다면 보통 로그아웃 처리.
보통 이런 흐름으로 진행됩니다.
이 설명을 한 이유는 프로젝트마다 설정하는 게 다 다르기 때문이에요. 바로 예시 설정을 보여드릴게요.
const api = createApiClient({
// .env에 정의한 기본 경로 변수명 작성, 하드코딩으로 바로 기본 경로 작성해도 무관
baseURL: import.meta.env.VITE_BASE_URL,,
// AccessToken을 가져오는 함수 작성, 여기선 localStorage에 저장하고 가져오는 방식 채택
getAccessToken: () => localStorage.getItem("accessToken"),
// 요청 Interceptor로 재발급받은 AccessToken을 저장하는 함수 작성.
setAccessToken: (token) => localStorage.setItem("accessToken", token),
// RefreshToken를 통해 access token을 갱신해주는 API 주소 작성
refreshEndpoint: "/api/auth/refresh",
// 로그아웃 함수 작성
onLogout: () => {
localStorage.removeItem("accessToken");
window.location.href = "/login";
},
});
우선 설정해야 할 부분은 총 5개에요. 주석에도 적혀 있지만, 조금 더 자세하게 설명해보도록 할게요!
- 백엔드 서버 주소를 정의해주면 돼요. 백엔드 서버와 통신하기 때문에 해당 주소를 적어주면 됩니다.
baseURL: import.meta.env.VITE_BASE_URL,,
- access token을 가져오는 함수에요. 저는 보통 localStorage에 저장하기 때문에 다음과 같이 작성했어요!
getAccessToken: () => localStorage.getItem("accessToken"),
- refresh token으로 access token을 갱신했을 때 저장하는 함수에요.
setAccessToken: (token) => localStorage.setItem("accessToken", token),
- refresh token으로 access token을 갱신해주는 api 주소에요.
refreshEndpoint: "/api/auth/refresh",
- access token 갱신 실패 시 로그아웃 처리 함수에요.
onLogout: () => {
localStorage.removeItem("accessToken");
window.location.href = "/login";
},
다시 말하지만 위 코드는 제가 쓰는 방식으로 작성한 것이기 때문에 얼마든지 자신에게 맞게 작성하시면 돼요.
4. interceptor 사용
이제 설정할 건 다 했으니 어떻게 사용하는지 알아보겠습니다!
const fetchUserInfo = async () => {
try {
// 통신할 때 앞에 api 붙여주면 됨
const response = await api.get("api/my-info");
if (response.status === 200) {
alert("성공");
} else {
const error = await response.data;
alert(error.message);
}
} catch (error: any) {
const errorMessage =
error.response?.data?.message ||
error.message ||
"알 수 없는 오류 발생";
alert(errorMessage);
}
};
사용자 정보를 불러오는 비동기 함수를 작성해봤어요. 받아온 값을 설정하는 부분은 제외하고 전체적인 흐름을 볼 수 있도록 작성했습니다!
결론부터 말하면
const response = await api.get("api/my-info");
여기서 api. 를 붙인 게 interceptor을 적용한 거에요.
그럼 저 코드는 무슨 뜻이냐 하면, 아까 위에서 정의한 baseURL + "api/my-info" 로 get 요청을 보내는 거에요.
그리고 그 이후에 로직은 아까 interceptor 인증/인가에서 설명한 것과 같이 흘러갑니다.
5. 예시 코드
import { createApiClient } from "nyeonseok-interceptor";
export default function Profile() {
const api = createApiClient({
baseURL: import.meta.env.VITE_BASE_URL,
getAccessToken: () => localStorage.getItem("accessToken"),
setAccessToken: (token) => localStorage.setItem("accessToken", token),
refreshEndpoint: "/api/auth/refresh",
onLogout: () => {
localStorage.removeItem("accessToken");
window.location.href = "/login";
},
});
const fetchUserInfo = async () => {
try {
const response = await api.get("api/my-info");
if (response.status === 200) {
alert("성공");
} else {
const error = await response.data;
alert(error.message);
}
} catch (error: any) {
const errorMessage =
error.response?.data?.message ||
error.message ||
"알 수 없는 오류 발생";
alert(errorMessage);
}
};
return <></>;
}
createApiClient에서 설정한대로 interceptor은 동작합니다!
6. 마무리
이렇게 두 번째 라이브러리 게시글도 끝났습니다.
간단하게 사용하길 바라는 마음에서 배포하게 됐는데 간단하다고 느끼실 지 모르겠네요,,
다른 라이브러리를 만들고 또 다른 라이브러리 게시글을 작성하도록 하겠습니다!!
혹시라도 사용하다가 오류가 있거나 개선하면 좋겠는 점이 있다면 말해주세요!
감사합니다!
'라이브러리' 카테고리의 다른 글
| React 환경에서 웹 화면에 Modal 창 쉽게 적용하기 (2) | 2025.09.28 |
|---|