Skip to content

Commit 813f140

Browse files
committed
[refactor]: Auth 토큰 로컬 스토리지에서 관리 및 갱신 기능 추가
1 parent e064fb6 commit 813f140

File tree

2 files changed

+89
-8
lines changed

2 files changed

+89
-8
lines changed

pages/api/articleApi.ts

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { FormInputInterface } from "@/types/addBoard";
22
import { ArticleInquiryInterface, PostCommentInterface } from "@/types/article";
3-
import { useRouter } from "next/router";
43

54
const fetchArticles = async ({
65
page = "1",
@@ -56,17 +55,94 @@ const fetchInquiryById = async (id: string, cursor: string | null = null) => {
5655
}
5756
};
5857

58+
const SetTokensToLocalStorage = async () => {
59+
try {
60+
const response = await fetch(
61+
`${process.env.NEXT_PUBLIC_SERVER_URL}/auth/signIn`,
62+
{
63+
method: "POST",
64+
headers: {
65+
"Content-Type": "application/json",
66+
},
67+
body: JSON.stringify({
68+
email: process.env.NEXT_PUBLIC_USER_EMAIL,
69+
password: process.env.NEXT_PUBLIC_USER_PASSWORD,
70+
}),
71+
}
72+
);
73+
74+
if (!response.ok) {
75+
throw new Error("로그인 실패: 서버에서 인증 정보를 확인하세요.");
76+
}
77+
78+
const { accessToken, refreshToken } = await response.json();
79+
80+
// localStorage에 토큰 저장
81+
localStorage.setItem("accessToken", accessToken);
82+
localStorage.setItem("refreshToken", refreshToken);
83+
84+
console.log("토큰이 성공적으로 설정되었습니다.");
85+
} catch (error) {
86+
console.error("초기 로그인 요청 실패:", error);
87+
throw error;
88+
}
89+
};
90+
91+
const retryFetch = async (
92+
url: string,
93+
options: RequestInit
94+
): Promise<Response> => {
95+
const response = await fetch(url, options);
96+
97+
if (response.status === 401) {
98+
// status === 401 : Unauthorized 토큰 갱신
99+
const refreshResponse = await fetch(
100+
`${process.env.NEXT_PUBLIC_SERVER_URL}/auth/refresh-token`,
101+
{
102+
method: "POST",
103+
headers: {
104+
"Content-Type": "application/json",
105+
},
106+
body: JSON.stringify({
107+
refreshToken: localStorage.getItem("refreshToken"),
108+
}),
109+
}
110+
);
111+
112+
if (refreshResponse.ok) {
113+
const { accessToken, refreshToken } = await refreshResponse.json();
114+
// localStorage에 갱신된 토큰 저장
115+
localStorage.setItem("accessToken", accessToken);
116+
localStorage.setItem("refreshToken", refreshToken);
117+
118+
// requset 재요청
119+
const newOptions = {
120+
...options,
121+
headers: {
122+
...options.headers,
123+
Authorization: `Bearer ${accessToken}`,
124+
},
125+
};
126+
return await fetch(url, newOptions);
127+
} else {
128+
throw new Error("토큰 갱신 실패. 다시 로그인하세요.");
129+
}
130+
}
131+
132+
return response;
133+
};
134+
59135
const uploadImage = async (file: string) => {
60136
const formData = new FormData();
61137
formData.append("image", file);
62138

63139
try {
64-
const response = await fetch(
140+
const response = await retryFetch(
65141
`${process.env.NEXT_PUBLIC_UPLOAD_IMAGE_URL}`,
66142
{
67143
method: "POST",
68144
headers: {
69-
Authorization: `Bearer ${process.env.NEXT_PUBLIC_API_TOKEN}`,
145+
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
70146
},
71147
body: formData,
72148
}
@@ -99,13 +175,13 @@ const postArticle = async ({ title, content, image }: FormInputInterface) => {
99175
};
100176

101177
try {
102-
const response = await fetch(
178+
const response = await retryFetch(
103179
`${process.env.NEXT_PUBLIC_SERVER_URL}/articles`,
104180
{
105181
method: "POST",
106182
headers: {
107183
"Content-Type": "application/json",
108-
Authorization: `Bearer ${process.env.NEXT_PUBLIC_API_TOKEN}`,
184+
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
109185
},
110186
body: JSON.stringify(data),
111187
}
@@ -128,13 +204,13 @@ const postArticleComment = async ({
128204
content,
129205
}: PostCommentInterface): Promise<ArticleInquiryInterface> => {
130206
try {
131-
const response = await fetch(
207+
const response = await retryFetch(
132208
`${process.env.NEXT_PUBLIC_SERVER_URL}/articles/${id}/comments`,
133209
{
134210
method: "POST",
135211
headers: {
136212
"Content-Type": "application/json",
137-
Authorization: `Bearer ${process.env.NEXT_PUBLIC_API_TOKEN}`,
213+
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
138214
},
139215
body: JSON.stringify({ content }),
140216
}
@@ -157,4 +233,5 @@ export {
157233
fetchInquiryById,
158234
postArticle,
159235
postArticleComment,
236+
SetTokensToLocalStorage,
160237
};

pages/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import Head from "next/head";
21
import Header from "@/components/common/Header";
32
import Link from "next/link";
43
import Footer from "@/components/common/Footer";
54
import Image from "next/image";
5+
import { useEffect } from "react";
6+
import { SetTokensToLocalStorage } from "./api/articleApi";
67

78
export default function Home() {
9+
useEffect(() => {
10+
SetTokensToLocalStorage();
11+
}, []);
812
return (
913
<>
1014
<Header />

0 commit comments

Comments
 (0)