From 586d40a4b56af70af3d73ba1091d0647fa3d3884 Mon Sep 17 00:00:00 2001 From: "SK\\ssssk" Date: Tue, 16 Jul 2024 10:18:38 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=9D=B4=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20fetch=EC=9D=BC=EB=95=8C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8A=94=20fetch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/fetchWithAuth.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/utils/fetchWithAuth.ts diff --git a/src/utils/fetchWithAuth.ts b/src/utils/fetchWithAuth.ts new file mode 100644 index 00000000..8a2ab2dd --- /dev/null +++ b/src/utils/fetchWithAuth.ts @@ -0,0 +1,24 @@ + +export async function fetchWithAuth(url: string, options = {}, retries = 1) { + + try { + const response = await fetch(url, options); + if (response.status === 401 && retries > 0) { + // 토큰 갱신 + const data = await fetch( + `/api/auth/refresh-access-token`, + ); + if (data.status == 401) { + return Promise.reject({ + status: 401, + message: "실패", + }); + } + // TODO: 추가적인 에러처리 필요 + return await fetchWithAuth(url, options, 0); // 요청 재시도 + } + return response; + } catch (error) { + throw error; + } +} \ No newline at end of file From ff3daba2674e0c148d33471275905f4ed892a084 Mon Sep 17 00:00:00 2001 From: "SK\\ssssk" Date: Tue, 16 Jul 2024 10:19:15 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/containers/common/HeaderContainer.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/containers/common/HeaderContainer.tsx b/src/containers/common/HeaderContainer.tsx index 4f61fa8a..24c60e5e 100644 --- a/src/containers/common/HeaderContainer.tsx +++ b/src/containers/common/HeaderContainer.tsx @@ -3,8 +3,9 @@ import Header from "@/components/common/Header"; import useAuthStore from "@/store/authStore"; import { userResponseDto } from "@/types/UserDto"; +import { fetchWithAuth } from "@/utils/fetchWithAuth"; import { usePathname } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useEffect, useLayoutEffect, useState } from "react"; const HeaderContainer = () => { const pathname = usePathname(); @@ -40,16 +41,17 @@ const HeaderContainer = () => { }; }, []); - useEffect(() => { + useLayoutEffect(() => { + // 자동 로그인 const login = async () => { - const user = await fetch("/api/auth/user", { - credentials: "include", - }); - user.json().then((res: userResponseDto) => { - authStore.setUser(res); - }); + const data = await fetchWithAuth("/api/auth/user"); + if (data.status == 200) { + data.json().then((res: userResponseDto) => { + authStore.setUser(res); + }); + } }; - login(); + login(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); From f0a898e8d04b6c1ac0b0f28f5d0f232bca8011d4 Mon Sep 17 00:00:00 2001 From: "SK\\ssssk" Date: Tue, 16 Jul 2024 10:19:26 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=EC=B9=B4=EC=B9=B4=EC=98=A4=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/containers/auth/AuthKaKaoContainer.tsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/containers/auth/AuthKaKaoContainer.tsx b/src/containers/auth/AuthKaKaoContainer.tsx index 4ad138ea..f68030ac 100644 --- a/src/containers/auth/AuthKaKaoContainer.tsx +++ b/src/containers/auth/AuthKaKaoContainer.tsx @@ -23,22 +23,26 @@ const AuthKaKaoContainer = () => { { method: "GET", headers: { - "Content-Type": "application/json;charset=utf-8", + "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", }, credentials: "include", }, ); // 액세스 토큰을 이용해서 사용자 정보 조회 - const user = await fetch("/api/auth/user", { - credentials: "include", - }); - user.json().then((res: userResponseDto) => { - authStore.setUser(res); - }) - router.push("/"); + const data = await fetch("/api/auth/user"); + if (data.status == 200) { + data.json().then((res: userResponseDto) => { + authStore.setUser(res); + }); + router.push("/"); + } + else { + throw ""; + } } catch (error) { console.error("로그인 실패", error); + alert("로그인에 실패했습니다.") router.push("/auth/signin"); } }; From 3c8f1fb6c92580414d89ae24c3bb7d1b381d12f3 Mon Sep 17 00:00:00 2001 From: "SK\\ssssk" Date: Tue, 16 Jul 2024 10:19:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/auth/user/route.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/app/api/auth/user/route.ts b/src/app/api/auth/user/route.ts index d36d662e..0de54dad 100644 --- a/src/app/api/auth/user/route.ts +++ b/src/app/api/auth/user/route.ts @@ -2,15 +2,19 @@ import { NextRequest, NextResponse } from "next/server"; export async function GET(request: NextRequest) { try { - const cookie = request.cookies.get("access_token"); - if (!cookie) { + const access_cookie = request.cookies.get("access_token"); + if (!access_cookie) { + const refresh_cookie = request.cookies.get("refresh_token"); + if (!refresh_cookie) { + return new NextResponse("불필요한 요청", { status: 400 }); + } return new NextResponse("Access token not found", { status: 401 }); } - - const response = await fetch(`${process.env.BACKEND_URL}/api/user/info`, { + // 사용자 정보 조회 API + const response = await fetch(`${process.env.BACKEND_URL}/api/users/info`, { method: "GET", headers: { - Cookie: `${cookie?.name}=${cookie?.value}`, + Cookie: `${access_cookie?.name}=${access_cookie?.value}`, "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", }, @@ -22,7 +26,6 @@ export async function GET(request: NextRequest) { headers: { "Content-Type": "application/json" }, }); } catch (error) { - console.error(error); return new NextResponse("Internal Server Error", { status: 500 }); } } From 243f5288f16654758aaa0047bba7b6be0b0759eb Mon Sep 17 00:00:00 2001 From: "SK\\ssssk" Date: Tue, 16 Jul 2024 10:19:43 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=EC=95=A1=EC=84=B8=EC=8A=A4=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/auth/refresh-access-token/route.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/app/api/auth/refresh-access-token/route.ts diff --git a/src/app/api/auth/refresh-access-token/route.ts b/src/app/api/auth/refresh-access-token/route.ts new file mode 100644 index 00000000..e4afc212 --- /dev/null +++ b/src/app/api/auth/refresh-access-token/route.ts @@ -0,0 +1,35 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(request: NextRequest) { + try { + const cookie = request.cookies.get("refresh_token"); + if (!cookie) { + return new NextResponse("Access token not found", { status: 401 }); + } + // 액세스 토큰 재요청 + const backendResponse = await fetch( + `${process.env.BACKEND_URL}/api/auth/oauth2/token/refresh`, + { + method: "POST", + headers: { + Cookie: `${cookie?.name}=${cookie?.value}`, + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + }, + credentials: "include", + }, + ) + const result = new NextResponse(backendResponse.status == 200 ? "성공" : "실패", { + status: backendResponse.status, + headers: { "Content-Type": "application/json" }, + }); + if (backendResponse.status == 200) { + const accessToken = backendResponse.headers.get("set-cookie"); + result.headers.set("set-cookie", accessToken as string); + } + return result; + } catch (error) { + console.error(error); + return new NextResponse("살려줘" , { status: 500 }); + } +}