Skip to content

Commit

Permalink
feat: use refresh token
Browse files Browse the repository at this point in the history
  • Loading branch information
twtwkim committed Dec 7, 2024
1 parent d0e2392 commit 0d0d30b
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 12 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"axios": "^1.7.9",
"jwt-decode": "^4.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.2",
Expand Down
5 changes: 0 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Navigate, Route, Routes } from "react-router-dom";
import { useEffect } from "react";
import "./App.css";
import MainPage from "./page/MainPage";
import AddItemPage from "./page/AddItemPage";
Expand All @@ -13,11 +12,7 @@ import RegisterPage from "./page/RegisterPage";
import { useSelector } from "react-redux";

function App() {
// const isAccessToken = localStorage.getItem("access_token");
const count = useSelector((state: any) => state.counter.value);
useEffect(() => {
console.log("Count changed:", count);
}, [count]);
return (
<Routes>
<Route path={ROUTES.LANDING} element={<LandingPage />} />
Expand Down
102 changes: 98 additions & 4 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const API_BASE_URL = "https://panda-market-api.vercel.app";

// 기본 함수
async function fetchApi(url: string, options = {}) {
try {
const response = await fetch(url, options);
Expand All @@ -15,6 +16,7 @@ async function fetchApi(url: string, options = {}) {
}
}

// 상품 가져오기 함수
export async function getProducts({
page = "",
pageSize = "",
Expand All @@ -24,29 +26,32 @@ export async function getProducts({
const params = new URLSearchParams({ page, pageSize, orderBy, keyword });
const url = `${API_BASE_URL}/products?${params}`;

return fetchApi(url);
return fetchWithAuth(url);
}

// 상품 id별 가져오기 함수
export async function getProductsById(productId: string | undefined) {
const url = `${API_BASE_URL}/products/${productId}`;

return fetchApi(url);
return fetchWithAuth(url);
}

// 댓글 가져오기 함수
export async function getCommentsById(
productId: string | undefined,
{ limit = "" }
) {
const params = new URLSearchParams({ limit });
const url = `${API_BASE_URL}/products/${productId}/comments?${params}`;

return fetchApi(url);
return fetchWithAuth(url);
}

interface UpdateCommentParams {
content: string;
}

// 댓글 수정 함수
export async function updateCommentsById(
commentId: number,
{ content }: UpdateCommentParams
Expand All @@ -60,7 +65,7 @@ export async function updateCommentsById(
body: JSON.stringify({ content }),
};

return fetchApi(url, options);
return fetchWithAuth(url, options);
}

interface SignupParams {
Expand All @@ -70,6 +75,7 @@ interface SignupParams {
passwordConfirmation: string;
}

// 회원가입 함수
export async function signup(data: SignupParams) {
const url = `${API_BASE_URL}/auth/signUp`;
const options = {
Expand All @@ -92,6 +98,7 @@ interface LoginParams {
password: string;
}

// 로그인 함수
export async function login(data: LoginParams) {
const url = `${API_BASE_URL}/auth/signIn`;
const options = {
Expand All @@ -106,3 +113,90 @@ export async function login(data: LoginParams) {
};
return fetchApi(url, options);
}

// 토큰을 디코딩하는 함수
const parseJwt = (token: string) => {
try {
const base64Url = token.split(".")[1];
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const decodedPayload = JSON.parse(atob(base64));
return decodedPayload;
} catch (error) {
console.error("JWT 파싱 오류 : ", error);
return null;
}
};

// 토큰이 만료되었는지 비교하는 함수
const isTokenExpired = (token: string) => {
if (!token) return true;

const decodedToken = parseJwt(token);
if (!decodedToken) return true;

const expirationTime = decodedToken.exp * 1000;
return Date.now() > expirationTime;
};

// 리프레시 토큰을 통해 새로운 엑세스 토큰을 받아오는 함수
const refreshAccessToken = async (refreshToken: string) => {
const url = `${API_BASE_URL}/auth/refresh-token`;
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ refreshToken }),
};

try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error("리프레시 토큰 요청 실패");
}

const data = await response.json();
const newAccessToken = data.accessToken;
if (newAccessToken) {
localStorage.setItem("access_token", newAccessToken);
}
return newAccessToken;
} catch (error) {
console.error("엑세스 토큰 갱신 오류:", error);
throw new Error("엑세스 토큰을 갱신할 수 없습니다.");
}
};

// 로컬 스토리지에서 엑세스 토큰을 가져오고 만료되었으면 리프레시 토큰으로 갱신하는 함수
export const getAccessToken = async () => {
let accessToken = localStorage.getItem("access_token");
const refreshToken = localStorage.getItem("refresh_token");

if (accessToken && !isTokenExpired(accessToken)) {
return accessToken;
}

if (refreshToken) {
return await refreshAccessToken(refreshToken);
}

return null;
};

// 모든 API 보낼때 토큰 담아서 보내는 함수
async function fetchWithAuth(url: string, options: RequestInit = {}) {
const accessToken = await getAccessToken();

if (!accessToken) {
throw new Error("로그인이 필요합니다.");
}

const authOptions = {
...options,
headers: {
...(options.headers || {}),
Authorization: `Bearer ${accessToken}`,
},
};
return fetchApi(url, authOptions);
}
1 change: 0 additions & 1 deletion src/components/LoginPage/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const Login = () => {
dispatch(check());
alert("로그인이 정상적으로 완료되었습니다.");
navigate("/");
console.log(res);
} catch (error: any) {
console.error(
"회원가입 실패:",
Expand Down
2 changes: 0 additions & 2 deletions src/redux/counterAccessToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ const counterSlice = createSlice({
reducers: {
check: (state) => {
state.value = true;
console.log("check 이후:", state.value);
},
reset: (state) => {
state.value = false;
console.log("reset 이후:", state.value);
},
},
});
Expand Down

0 comments on commit 0d0d30b

Please sign in to comment.