KIM-JINO5 2 сар өмнө
parent
commit
a5696a08c5

+ 2 - 2
app/api/auth/[...path]/route.ts

@@ -29,8 +29,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
 
 	const response = NextResponse.json(res);
 
-	// 로그인 성공 시 쿠키 설정
-	if (path[0] === 'login' && res.success && res.data) {
+	// 로그인 또는 토큰 갱신 성공 시 쿠키 설정
+	if ((path[0] === 'login' || path[0] === 'refresh-token') && res.success && res.data) {
 		const data = res.data as LoginResponse;
 		const cookieOptions = { httpOnly: true, path: '/' };
 		response.cookies.set('accessToken', data.accessToken, cookieOptions);

+ 30 - 3
lib/utils/client.ts

@@ -14,20 +14,47 @@ interface FetchApiOptions extends Omit<RequestInit, 'body'> {
 	body?: unknown;
 }
 
+// 토큰 갱신 중복 방지
+let refreshPromise: Promise<boolean> | null = null;
+
+async function tryRefreshToken(): Promise<boolean> {
+	if (refreshPromise) {
+		return refreshPromise;
+	}
+
+	refreshPromise = fetch('/api/auth/refresh-token', { method: 'POST' })
+		.then(res => res.json())
+		.then((res: ResultDto<unknown>) => res.success)
+		.catch(() => false)
+		.finally(() => { refreshPromise = null; });
+
+	return refreshPromise;
+}
+
 export async function fetchApi<T>(url: string, options?: FetchApiOptions): Promise<ResultDto<T>> {
 	const isFormData = options?.body instanceof FormData;
 	const hasBody = options?.body !== undefined;
 
-	const res = (await fetch(url, {
+	const fetchOptions: RequestInit = {
 		...options,
 		headers: {
 			...(!isFormData && hasBody ? { 'Content-Type': 'application/json' } : {}),
 			...options?.headers
 		},
 		body: isFormData ? options.body as BodyInit : (options?.body ? JSON.stringify(options.body) : undefined)
-	}));
+	};
+
+	const res: ResultDto<T> = await (await fetch(url, fetchOptions)).json();
+
+	// 401 시 토큰 갱신 후 재시도
+	if (!res.success && res.status === 401) {
+		const refreshed = await tryRefreshToken();
+		if (refreshed) {
+			return (await fetch(url, fetchOptions)).json();
+		}
+	}
 
-	return res.json();
+	return res;
 }
 
 // JWT 토큰에서 사용자 정보 추출