Skip to content

Commit 1d3d2b4

Browse files
committed
feat: get 요청이 아닌 경우 refreshToken 재발급 로직 수정
1 parent a4bb3ad commit 1d3d2b4

3 files changed

Lines changed: 105 additions & 70 deletions

File tree

apps/ticket/src/app/api/login/route.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { NextResponse } from "next/server";
22

3+
import { API_URL } from "@/data/constants";
4+
35
/**
46
* 로그인 요청 API
57
* NOTE: API 서버에서 Set-Cookie 설정해주는 것을 브라우저에서 동일하게 쿠키 설정하기 위함
68
*/
79
export async function POST(req: Request) {
810
const body = await req.json();
911

10-
const apiRes = await fetch(process.env.NEXT_PUBLIC_TICKET_API_BASE_URL + "/api/users/login", {
12+
const apiRes = await fetch(process.env.NEXT_PUBLIC_TICKET_API_BASE_URL + API_URL.USER.LOGIN, {
1113
method: "POST",
1214
headers: { "Content-Type": "application/json" },
1315
body: JSON.stringify(body),

apps/ticket/src/app/api/reissue/route.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import { NextResponse } from "next/server";
22

3+
import { API_URL } from "@/data/constants";
4+
35
export async function POST(req: Request) {
46
const body = await req.json();
57

6-
const apiRes = await fetch(process.env.NEXT_PUBLIC_TICKET_API_BASE_URL + "/api/users/reissue", {
7-
method: "POST",
8-
headers: { "Content-Type": "application/json" },
9-
body: JSON.stringify(body),
10-
credentials: "include",
11-
});
8+
const apiRes = await fetch(
9+
process.env.NEXT_PUBLIC_TICKET_API_BASE_URL + API_URL.USER.REISSUE_ACCESS_TOKEN,
10+
{
11+
method: "POST",
12+
headers: { "Content-Type": "application/json" },
13+
body: JSON.stringify(body),
14+
credentials: "include",
15+
},
16+
);
1217

13-
const setCookies = apiRes.headers.getSetCookie(); // 배열 반환
18+
const setCookies = apiRes.headers.getSetCookie();
1419

1520
const res = NextResponse.json(await apiRes.json(), {
1621
status: apiRes.status,

apps/ticket/src/lib/axios/clientAxios.ts

Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from "axios";
22

3-
import { API_URL } from "@/data/constants";
43
import { PATH } from "@/shared/constants/path";
54
import { IS_LOGINED } from "@/shared/constants/storage";
65
import { AxiosErrorResponse, isAxiosErrorResponse } from "@/shared/types/axioxError";
76

8-
import { safeLocalStorage, safeSessionStorage } from "../storage";
9-
import { refreshAccessToken } from "./helpers";
7+
import { safeSessionStorage } from "../storage";
108
import { ERROR_CODE } from "./utils/errorCode";
119

1210
export const clientAxios = axios.create({
@@ -28,72 +26,102 @@ clientAxios.interceptors.request.use(
2826
},
2927
);
3028

31-
const tokenRefreshPromise: Promise<void> | null = null;
3229
let isLoginAlertShown = false;
3330

31+
interface TempAxiosErrorResponse extends AxiosErrorResponse {
32+
config: InternalAxiosRequestConfig & { _retry?: boolean };
33+
}
34+
3435
// 응답 인터셉터
3536
clientAxios.interceptors.response.use(
3637
(response: AxiosResponse) => {
3738
return response.data;
3839
},
39-
async (error: AxiosErrorResponse) => {
40-
// if (error.config?.url === API_URL.USER.LOGOUT) {
41-
// safeLocalStorage.remove(IS_LOGINED);
42-
// window.location.href = PATH.LOGIN;
43-
// }
44-
// // 로그인이 필요한 요청이거나, 리프레시 토큰 모두 만료시 로그인 페이지로 이동
45-
// if (isAxiosErrorResponse(error.response?.data)) {
46-
// // 엑세스 토큰 없음
47-
// if (
48-
// error.response?.data.code === ERROR_CODE.NO_ACCESS_TOKEN ||
49-
// error.response?.data.code === ERROR_CODE.REFRESH_TOKEN_EXPIRED
50-
// ) {
51-
// redirectToLoginOnce();
52-
// }
53-
// if (error.response?.data.code === ERROR_CODE.LOGIN_REQUIRED) {
54-
// // 인증 페이지에서는 로그인 페이지로 이동하지 않음
55-
// if (window.location.pathname !== "/auth") {
56-
// redirectToLoginOnce();
57-
// }
58-
// }
59-
// if (error.response?.data.code === ERROR_CODE.PAYMENT) {
60-
// return Promise.reject(error?.response?.data);
61-
// }
62-
// // 액세스 토큰 만료
63-
// if (error.response?.data.code === ERROR_CODE.ACCESS_TOKEN_EXPIRED) {
64-
// try {
65-
// // 이미 토큰 재발급이 진행 중이면 완료될 때까지 대기
66-
// if (tokenRefreshPromise) {
67-
// console.log("@@@@@");
68-
// await tokenRefreshPromise;
69-
// } else {
70-
// // 토큰 재발급 시작
71-
// tokenRefreshPromise = refreshAccessToken()
72-
// .then(() => {
73-
// tokenRefreshPromise = null;
74-
// })
75-
// .catch((error) => {
76-
// tokenRefreshPromise = null;
77-
// throw error;
78-
// });
79-
// }
80-
// // 원래 요청 재시도
81-
// const originalRequest = error.config;
82-
// if (!originalRequest) {
83-
// return Promise.reject(error);
84-
// }
85-
// return clientAxios(originalRequest);
86-
// } catch {
87-
// if (typeof window !== "undefined") {
88-
// redirectToLoginOnce();
89-
// return Promise.reject(error?.response?.data);
90-
// }
91-
// }
92-
// }
93-
// }
94-
// if (error.response?.status === 500) {
95-
// return Promise.reject(error?.response?.data);
96-
// }
40+
async (error: TempAxiosErrorResponse) => {
41+
const original = error.config;
42+
43+
// get 요청이 아닌 경우 즉시 refreshToken 재발급 및 요청
44+
if (original?.method !== "get" && !original?._retry) {
45+
if (isAxiosErrorResponse(error.response?.data)) {
46+
if (error.response?.data.code === ERROR_CODE.ACCESS_TOKEN_EXPIRED) {
47+
original._retry = true;
48+
49+
try {
50+
const res = await fetch("/api/reissue", {
51+
method: "POST",
52+
headers: {
53+
"Content-Type": "application/json",
54+
},
55+
credentials: "include",
56+
});
57+
58+
return res.json();
59+
} catch (e) {
60+
redirectToLoginOnce();
61+
62+
return;
63+
}
64+
}
65+
}
66+
}
67+
68+
// 로그인이 필요한 요청이거나, 리프레시 토큰 모두 만료시 로그인 페이지로 이동
69+
if (isAxiosErrorResponse(error.response?.data)) {
70+
const { code } = error.response?.data ?? {};
71+
72+
// 엑세스 토큰 없음
73+
if (code === ERROR_CODE.NO_ACCESS_TOKEN || code === ERROR_CODE.REFRESH_TOKEN_EXPIRED) {
74+
redirectToLoginOnce();
75+
}
76+
77+
if (code === ERROR_CODE.LOGIN_REQUIRED) {
78+
// 인증 페이지에서는 로그인 페이지로 이동하지 않음
79+
if (window.location.pathname !== "/auth") {
80+
redirectToLoginOnce();
81+
}
82+
}
83+
84+
if (code === ERROR_CODE.PAYMENT) {
85+
return Promise.reject(error?.response?.data);
86+
}
87+
88+
// // 액세스 토큰 만료
89+
// if (code === ERROR_CODE.ACCESS_TOKEN_EXPIRED) {
90+
// try {
91+
// // 이미 토큰 재발급이 진행 중이면 완료될 때까지 대기
92+
// if (tokenRefreshPromise) {
93+
// console.log("@@@@@");
94+
// await tokenRefreshPromise;
95+
// } else {
96+
// // 토큰 재발급 시작
97+
// tokenRefreshPromise = refreshAccessToken()
98+
// .then(() => {
99+
// tokenRefreshPromise = null;
100+
// })
101+
// .catch((error) => {
102+
// tokenRefreshPromise = null;
103+
104+
// throw error;
105+
// });
106+
// }
107+
108+
// 원래 요청 재시도
109+
// const originalRequest = error.config;
110+
111+
// if (!originalRequest) {
112+
// return Promise.reject(error);
113+
// }
114+
115+
// return clientAxios(originalRequest);
116+
// } catch {
117+
// if (typeof window !== "undefined") {
118+
// redirectToLoginOnce();
119+
120+
// return Promise.reject(error?.response?.data);
121+
// }
122+
// }
123+
// }
124+
}
97125

98126
if (error?.response?.data) {
99127
return Promise.reject({

0 commit comments

Comments
 (0)