From bee1bc9b0bbc9da46095b43cf4c2dce0c733ef22 Mon Sep 17 00:00:00 2001
From: cjeongmin <cjeongmin27@gmail.com>
Date: Thu, 16 May 2024 21:29:58 +0900
Subject: [PATCH] feat: Add axios interceptor setup (#83)

---
 src/app/layout.tsx   |  1 +
 src/app/lib/axios.ts | 66 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
 create mode 100644 src/app/lib/axios.ts

diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index fc4ca1d38d..109c3b843c 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -2,6 +2,7 @@ import type { Metadata } from 'next';
 import { Inter } from 'next/font/google';
 import React from 'react';
 import './globals.scss';
+import './lib/axios';
 
 import {
   AuthProvider,
diff --git a/src/app/lib/axios.ts b/src/app/lib/axios.ts
new file mode 100644
index 0000000000..0963a9548b
--- /dev/null
+++ b/src/app/lib/axios.ts
@@ -0,0 +1,66 @@
+'use client';
+
+import axios, {
+  type AxiosError,
+  type AxiosRequestConfig,
+  type AxiosResponse,
+} from 'axios';
+import { useRouter } from 'next/navigation';
+
+import { getUserData, postTokenRefresh, useAuthActions } from '@/features/auth';
+import { load } from '@/shared/storage';
+
+let isRefreshing = false;
+
+axios.interceptors.response.use(
+  (response: AxiosResponse) => response,
+  async (error: AxiosError) => {
+    const router = useRouter();
+    const { login, logout, setAuthUserData } = useAuthActions();
+
+    const { config, response } = error;
+    const originalRequest = config as AxiosRequestConfig;
+
+    const refreshToken = load<string>({
+      type: 'local',
+      key: 'refreshToken',
+    });
+
+    if (
+      response != null &&
+      refreshToken != null &&
+      response.status === 401 &&
+      !isRefreshing
+    ) {
+      isRefreshing = true;
+
+      return await new Promise((resolve, reject) => {
+        postTokenRefresh(refreshToken)
+          .then(({ data }) => {
+            if (originalRequest.headers != null)
+              originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
+
+            login(data);
+            getUserData()
+              .then(({ data: user }) => {
+                setAuthUserData(user);
+              })
+              .catch(err => {
+                console.error(err);
+              });
+
+            resolve(axios(originalRequest));
+          })
+          .catch(err => {
+            router.push('/');
+            logout();
+            reject(err);
+          })
+          .finally(() => {
+            isRefreshing = false;
+          });
+      });
+    }
+    return await Promise.reject(error);
+  },
+);