From b2aeefcb23b7799a74e577a2ce74de800ee051ae Mon Sep 17 00:00:00 2001 From: hakyoung12 Date: Thu, 17 Oct 2024 17:21:29 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[KAN-126]=20fix:=20=ED=83=AD=20=EB=B9=84?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=EC=8B=9C=20=ED=83=80=EC=9D=B4?= =?UTF-8?q?=EB=A8=B8=EC=99=80=20=EC=8B=A4=EC=A0=9C=20=EB=A7=8C=EB=A3=8C?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=B0=A8=EC=9D=B4=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Gnb/TokenExpirationTimerLayout.tsx | 7 ++-- src/utils/TokenExpirationTimer.ts | 35 +++++++------------ 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/app/components/Gnb/TokenExpirationTimerLayout.tsx b/src/app/components/Gnb/TokenExpirationTimerLayout.tsx index 9adc088e..333944a7 100644 --- a/src/app/components/Gnb/TokenExpirationTimerLayout.tsx +++ b/src/app/components/Gnb/TokenExpirationTimerLayout.tsx @@ -1,6 +1,7 @@ 'use client'; import { TokenExpirationTimer } from '@/utils/TokenExpirationTimer'; +import { useEffect, useState } from 'react'; const TimerStyle = { gnb: 'hidden text-14 font-semibold text-var-orange-50 md:block md:text-16 w-140', @@ -17,11 +18,7 @@ const TokenExpirationTimerLayout = ({ token, variant, }: TokenExpirationTimerLayoutProps) => { - const { timeLeft, isLoggedIn } = TokenExpirationTimer(token); - - if (!isLoggedIn) { - return null; - } + const { timeLeft } = TokenExpirationTimer(token); return timeLeft > 0 ? (

diff --git a/src/utils/TokenExpirationTimer.ts b/src/utils/TokenExpirationTimer.ts index 92c7b32c..43da4496 100644 --- a/src/utils/TokenExpirationTimer.ts +++ b/src/utils/TokenExpirationTimer.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useRouter } from 'next/navigation'; import { postUserLogoutData } from '@/app/api/actions/mypage/postUserLogoutData'; import toast from 'react-hot-toast'; @@ -6,8 +6,8 @@ import { deleteCookie } from '@/app/api/actions/cookie/cookie'; export const TokenExpirationTimer = (token: string | undefined) => { const router = useRouter(); - const [timeLeft, setTimeLeft] = useState(0); - const [isLoggedIn, setIsLoggedIn] = useState(false); + const timeLeftRef = useRef(0); // 남은 시간을 저장 + const [timeLeft, setTimeLeft] = useState(0); // 재렌더링을 위한 스테이트 저장 // JWT 디코드하여 만료 시간을 가져오는 함수 const getTokenExpirationTime = (token: string) => { @@ -17,29 +17,20 @@ export const TokenExpirationTimer = (token: string | undefined) => { useEffect(() => { if (token) { - setIsLoggedIn(true); - const expirationTime = getTokenExpirationTime(token); // 만료시간 - const currentTime = Date.now(); // 현재시간 + const expirationTime = getTokenExpirationTime(token); // 만료 시간 + const currentTime = Date.now(); // 현재 시간 const remainingTime = Math.max(0, expirationTime - currentTime); // 남은 시간 계산 - setTimeLeft(Math.floor(remainingTime / 1000)); // 초 단위로 변환 + timeLeftRef.current = Math.floor(remainingTime / 1000); // 초 단위로 변환 // 타이머 설정 const interval = setInterval(() => { - setTimeLeft((prev) => { - if (prev <= 1) { - clearInterval(interval); - logout(); - return 0; - } - return prev - 1; - }); + timeLeftRef.current -= 1; // 남은 시간 감소 + setTimeLeft(timeLeftRef.current); + if (timeLeftRef.current <= 0) { + logout(); + } }, 1000); - - return () => { - clearInterval(interval); - }; - } else { - setIsLoggedIn(false); + return () => clearInterval(interval); } }, [token]); @@ -55,5 +46,5 @@ export const TokenExpirationTimer = (token: string | undefined) => { } }; - return { timeLeft, isLoggedIn }; + return { timeLeft }; }; From de9e7b3db37b61c0cc27db636d53defc2ecbc9a9 Mon Sep 17 00:00:00 2001 From: hakyoung12 Date: Fri, 18 Oct 2024 13:34:57 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[KAN-126]=20fix:=20=ED=83=AD=20=EB=B9=84?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=EB=90=98=EC=97=88=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=EC=8B=A4=EC=A0=9C=20=EB=A7=8C=EB=A3=8C=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EA=B3=BC=20=ED=83=80=EC=9D=B4=EB=A8=B8=EC=9D=98=20?= =?UTF-8?q?=EC=98=A4=EC=B0=A8=20=EC=83=9D=EA=B8=B0=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/TokenExpirationTimer.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/utils/TokenExpirationTimer.ts b/src/utils/TokenExpirationTimer.ts index 43da4496..5d51bb66 100644 --- a/src/utils/TokenExpirationTimer.ts +++ b/src/utils/TokenExpirationTimer.ts @@ -6,7 +6,6 @@ import { deleteCookie } from '@/app/api/actions/cookie/cookie'; export const TokenExpirationTimer = (token: string | undefined) => { const router = useRouter(); - const timeLeftRef = useRef(0); // 남은 시간을 저장 const [timeLeft, setTimeLeft] = useState(0); // 재렌더링을 위한 스테이트 저장 // JWT 디코드하여 만료 시간을 가져오는 함수 @@ -18,18 +17,19 @@ export const TokenExpirationTimer = (token: string | undefined) => { useEffect(() => { if (token) { const expirationTime = getTokenExpirationTime(token); // 만료 시간 - const currentTime = Date.now(); // 현재 시간 - const remainingTime = Math.max(0, expirationTime - currentTime); // 남은 시간 계산 - timeLeftRef.current = Math.floor(remainingTime / 1000); // 초 단위로 변환 - - // 타이머 설정 - const interval = setInterval(() => { - timeLeftRef.current -= 1; // 남은 시간 감소 - setTimeLeft(timeLeftRef.current); - if (timeLeftRef.current <= 0) { - logout(); + const updateRemainingTime = () => { + const currentTime = Date.now(); // 현재 시간 + const remainingTime = Math.max(0, expirationTime - currentTime); // 남은 시간 계산 + setTimeLeft(Math.floor(remainingTime / 1000)); // 초 단위로 상태 업데이트 + + if (remainingTime <= 0) { + logout(); // 시간이 다되면 로그아웃 } - }, 1000); + }; + + updateRemainingTime(); // 초기 업데이트 + const interval = setInterval(updateRemainingTime, 1000); // 1초마다 업데이트 + return () => clearInterval(interval); } }, [token]); From 68696f9f7ba575a21214b33fe051a8a04f08adae Mon Sep 17 00:00:00 2001 From: hakyoung12 Date: Fri, 18 Oct 2024 14:41:48 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[KAN-126]=20chore:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C=20gatherings=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98=EB=8A=94=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/components/Gnb/TokenExpirationTimerLayout.tsx | 2 +- src/app/components/Gnb/UserStatus.tsx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/app/components/Gnb/TokenExpirationTimerLayout.tsx b/src/app/components/Gnb/TokenExpirationTimerLayout.tsx index 333944a7..e28d9113 100644 --- a/src/app/components/Gnb/TokenExpirationTimerLayout.tsx +++ b/src/app/components/Gnb/TokenExpirationTimerLayout.tsx @@ -20,7 +20,7 @@ const TokenExpirationTimerLayout = ({ }: TokenExpirationTimerLayoutProps) => { const { timeLeft } = TokenExpirationTimer(token); - return timeLeft > 0 ? ( + return token ? (

{variant === 'gnb' ? ( <> diff --git a/src/app/components/Gnb/UserStatus.tsx b/src/app/components/Gnb/UserStatus.tsx index 1986965c..ab5a6554 100644 --- a/src/app/components/Gnb/UserStatus.tsx +++ b/src/app/components/Gnb/UserStatus.tsx @@ -5,7 +5,6 @@ import { Profile } from '@/public/images'; import Image from 'next/image'; import Link from 'next/link'; import { useState, useRef, useEffect } from 'react'; -import { useRouter } from 'next/navigation'; import { UserData } from '@/types/client.type'; import { postUserLogoutData } from '@/app/api/actions/mypage/postUserLogoutData'; import toast from 'react-hot-toast'; @@ -19,7 +18,6 @@ interface UserStatusProps { const UserStatus = ({ user, token }: UserStatusProps) => { const [isOpen, setIsOpen] = useState(false); const dropDownRef = useRef(null); - const router = useRouter(); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -43,7 +41,7 @@ const UserStatus = ({ user, token }: UserStatusProps) => { if (result) { toast.success('로그아웃이 완료되었습니다.'); deleteCookie('token'); - router.push('/gatherings'); + // router.push('/gatherings'); } else { toast.error('로그아웃에 실패했습니다. 다시 시도해 주세요.'); } From 9bccf3e933f8509ccec266c53a0d92b5cb176ccb Mon Sep 17 00:00:00 2001 From: hakyoung12 Date: Fri, 18 Oct 2024 15:16:44 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[KAN-126]=20test:=20=EB=B0=94=EB=80=90=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Gnb/TokenExpirationTimerLayout.test.tsx | 13 ++++--------- src/app/components/Gnb/UserStatus.test.tsx | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/app/components/Gnb/TokenExpirationTimerLayout.test.tsx b/src/app/components/Gnb/TokenExpirationTimerLayout.test.tsx index 8dc75f5b..1fa30fb8 100644 --- a/src/app/components/Gnb/TokenExpirationTimerLayout.test.tsx +++ b/src/app/components/Gnb/TokenExpirationTimerLayout.test.tsx @@ -22,7 +22,7 @@ describe('TokenExpirationTimerLayout 컴포넌트', () => { timeLeft: 300, }); - render(); + render(); expect(screen.queryByText(/남은 시간:/)).not.toBeInTheDocument(); }); @@ -40,14 +40,9 @@ describe('TokenExpirationTimerLayout 컴포넌트', () => { expect(screen.getByText('남은 시간: 2분 0초')).toBeInTheDocument(); }); - // 남은 시간이 0일 경우 아무것도 렌더링하지 않아야 함 - it('should render nothing when time left is 0', () => { - (TokenExpirationTimer as jest.Mock).mockReturnValue({ - isLoggedIn: true, - timeLeft: 0, - }); - - render(); + // 토큰이 없을 경우 아무것도 렌더링하지 않아야 함 + it('should not render anything when there is no token', () => { + render(); expect(screen.queryByText(/남은 시간:/)).not.toBeInTheDocument(); }); diff --git a/src/app/components/Gnb/UserStatus.test.tsx b/src/app/components/Gnb/UserStatus.test.tsx index 88d6dd45..a06332ae 100644 --- a/src/app/components/Gnb/UserStatus.test.tsx +++ b/src/app/components/Gnb/UserStatus.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import UserStatus from './UserStatus'; import { useRouter } from 'next/navigation'; import { deleteCookie } from '@/app/api/actions/cookie/cookie'; @@ -35,6 +35,7 @@ jest.mock('@/app/api/actions/mypage/postUserLogoutData', () => ({ jest.mock('react-hot-toast', () => ({ success: jest.fn(), + error: jest.fn(), })); describe('UserStatus 컴포넌트', () => { @@ -94,6 +95,5 @@ describe('UserStatus 컴포넌트', () => { expect(mockPostUserLogoutData).toHaveBeenCalled(); expect(mockToastSuccess).toHaveBeenCalledWith('로그아웃이 완료되었습니다.'); expect(mockDeleteCookie).toHaveBeenCalledWith('token'); - expect(mockPush).toHaveBeenCalledWith('/gatherings'); }); }); From 3b03cc095fcc0e7c00e56711e3d5bfcbe7e8ed1e Mon Sep 17 00:00:00 2001 From: hakyoung12 Date: Fri, 18 Oct 2024 15:32:24 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[KAN-126]=20chore:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/components/Gnb/UserStatus.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/components/Gnb/UserStatus.tsx b/src/app/components/Gnb/UserStatus.tsx index ab5a6554..307154c0 100644 --- a/src/app/components/Gnb/UserStatus.tsx +++ b/src/app/components/Gnb/UserStatus.tsx @@ -41,7 +41,6 @@ const UserStatus = ({ user, token }: UserStatusProps) => { if (result) { toast.success('로그아웃이 완료되었습니다.'); deleteCookie('token'); - // router.push('/gatherings'); } else { toast.error('로그아웃에 실패했습니다. 다시 시도해 주세요.'); }