From f2f772ee4ab77f5a325073ec67bf43610495b6e7 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Tue, 14 Jan 2025 22:06:11 +0900
Subject: [PATCH 01/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EA=B5=AC=EC=A1=B0=EC=97=90=20=EB=A7=9E?=
 =?UTF-8?q?=EA=B2=8C=20=EC=9A=94=EC=B2=AD=20=EA=B0=9C=EC=84=A0=20#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/chat.ts             | 5 ++++-
 src/pages/ChatPage/index.tsx | 8 +++++++-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/apis/chat.ts b/src/apis/chat.ts
index 9017b54e..b976c784 100644
--- a/src/apis/chat.ts
+++ b/src/apis/chat.ts
@@ -8,8 +8,11 @@ import { authInstance } from '.';
 // 메시지 기록 조회
 export const getMessage = async (
   roomId: number,
+  date: Date,
 ): Promise<ChatMessageResponse[]> => {
-  const response = await authInstance.get(`/api/v1/chat/room/${roomId}`);
+  const response = await authInstance.get(`/api/v1/chat/room/${roomId}`, {
+    params: { cursor: date },
+  });
   return response.data;
 };
 
diff --git a/src/pages/ChatPage/index.tsx b/src/pages/ChatPage/index.tsx
index 23218c0b..2d80836f 100644
--- a/src/pages/ChatPage/index.tsx
+++ b/src/pages/ChatPage/index.tsx
@@ -43,7 +43,7 @@ const ChatPage = () => {
 
   // 채팅 내용 불러오기
   const loadMessage = async () => {
-    const messageList = await getMessage(Number(roomId));
+    const messageList = await getMessage(Number(roomId), new Date());
     setMessages(messageList);
   };
 
@@ -54,6 +54,9 @@ const ChatPage = () => {
         brokerURL: WS_URL,
         webSocketFactory: () => new SockJS(WS_URL),
         reconnectDelay: 5000, // 자동 재연결
+        connectHeaders: {
+          chatRoomId: roomId as string, // 헤더에 chatRoomId 추가
+        },
       });
 
       // 구독
@@ -96,6 +99,9 @@ const ChatPage = () => {
       stompClient.publish({
         destination: '/pub/sendMessage',
         body: JSON.stringify(chatMessage),
+        headers: {
+          chatRoomId: roomId as string, // 헤더에 chatRoomId 추가
+        },
       });
     }
   };

From dc41c922c706c3bbbe53c57b6ba7ce2877d3830f Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Tue, 14 Jan 2025 22:11:47 +0900
Subject: [PATCH 02/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=EC=86=8C=EC=BC=93=20=ED=97=A4=EB=8D=94=EC=97=90=20=ED=95=84?=
 =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EA=B0=92=20=EC=B6=94=EA=B0=80=20#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/pages/ChatPage/index.tsx | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/pages/ChatPage/index.tsx b/src/pages/ChatPage/index.tsx
index 2d80836f..848b1b42 100644
--- a/src/pages/ChatPage/index.tsx
+++ b/src/pages/ChatPage/index.tsx
@@ -55,7 +55,9 @@ const ChatPage = () => {
         webSocketFactory: () => new SockJS(WS_URL),
         reconnectDelay: 5000, // 자동 재연결
         connectHeaders: {
-          chatRoomId: roomId as string, // 헤더에 chatRoomId 추가
+          nickName: user,
+          senderType: role === 'ROLE_USER' ? 'MEMBER' : 'BUSINESS',
+          chatRoomId: roomId as string,
         },
       });
 
@@ -100,7 +102,9 @@ const ChatPage = () => {
         destination: '/pub/sendMessage',
         body: JSON.stringify(chatMessage),
         headers: {
-          chatRoomId: roomId as string, // 헤더에 chatRoomId 추가
+          nickName: user,
+          senderType: role === 'ROLE_USER' ? 'MEMBER' : 'BUSINESS',
+          chatRoomId: roomId as string,
         },
       });
     }

From 294105d09e2827a8229af2cb41d432519d131217 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Tue, 14 Jan 2025 22:45:35 +0900
Subject: [PATCH 03/13] =?UTF-8?q?=F0=9F=92=84=20Design=20:=20=EA=B2=B0?=
 =?UTF-8?q?=EC=A0=9C=20=EB=B2=84=ED=8A=BC=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?=
 =?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../PaymentPage/components/PaymentButton.tsx   | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/pages/PaymentPage/components/PaymentButton.tsx b/src/pages/PaymentPage/components/PaymentButton.tsx
index a3b0134f..7702573a 100644
--- a/src/pages/PaymentPage/components/PaymentButton.tsx
+++ b/src/pages/PaymentPage/components/PaymentButton.tsx
@@ -163,14 +163,16 @@ const PaymentButton = (props: PaymentButtonProps) => {
   };
 
   return (
-    <div className='fixed bottom-0 z-10 flex h-[94px] w-[375px] items-center justify-between border-t-[1px] border-t-subfont bg-white px-[30px] pb-[30px] pt-[18px]'>
-      <button
-        type='button'
-        onClick={handlePaymentButton}
-        className='btn-primary'
-      >
-        {totalAmount.toLocaleString('ko-KR')}원 결제하기
-      </button>
+    <div className='fixed bottom-0 z-10 flex h-[94px] w-[375px] items-center justify-center border-t-[1px] border-t-subfont bg-white pb-[16px]'>
+      <div className='flex items-center gap-2'>
+        <button
+          type='button'
+          onClick={handlePaymentButton}
+          className='btn-primary w-custom px-1'
+        >
+          {totalAmount.toLocaleString('ko-KR')}원 결제하기
+        </button>
+      </div>
     </div>
   );
 };

From 11da7b9a576a8f89624f1a50d4a28868305156c5 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Wed, 15 Jan 2025 00:21:27 +0900
Subject: [PATCH 04/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=9A=94=EC=B2=AD=20params=20=EC=88=98?=
 =?UTF-8?q?=EC=A0=95=20=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81=20#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/index.ts | 72 +++++++++++++++++++++++------------------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/src/apis/index.ts b/src/apis/index.ts
index 4cfd8516..49729de1 100644
--- a/src/apis/index.ts
+++ b/src/apis/index.ts
@@ -1,16 +1,16 @@
 import axios, {
   AxiosError,
   AxiosInstance,
-  AxiosRequestConfig,
+  // AxiosRequestConfig,
   AxiosResponse,
   InternalAxiosRequestConfig,
 } from 'axios';
 import { BASE_URL } from '@constants/constants';
 import {
   getAuthToken,
-  removeAuthToken,
-  removeRole,
-  setAuthToken,
+  // removeAuthToken,
+  // removeRole,
+  // setAuthToken,
 } from '@utils/auth';
 import { toast } from 'react-toastify';
 
@@ -44,40 +44,40 @@ const authInstance: AxiosInstance = axios.create({
 */
 
 // response interceptor (토큰 갱신)
-authInstance.interceptors.response.use(
-  (response: AxiosResponse) => response,
-  // 에러 처리 함수
-  async (error: AxiosError) => {
-    // 401 Unauthorized 에러 시 token 갱신하기
-    if (error.response && error.response.status === 401) {
-      try {
-        const response = await defaultInstance.post('/reissue');
+// authInstance.interceptors.response.use(
+//   (response: AxiosResponse) => response,
+//   // 에러 처리 함수
+//   async (error: AxiosError) => {
+//     // 401 Unauthorized 에러 시 token 갱신하기
+//     if (error.response && error.response.status === 401) {
+//       try {
+//         const response = await defaultInstance.post('/reissue');
 
-        // reissue 요청 성공 시
-        if (response.status === 200) {
-          const token = response.headers.authorization;
-          setAuthToken(token);
+//         // reissue 요청 성공 시
+//         if (response.status === 200) {
+//           const token = response.headers.authorization;
+//           setAuthToken(token);
 
-          const originalRequest = error.config as AxiosRequestConfig;
-          if (originalRequest.headers) {
-            originalRequest.headers.Authorization = `Bearer ${token}`;
-          }
-          return await authInstance(originalRequest); // 실패했던 요청 재시도
-        }
-      } catch (refreshError) {
-        // reissue 요청 실패 시
-        const reissueError = refreshError as AxiosError;
-        if (reissueError.response?.status === 401) {
-          // 로그아웃
-          removeAuthToken();
-          removeRole();
-          window.location.replace('/start');
-        }
-      }
-    }
-    return Promise.reject(error);
-  },
-);
+//           const originalRequest = error.config as AxiosRequestConfig;
+//           if (originalRequest.headers) {
+//             originalRequest.headers.Authorization = `Bearer ${token}`;
+//           }
+//           return await authInstance(originalRequest); // 실패했던 요청 재시도
+//         }
+//       } catch (refreshError) {
+//         // reissue 요청 실패 시
+//         const reissueError = refreshError as AxiosError;
+//         if (reissueError.response?.status === 401) {
+//           // 로그아웃
+//           removeAuthToken();
+//           removeRole();
+//           window.location.replace('/start');
+//         }
+//       }
+//     }
+//     return Promise.reject(error);
+//   },
+// );
 
 // request interceptor
 authInstance.interceptors.request.use(

From 35cef9166ac768df212eb42ad69c83a3aae4bbbd Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Wed, 15 Jan 2025 00:23:02 +0900
Subject: [PATCH 05/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=ED=97=A4=EB=8D=94=EC=97=90=20=EB=8B=89=EB=84=A4=EC=9E=84=20?=
 =?UTF-8?q?=EB=84=98=EA=B2=A8=EC=A3=BC=EA=B8=B0=20=EA=B0=9C=EC=84=A0=20#16?=
 =?UTF-8?q?3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/pages/ChatPage/index.tsx | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/pages/ChatPage/index.tsx b/src/pages/ChatPage/index.tsx
index 848b1b42..c62c4bce 100644
--- a/src/pages/ChatPage/index.tsx
+++ b/src/pages/ChatPage/index.tsx
@@ -113,11 +113,13 @@ const ChatPage = () => {
   useEffect(() => {
     getUserNickName();
     loadMessage();
-    connect();
+    if (user !== '') {
+      connect();
+    }
 
     return () => disConnect();
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, []);
+  }, [user]);
 
   return (
     <>

From aa450866fb741a2bce85e263bd0b944613109ef2 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Wed, 15 Jan 2025 10:26:00 +0900
Subject: [PATCH 06/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EC=9A=94=EC=B2=AD=EC=97=90=20params=20?=
 =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81=20?=
 =?UTF-8?q?#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/chat.ts | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/apis/chat.ts b/src/apis/chat.ts
index b976c784..6a6022ee 100644
--- a/src/apis/chat.ts
+++ b/src/apis/chat.ts
@@ -17,14 +17,22 @@ export const getMessage = async (
 };
 
 // 채팅방 목록 조회 (사용자)
-export const getChatListMember = async (): Promise<ChatListMember[]> => {
-  const response = await authInstance.get('/api/v1/chat/room');
+export const getChatListMember = async (
+  date: Date,
+): Promise<ChatListMember[]> => {
+  const response = await authInstance.get('/api/v1/chat/room', {
+    params: { cursor: date },
+  });
   return response.data;
 };
 
 // 채팅방 목록 조회 (사업자)
-export const getChatListBusiness = async (): Promise<ChatListBusiness[]> => {
-  const response = await authInstance.get('/api/v1/chat/room');
+export const getChatListBusiness = async (
+  date: Date,
+): Promise<ChatListBusiness[]> => {
+  const response = await authInstance.get('/api/v1/chat/room', {
+    params: { cursor: date },
+  });
   return response.data;
 };
 

From f20792b5d51554f3b54ddf2bf8561f60027002ff Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Wed, 15 Jan 2025 10:27:45 +0900
Subject: [PATCH 07/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=20=EC=8B=9C=20=ED=99=94?=
 =?UTF-8?q?=EB=A9=B4=20=EC=A0=84=ED=99=98=20=EA=B0=9C=EC=84=A0=20=EC=A4=91?=
 =?UTF-8?q?=20#164?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/TokenRefresher.tsx | 73 +++++++++++++++++++++++++++++++
 src/pages/UserMypage/index.tsx    | 19 ++++----
 2 files changed, 84 insertions(+), 8 deletions(-)
 create mode 100644 src/components/TokenRefresher.tsx

diff --git a/src/components/TokenRefresher.tsx b/src/components/TokenRefresher.tsx
new file mode 100644
index 00000000..78872a7c
--- /dev/null
+++ b/src/components/TokenRefresher.tsx
@@ -0,0 +1,73 @@
+import { authInstance, defaultInstance } from '@apis/index';
+import { removeAuthToken, removeRole, setAuthToken } from '@utils/auth';
+import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
+import { useEffect, useState } from 'react';
+import { SyncLoader } from 'react-spinners';
+
+const TokenRefresher = ({ children }: { children: React.ReactNode }) => {
+  const [isRefreshing, setIsRefreshing] = useState(true);
+
+  const handleLogout = () => {
+    removeAuthToken();
+    removeRole();
+    window.location.replace('/start');
+  };
+
+  useEffect(() => {
+    const interceptor = authInstance.interceptors.response.use(
+      (response: AxiosResponse) => {
+        setIsRefreshing(false);
+        return response;
+      },
+      // 에러 처리
+      async (error: AxiosError) => {
+        if (error.response && error.response.status === 401) {
+          setIsRefreshing(true);
+          try {
+            const res = await defaultInstance.post('/reissue');
+
+            if (res.status === 200) {
+              const token = res.headers.authorization;
+              setAuthToken(token);
+
+              const originalRequest = error.config as AxiosRequestConfig;
+              if (originalRequest.headers) {
+                originalRequest.headers.Authorization = `Bearer ${token}`;
+              }
+              // 실패했던 요청 재시도
+              setIsRefreshing(false);
+              return authInstance(originalRequest);
+            }
+          } catch (refreshError) {
+            // 토큰 갱신 실패 시 로그아웃 처리
+            const reissueError = refreshError as AxiosError;
+            if (reissueError.response?.status === 401) {
+              // 로그아웃
+              handleLogout();
+            }
+          }
+        }
+        setIsRefreshing(true);
+        return Promise.reject(error);
+      },
+    );
+    setIsRefreshing(false);
+    return () => {
+      authInstance.interceptors.response.eject(interceptor); // 인터셉터 해제
+    };
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+  if (isRefreshing) {
+    return (
+      <div className='flex h-[300px] w-full items-center justify-center'>
+        <SyncLoader color='#50BEAD' />
+      </div>
+    );
+  }
+
+  return <>{children}</>;
+};
+
+export default TokenRefresher;
diff --git a/src/pages/UserMypage/index.tsx b/src/pages/UserMypage/index.tsx
index 974234e7..b5e1475b 100644
--- a/src/pages/UserMypage/index.tsx
+++ b/src/pages/UserMypage/index.tsx
@@ -1,20 +1,23 @@
 import HeaderNoTitle from '@layouts/HeaderNoTitle';
 import MainLayout from '@layouts/MainLayout';
 import BottomNavigation from '@layouts/BottomNavigation';
+import TokenRefresher from '@components/TokenRefresher';
 import UserButtonContainer from './components/UserButtonContainer';
 import UserInfo from './components/UserInfo';
 
 const UserMypage = () => {
   return (
     <>
-      <MainLayout>
-        <HeaderNoTitle />
-        <div className='flex h-[263px] w-[375px] flex-col items-center bg-primary'>
-          <UserInfo />
-          <UserButtonContainer />
-        </div>
-        <BottomNavigation />
-      </MainLayout>
+      <TokenRefresher>
+        <MainLayout>
+          <HeaderNoTitle />
+          <div className='flex h-[263px] w-[375px] flex-col items-center bg-primary'>
+            <UserInfo />
+            <UserButtonContainer />
+          </div>
+          <BottomNavigation />
+        </MainLayout>
+      </TokenRefresher>
     </>
   );
 };

From 7fa15d94f5548134524595ca091191ec23d53161 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Thu, 16 Jan 2025 14:35:10 +0900
Subject: [PATCH 08/13] =?UTF-8?q?=F0=9F=94=A7=20Fix=20:=20=EC=B1=84?=
 =?UTF-8?q?=ED=8C=85=20=EC=9A=94=EC=B2=AD=20cursor=20=EA=B0=92=20=EC=9E=98?=
 =?UTF-8?q?=EB=AA=BB=20=EB=84=98=EA=B2=A8=EC=A3=BC=EB=8A=94=20=EA=B2=83=20?=
 =?UTF-8?q?=EA=B0=9C=EC=84=A0=20#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/chat.ts             | 15 +++++----------
 src/pages/ChatPage/index.tsx |  2 +-
 2 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/src/apis/chat.ts b/src/apis/chat.ts
index 6a6022ee..0e6f98db 100644
--- a/src/apis/chat.ts
+++ b/src/apis/chat.ts
@@ -8,30 +8,25 @@ import { authInstance } from '.';
 // 메시지 기록 조회
 export const getMessage = async (
   roomId: number,
-  date: Date,
 ): Promise<ChatMessageResponse[]> => {
   const response = await authInstance.get(`/api/v1/chat/room/${roomId}`, {
-    params: { cursor: date },
+    params: { cursor: new Date().toISOString() },
   });
   return response.data;
 };
 
 // 채팅방 목록 조회 (사용자)
-export const getChatListMember = async (
-  date: Date,
-): Promise<ChatListMember[]> => {
+export const getChatListMember = async (): Promise<ChatListMember[]> => {
   const response = await authInstance.get('/api/v1/chat/room', {
-    params: { cursor: date },
+    params: { cursor: new Date().toISOString() },
   });
   return response.data;
 };
 
 // 채팅방 목록 조회 (사업자)
-export const getChatListBusiness = async (
-  date: Date,
-): Promise<ChatListBusiness[]> => {
+export const getChatListBusiness = async (): Promise<ChatListBusiness[]> => {
   const response = await authInstance.get('/api/v1/chat/room', {
-    params: { cursor: date },
+    params: { cursor: new Date().toISOString() },
   });
   return response.data;
 };
diff --git a/src/pages/ChatPage/index.tsx b/src/pages/ChatPage/index.tsx
index c62c4bce..fefc35ab 100644
--- a/src/pages/ChatPage/index.tsx
+++ b/src/pages/ChatPage/index.tsx
@@ -43,7 +43,7 @@ const ChatPage = () => {
 
   // 채팅 내용 불러오기
   const loadMessage = async () => {
-    const messageList = await getMessage(Number(roomId), new Date());
+    const messageList = await getMessage(Number(roomId));
     setMessages(messageList);
   };
 

From ced8b406d846ff24524f48f3bf027e71b5652080 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Thu, 16 Jan 2025 14:55:05 +0900
Subject: [PATCH 09/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20S3=20?=
 =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AD=EC=A0=9C=20=EA=B0=9C?=
 =?UTF-8?q?=EC=84=A0=20#151?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/workplace.ts                           | 10 ++++++++++
 src/pages/ModifySpace/components/RoomModify.tsx |  7 ++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/src/apis/workplace.ts b/src/apis/workplace.ts
index d24caab7..dbbb61c3 100644
--- a/src/apis/workplace.ts
+++ b/src/apis/workplace.ts
@@ -51,6 +51,16 @@ export const deleteStudyRoom = async (studyRoomId: string): Promise<void> => {
   return authInstance.delete(`/api/v1/studyroom/${studyRoomId}`);
 };
 
+// 스터디룸 이미지 삭제
+export const deleteStudyRoomImage = async (
+  fileName: string,
+  fileLocation: string,
+): Promise<void> => {
+  await authInstance.delete(`/api/delete-object`, {
+    params: { fileName, fileLocation },
+  });
+};
+
 // 사업장의 스터디룸 찾기
 export const getWorkplaceStudyRoom = async (
   workplaceId: number,
diff --git a/src/pages/ModifySpace/components/RoomModify.tsx b/src/pages/ModifySpace/components/RoomModify.tsx
index 6344ef67..78c1f3da 100644
--- a/src/pages/ModifySpace/components/RoomModify.tsx
+++ b/src/pages/ModifySpace/components/RoomModify.tsx
@@ -6,7 +6,7 @@ import CountPeople from '@components/CountPeople';
 import { ERROR_MESSAGE } from '@constants/constants';
 import { validate } from 'uuid';
 import { useParams } from 'react-router-dom';
-import { getS3URL } from '@apis/workplace';
+import { deleteStudyRoomImage, getS3URL } from '@apis/workplace';
 import axios from 'axios';
 import useGetRoomListInfo from '../hooks/useGetRoomListInfo';
 import usePostRoom from '../hooks/usePostRoom';
@@ -155,6 +155,11 @@ const RoomModify = ({ room, updateRoomData, completeAdd }: RoomModifyProps) => {
         // 삭제된 이미지가 있을 경우
         if (deletedImage) {
           // 룸 사진 삭제
+          deletedImage.forEach((img) => {
+            const fileName = img;
+            const fileLocation = `workplace-${workplaceId}/studyroom-${room.id}`;
+            deleteStudyRoomImage(fileName, fileLocation);
+          });
         }
       }
 

From fab9227094a9282112362a795c5bd4cfa7e709f9 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Thu, 16 Jan 2025 14:57:00 +0900
Subject: [PATCH 10/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=20=EA=B8=B0=EC=A1=B4=20?=
 =?UTF-8?q?=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=EC=97=90=EC=84=9C=20?=
 =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?=
 =?UTF-8?q?=EC=A0=95=20#164?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/index.ts              | 72 +++++++++++++++++-----------------
 src/pages/UserMypage/index.tsx | 19 ++++-----
 2 files changed, 44 insertions(+), 47 deletions(-)

diff --git a/src/apis/index.ts b/src/apis/index.ts
index 49729de1..4cfd8516 100644
--- a/src/apis/index.ts
+++ b/src/apis/index.ts
@@ -1,16 +1,16 @@
 import axios, {
   AxiosError,
   AxiosInstance,
-  // AxiosRequestConfig,
+  AxiosRequestConfig,
   AxiosResponse,
   InternalAxiosRequestConfig,
 } from 'axios';
 import { BASE_URL } from '@constants/constants';
 import {
   getAuthToken,
-  // removeAuthToken,
-  // removeRole,
-  // setAuthToken,
+  removeAuthToken,
+  removeRole,
+  setAuthToken,
 } from '@utils/auth';
 import { toast } from 'react-toastify';
 
@@ -44,40 +44,40 @@ const authInstance: AxiosInstance = axios.create({
 */
 
 // response interceptor (토큰 갱신)
-// authInstance.interceptors.response.use(
-//   (response: AxiosResponse) => response,
-//   // 에러 처리 함수
-//   async (error: AxiosError) => {
-//     // 401 Unauthorized 에러 시 token 갱신하기
-//     if (error.response && error.response.status === 401) {
-//       try {
-//         const response = await defaultInstance.post('/reissue');
+authInstance.interceptors.response.use(
+  (response: AxiosResponse) => response,
+  // 에러 처리 함수
+  async (error: AxiosError) => {
+    // 401 Unauthorized 에러 시 token 갱신하기
+    if (error.response && error.response.status === 401) {
+      try {
+        const response = await defaultInstance.post('/reissue');
 
-//         // reissue 요청 성공 시
-//         if (response.status === 200) {
-//           const token = response.headers.authorization;
-//           setAuthToken(token);
+        // reissue 요청 성공 시
+        if (response.status === 200) {
+          const token = response.headers.authorization;
+          setAuthToken(token);
 
-//           const originalRequest = error.config as AxiosRequestConfig;
-//           if (originalRequest.headers) {
-//             originalRequest.headers.Authorization = `Bearer ${token}`;
-//           }
-//           return await authInstance(originalRequest); // 실패했던 요청 재시도
-//         }
-//       } catch (refreshError) {
-//         // reissue 요청 실패 시
-//         const reissueError = refreshError as AxiosError;
-//         if (reissueError.response?.status === 401) {
-//           // 로그아웃
-//           removeAuthToken();
-//           removeRole();
-//           window.location.replace('/start');
-//         }
-//       }
-//     }
-//     return Promise.reject(error);
-//   },
-// );
+          const originalRequest = error.config as AxiosRequestConfig;
+          if (originalRequest.headers) {
+            originalRequest.headers.Authorization = `Bearer ${token}`;
+          }
+          return await authInstance(originalRequest); // 실패했던 요청 재시도
+        }
+      } catch (refreshError) {
+        // reissue 요청 실패 시
+        const reissueError = refreshError as AxiosError;
+        if (reissueError.response?.status === 401) {
+          // 로그아웃
+          removeAuthToken();
+          removeRole();
+          window.location.replace('/start');
+        }
+      }
+    }
+    return Promise.reject(error);
+  },
+);
 
 // request interceptor
 authInstance.interceptors.request.use(
diff --git a/src/pages/UserMypage/index.tsx b/src/pages/UserMypage/index.tsx
index b5e1475b..974234e7 100644
--- a/src/pages/UserMypage/index.tsx
+++ b/src/pages/UserMypage/index.tsx
@@ -1,23 +1,20 @@
 import HeaderNoTitle from '@layouts/HeaderNoTitle';
 import MainLayout from '@layouts/MainLayout';
 import BottomNavigation from '@layouts/BottomNavigation';
-import TokenRefresher from '@components/TokenRefresher';
 import UserButtonContainer from './components/UserButtonContainer';
 import UserInfo from './components/UserInfo';
 
 const UserMypage = () => {
   return (
     <>
-      <TokenRefresher>
-        <MainLayout>
-          <HeaderNoTitle />
-          <div className='flex h-[263px] w-[375px] flex-col items-center bg-primary'>
-            <UserInfo />
-            <UserButtonContainer />
-          </div>
-          <BottomNavigation />
-        </MainLayout>
-      </TokenRefresher>
+      <MainLayout>
+        <HeaderNoTitle />
+        <div className='flex h-[263px] w-[375px] flex-col items-center bg-primary'>
+          <UserInfo />
+          <UserButtonContainer />
+        </div>
+        <BottomNavigation />
+      </MainLayout>
     </>
   );
 };

From aa228979b8f74d04e8f953419add8fed953842c8 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Thu, 16 Jan 2025 15:30:02 +0900
Subject: [PATCH 11/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20curso?=
 =?UTF-8?q?r=20=EA=B0=92=20=ED=95=9C=EA=B5=AD=20=EC=8B=9C=EA=B0=84?=
 =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B0=9C=EC=84=A0=20#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/chat.ts             | 11 ++++-------
 src/pages/ChatPage/index.tsx |  2 +-
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/apis/chat.ts b/src/apis/chat.ts
index 0e6f98db..7c4c1f37 100644
--- a/src/apis/chat.ts
+++ b/src/apis/chat.ts
@@ -9,25 +9,22 @@ import { authInstance } from '.';
 export const getMessage = async (
   roomId: number,
 ): Promise<ChatMessageResponse[]> => {
+  const cursor = new Date(Date.now() + 9 * 60 * 60 * 1000);
   const response = await authInstance.get(`/api/v1/chat/room/${roomId}`, {
-    params: { cursor: new Date().toISOString() },
+    params: { cursor },
   });
   return response.data;
 };
 
 // 채팅방 목록 조회 (사용자)
 export const getChatListMember = async (): Promise<ChatListMember[]> => {
-  const response = await authInstance.get('/api/v1/chat/room', {
-    params: { cursor: new Date().toISOString() },
-  });
+  const response = await authInstance.get('/api/v1/chat/room');
   return response.data;
 };
 
 // 채팅방 목록 조회 (사업자)
 export const getChatListBusiness = async (): Promise<ChatListBusiness[]> => {
-  const response = await authInstance.get('/api/v1/chat/room', {
-    params: { cursor: new Date().toISOString() },
-  });
+  const response = await authInstance.get('/api/v1/chat/room');
   return response.data;
 };
 
diff --git a/src/pages/ChatPage/index.tsx b/src/pages/ChatPage/index.tsx
index fefc35ab..cc429b42 100644
--- a/src/pages/ChatPage/index.tsx
+++ b/src/pages/ChatPage/index.tsx
@@ -112,8 +112,8 @@ const ChatPage = () => {
 
   useEffect(() => {
     getUserNickName();
-    loadMessage();
     if (user !== '') {
+      loadMessage();
       connect();
     }
 

From 50c0231d608edf859474872436dc9dc5d6788134 Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Thu, 16 Jan 2025 16:04:48 +0900
Subject: [PATCH 12/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=EB=A9=94=EC=9D=B8=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C?=
 =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=20=ED=99=95=EC=9D=B8?=
 =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EA=B0=9C=EC=84=A0=20#164?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/TokenRefresher.tsx | 73 -------------------------------
 src/pages/MainPage/index.tsx      | 21 ++++++++-
 src/utils/auth.ts                 | 18 ++++++--
 3 files changed, 34 insertions(+), 78 deletions(-)
 delete mode 100644 src/components/TokenRefresher.tsx

diff --git a/src/components/TokenRefresher.tsx b/src/components/TokenRefresher.tsx
deleted file mode 100644
index 78872a7c..00000000
--- a/src/components/TokenRefresher.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { authInstance, defaultInstance } from '@apis/index';
-import { removeAuthToken, removeRole, setAuthToken } from '@utils/auth';
-import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
-import { useEffect, useState } from 'react';
-import { SyncLoader } from 'react-spinners';
-
-const TokenRefresher = ({ children }: { children: React.ReactNode }) => {
-  const [isRefreshing, setIsRefreshing] = useState(true);
-
-  const handleLogout = () => {
-    removeAuthToken();
-    removeRole();
-    window.location.replace('/start');
-  };
-
-  useEffect(() => {
-    const interceptor = authInstance.interceptors.response.use(
-      (response: AxiosResponse) => {
-        setIsRefreshing(false);
-        return response;
-      },
-      // 에러 처리
-      async (error: AxiosError) => {
-        if (error.response && error.response.status === 401) {
-          setIsRefreshing(true);
-          try {
-            const res = await defaultInstance.post('/reissue');
-
-            if (res.status === 200) {
-              const token = res.headers.authorization;
-              setAuthToken(token);
-
-              const originalRequest = error.config as AxiosRequestConfig;
-              if (originalRequest.headers) {
-                originalRequest.headers.Authorization = `Bearer ${token}`;
-              }
-              // 실패했던 요청 재시도
-              setIsRefreshing(false);
-              return authInstance(originalRequest);
-            }
-          } catch (refreshError) {
-            // 토큰 갱신 실패 시 로그아웃 처리
-            const reissueError = refreshError as AxiosError;
-            if (reissueError.response?.status === 401) {
-              // 로그아웃
-              handleLogout();
-            }
-          }
-        }
-        setIsRefreshing(true);
-        return Promise.reject(error);
-      },
-    );
-    setIsRefreshing(false);
-    return () => {
-      authInstance.interceptors.response.eject(interceptor); // 인터셉터 해제
-    };
-
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, []);
-
-  if (isRefreshing) {
-    return (
-      <div className='flex h-[300px] w-full items-center justify-center'>
-        <SyncLoader color='#50BEAD' />
-      </div>
-    );
-  }
-
-  return <>{children}</>;
-};
-
-export default TokenRefresher;
diff --git a/src/pages/MainPage/index.tsx b/src/pages/MainPage/index.tsx
index a56e329c..b6159bfe 100644
--- a/src/pages/MainPage/index.tsx
+++ b/src/pages/MainPage/index.tsx
@@ -4,7 +4,12 @@ import BottomNavigation from '@layouts/BottomNavigation';
 import usePositionStore from '@store/positionStore';
 import useAuthStore from '@store/authStore';
 import { useEffect, useState } from 'react';
-import { getRole } from '@utils/auth';
+import {
+  getRole,
+  getTokenExpiration,
+  removeAuthToken,
+  removeRole,
+} from '@utils/auth';
 import MainList from './components/MainList';
 import KakaoMap from './components/KakaoMap';
 import {
@@ -28,9 +33,21 @@ const MainPage = () => {
   );
 
   // 비로그인 / 사업자 / 사용자 확인
-  const { isLogin } = useAuthStore();
+  const { isLogin, storeLogout } = useAuthStore();
   const [isUser, setIsUser] = useState<boolean>(false);
 
+  // 토큰 만료 확인
+  useEffect(() => {
+    const isExpiration = getTokenExpiration();
+    if (isExpiration && isExpiration < new Date().getTime()) {
+      removeAuthToken();
+      removeRole();
+      storeLogout();
+    }
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
   useEffect(() => {
     if (isLogin) {
       const role = getRole();
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
index 27b8aacd..bad8f05d 100644
--- a/src/utils/auth.ts
+++ b/src/utils/auth.ts
@@ -1,10 +1,21 @@
 const setAuthToken = (accessToken: string) => {
-  localStorage.setItem('accessToken', accessToken);
+  const expiration = new Date().getTime() + 5 * 60 * 1000; // 만료시간 5분
+  localStorage.setItem(
+    'accessToken',
+    JSON.stringify({ accessToken, expiration }),
+  );
 };
 
 const getAuthToken = () => {
-  const token = localStorage.getItem('accessToken');
-  return token;
+  const tokenData = localStorage.getItem('accessToken');
+  const accessToken = tokenData ? JSON.parse(tokenData).accessToken : '';
+  return accessToken;
+};
+
+const getTokenExpiration = () => {
+  const tokenData = localStorage.getItem('accessToken');
+  const expiration = tokenData ? JSON.parse(tokenData).expiration : '';
+  return expiration;
 };
 
 const removeAuthToken = () => {
@@ -29,6 +40,7 @@ const removeRole = () => {
 export {
   setAuthToken,
   getAuthToken,
+  getTokenExpiration,
   removeAuthToken,
   setRole,
   getRole,

From c0d69d335e94b24f68eda115083beb33c5ca0e2f Mon Sep 17 00:00:00 2001
From: eunjju2 <graceun309@gmail.com>
Date: Thu, 16 Jan 2025 17:51:07 +0900
Subject: [PATCH 13/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20?=
 =?UTF-8?q?=EC=B1=84=ED=8C=85=20cursor=20=EA=B0=92=20=EC=88=98=EC=A0=95=20?=
 =?UTF-8?q?=EB=B0=8F=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4?=
 =?UTF-8?q?=EC=85=98=201=EC=B0=A8=20=EB=B0=98=EC=98=81=20#163?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/apis/chat.ts             |  5 +++--
 src/pages/ChatPage/index.tsx | 28 ++++++++++++++++++++++++----
 2 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/src/apis/chat.ts b/src/apis/chat.ts
index 7c4c1f37..eb984308 100644
--- a/src/apis/chat.ts
+++ b/src/apis/chat.ts
@@ -8,10 +8,11 @@ import { authInstance } from '.';
 // 메시지 기록 조회
 export const getMessage = async (
   roomId: number,
+  cursor?: string,
 ): Promise<ChatMessageResponse[]> => {
-  const cursor = new Date(Date.now() + 9 * 60 * 60 * 1000);
+  const now = new Date(Date.now() + 9 * 60 * 60 * 1000);
   const response = await authInstance.get(`/api/v1/chat/room/${roomId}`, {
-    params: { cursor },
+    params: { cursor: cursor || now },
   });
   return response.data;
 };
diff --git a/src/pages/ChatPage/index.tsx b/src/pages/ChatPage/index.tsx
index cc429b42..3613b6fb 100644
--- a/src/pages/ChatPage/index.tsx
+++ b/src/pages/ChatPage/index.tsx
@@ -42,9 +42,21 @@ const ChatPage = () => {
   };
 
   // 채팅 내용 불러오기
-  const loadMessage = async () => {
-    const messageList = await getMessage(Number(roomId));
-    setMessages(messageList);
+  const loadMessage = async (cursor?: string) => {
+    try {
+      const messageList = await getMessage(Number(roomId), cursor);
+      setMessages((prevMessages) => [...messageList, ...prevMessages]);
+    } catch (error) {
+      toast.error('메시지를 불러오는 중 오류가 발생했습니다.');
+    }
+  };
+
+  // 채팅 무한 스크롤
+  const loadMoreMessages = () => {
+    if (messages.length > 0) {
+      const lastMessageTimestamp = messages[0].timestamp; // 첫 번째 메시지의 타임스탬프 사용
+      loadMessage(lastMessageTimestamp);
+    }
   };
 
   // 소켓 연결
@@ -126,7 +138,15 @@ const ChatPage = () => {
       <MainLayout>
         <HeaderOnlyTitle title={chatTitle} />
         <div className='fixed left-1/2 top-[93px] flex h-[calc(100vh-93px-94px)] w-custom -translate-x-1/2 overflow-hidden'>
-          <div className='overflow-y-auto'>
+          <div
+            className='overflow-y-auto'
+            onScroll={(e) => {
+              const target = e.target as HTMLDivElement;
+              if (target.scrollTop === 0) {
+                loadMoreMessages();
+              }
+            }}
+          >
             <MessageContainer
               messages={messages}
               user={user}