From 74a9bc7615ebb5bde3cc3d4c54cc65a659d1c5ef Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Wed, 1 Nov 2023 14:38:26 +0900 Subject: [PATCH 01/23] =?UTF-8?q?style:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=93=B1=EB=A1=9D=ED=95=98=EA=B8=B0=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/register/RegisterHeader.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/register/RegisterHeader.tsx b/src/pages/register/RegisterHeader.tsx index 287cb567..33835f46 100644 --- a/src/pages/register/RegisterHeader.tsx +++ b/src/pages/register/RegisterHeader.tsx @@ -98,8 +98,7 @@ const RegisterHeader = () => { return ( <>
-
-

등록하기

+
+ +
+
+ + ); +}; + +export default LoginGuideModal; From a2f234ff4d96676ab2daca7529b3ac4ea54a924a Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Wed, 1 Nov 2023 14:41:01 +0900 Subject: [PATCH 03/23] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 쿠키에 maxAge와 expires 저장으로 인해 만료일 검증 로직이 불필요해서 삭제했습니다. --- src/commons/cookie/getUser.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/commons/cookie/getUser.ts b/src/commons/cookie/getUser.ts index 84c1c2c8..f7117212 100644 --- a/src/commons/cookie/getUser.ts +++ b/src/commons/cookie/getUser.ts @@ -8,20 +8,6 @@ export const getLoginState = () => { return '로그인'; }; -export const validateExpiredToken = () => { - const now = new Date(); - const expiredDate = new Date(getCookie('expiredDate')); - console.log('만료 검사'); - - // 토큰만료 검사 후 삭제 - if (now > expiredDate) { - removeCookie('loginToken'); - removeCookie('expiredDate'); - removeCookie('userAccountInfo'); - console.log('토큰이 만료되어 삭제되었습니다.'); - } -}; - export const removeToken = () => { removeCookie('loginToken'); }; From baca3e203a920de4f95f1aac386eefd8b1caa66f Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Wed, 1 Nov 2023 14:41:37 +0900 Subject: [PATCH 04/23] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=BF=A0=ED=82=A4=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 쿠키에 저장할 때 maxAge와 expires를 option 값으로 넣도록 변경했습니다. 저장 방식 변경으로 인해 자동 삭제되는 쿠키를 확인하여 모달창이 나올 수 있도록 구현했습니다. --- src/layouts/ValidateCheckLayout.tsx | 97 ++++++++++++++++++++++++++--- src/pages/login/LoginInputForm.tsx | 43 +++++++------ 2 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/layouts/ValidateCheckLayout.tsx b/src/layouts/ValidateCheckLayout.tsx index da146e86..228db245 100644 --- a/src/layouts/ValidateCheckLayout.tsx +++ b/src/layouts/ValidateCheckLayout.tsx @@ -1,20 +1,103 @@ -import { validateExpiredToken } from 'commons/cookie/getUser'; -import React, { useEffect } from 'react'; -import { Routes } from 'react-router-dom'; +import { getCookie, setCookie } from 'commons/cookie/cookie'; +import LoginGuideModal from 'commons/modals/LoginGuideModal'; +import React, { useEffect, useState } from 'react'; +import { Route, Routes } from 'react-router-dom'; interface LayoutProps { children: React.ReactNode; } const ValidateCheckLayout: React.FC = ({ children }) => { + const [isModalOpen, setIsModalOpen] = useState(false); + useEffect(() => { - validateExpiredToken(); - const intervalId = setInterval(validateExpiredToken, 60000 * 30); // 30분에 한 번 + // 1분마다 loginToken이라는 이름의 쿠키가 있는지 검사 + const checkTokenInterval = setInterval(() => { + const loginToken = getCookie('loginToken'); + const userAccount = getCookie('userAccountInfo'); + if (!loginToken && !userAccount) { + // loginToken이 없으면 모달 열기 + setIsModalOpen(true); + setCookie('userAccountInfo', 'Not Login'); + } + }, 60000); - return () => clearInterval(intervalId); + return () => { + // 컴포넌트가 언마운트될 때 타이머를 정리 + clearInterval(checkTokenInterval); + }; }, []); - return {children}; + return ( +
+ + + } + /> + ; + + } + /> + ; + + } + /> + ; + + } + /> + ; + + } + /> + ; + + } + /> + ; + + } + /> + ; + + } + /> + ; + + } + /> + ; + + {children} +
+ ); }; export default ValidateCheckLayout; diff --git a/src/pages/login/LoginInputForm.tsx b/src/pages/login/LoginInputForm.tsx index 788d232f..e90b270a 100644 --- a/src/pages/login/LoginInputForm.tsx +++ b/src/pages/login/LoginInputForm.tsx @@ -20,6 +20,7 @@ const LoginInputForm = () => { }); const userfetch = () => { + let token: string; fetch(`${process.env.REACT_APP_URI}/account/login`, { method: 'POST', headers: { @@ -30,27 +31,32 @@ const LoginInputForm = () => { email: userInfo.email, password: userInfo.password, }), - }) - .then((res) => { - const jwtToken = res.headers.get('Authorization'); - if (jwtToken) { - const slicedToken = jwtToken.split(' ')[1]; - setCookie('loginToken', slicedToken); - } else { - console.log('로그인 실패로 token이 Null'); - } - return res.json(); - }) - - .then((data) => { - console.log('data: ', data); - if (data.success) { + }).then((res) => { + const jwtToken = res.headers.get('Authorization'); + if (jwtToken) { + // eslint-disable-next-line prefer-destructuring + token = jwtToken.split(' ')[1]; + } else { + console.log('로그인 실패로 token이 Null'); + } + return res.json().then((data) => { + if (data.success && token) { const { accountInfo, tokenExpirationDateTime } = data.response; const { id, role } = accountInfo; - const expiredDate = new Date(tokenExpirationDateTime); + const currentDate = new Date(); + const tokenExpirationDate = new Date(tokenExpirationDateTime); + const timeDifferenceInMilliseconds = + Number(tokenExpirationDate) - Number(currentDate); + + setCookie('userAccountInfo', `${role} ${id}`, { + expires: tokenExpirationDate, + maxAge: timeDifferenceInMilliseconds, + }); + setCookie('loginToken', token, { + expires: tokenExpirationDate, + maxAge: timeDifferenceInMilliseconds, + }); - setCookie('userAccountInfo', `${role} ${id}`); - setCookie('expiredDate', String(expiredDate)); navigate('/'); } else { // 형식은 맞지만 입력된 값이 가입되지 않은 계정일 때 @@ -58,6 +64,7 @@ const LoginInputForm = () => { } setIsLoading(false); }); + }); }; const validateCheck = () => { From ae36725515773a3a95e88bbe23e6b897e698e87b Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Wed, 1 Nov 2023 15:16:03 +0900 Subject: [PATCH 05/23] =?UTF-8?q?fix:=20maxAge=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit milliseconds 단위에서 maxAge 값에 맞는 초 단위로 변경했습니다. --- src/pages/login/LoginInputForm.tsx | 49 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/pages/login/LoginInputForm.tsx b/src/pages/login/LoginInputForm.tsx index e90b270a..17388116 100644 --- a/src/pages/login/LoginInputForm.tsx +++ b/src/pages/login/LoginInputForm.tsx @@ -11,6 +11,7 @@ const LoginInputForm = () => { const [errors, setErrors] = useState>({}); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); + const currentDate = new Date(); const validationSchema = Yup.object().shape({ email: Yup.string() @@ -31,7 +32,7 @@ const LoginInputForm = () => { email: userInfo.email, password: userInfo.password, }), - }).then((res) => { + }).then(async (res) => { const jwtToken = res.headers.get('Authorization'); if (jwtToken) { // eslint-disable-next-line prefer-destructuring @@ -39,31 +40,29 @@ const LoginInputForm = () => { } else { console.log('로그인 실패로 token이 Null'); } - return res.json().then((data) => { - if (data.success && token) { - const { accountInfo, tokenExpirationDateTime } = data.response; - const { id, role } = accountInfo; - const currentDate = new Date(); - const tokenExpirationDate = new Date(tokenExpirationDateTime); - const timeDifferenceInMilliseconds = - Number(tokenExpirationDate) - Number(currentDate); - - setCookie('userAccountInfo', `${role} ${id}`, { - expires: tokenExpirationDate, - maxAge: timeDifferenceInMilliseconds, - }); - setCookie('loginToken', token, { - expires: tokenExpirationDate, - maxAge: timeDifferenceInMilliseconds, - }); + const data = await res.json(); + if (data.success && token) { + const { accountInfo, tokenExpirationDateTime } = data.response; + const { id, role } = accountInfo; + const tokenExpirationDate = new Date(tokenExpirationDateTime); + const timeDifferenceseconds = Math.floor( + (Number(tokenExpirationDate) - Number(currentDate)) / 1000, + ); - navigate('/'); - } else { - // 형식은 맞지만 입력된 값이 가입되지 않은 계정일 때 - alert(data.error.message); - } - setIsLoading(false); - }); + setCookie('userAccountInfo', `${role} ${id}`, { + expires: tokenExpirationDate, + maxAge: timeDifferenceseconds, + }); + setCookie('loginToken', token, { + expires: tokenExpirationDate, + maxAge: timeDifferenceseconds, + }); + navigate('/'); + } else { + // 형식은 맞지만 입력된 값이 가입되지 않은 계정일 때 + alert(data.error.message); + } + setIsLoading(false); }); }; From b6fe67ffa771eac0f9c5d863258795de889d83a5 Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Wed, 1 Nov 2023 15:17:05 +0900 Subject: [PATCH 06/23] =?UTF-8?q?style:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=AA=A8=EB=8B=AC=EC=B0=BD=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 글자와 버튼 간격이 안 맞아서 스타일 수정했습니다. --- src/commons/modals/LoginGuideModal.tsx | 10 ++++++---- src/layouts/ValidateCheckLayout.tsx | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/commons/modals/LoginGuideModal.tsx b/src/commons/modals/LoginGuideModal.tsx index 2607cab4..6149fa85 100644 --- a/src/commons/modals/LoginGuideModal.tsx +++ b/src/commons/modals/LoginGuideModal.tsx @@ -1,3 +1,4 @@ +import { setCookie } from 'commons/cookie/cookie'; import { useNavigate } from 'react-router-dom'; interface ModalProps { @@ -12,15 +13,15 @@ const LoginGuideModal = ({ isOpen, setIsOpen }: ModalProps) => { } return ( -
-
+
+
자동으로 로그아웃 되었습니다.
재로그인 하시겠습니까?
-
+
+ +
+ ); + } + + return ( +
+
+ +
+ + {isDropdownOpen && ( +
+
+ {options.map((option, index) => ( +
handleOptionClick(option)} + role="menuitem" + > + {option} +
+ ))} +
+
+ )} +
+ ); +}; + +export default UserDropdownBox; diff --git a/src/layouts/VGNB.tsx b/src/layouts/VGNB.tsx index c211afdd..d8230836 100644 --- a/src/layouts/VGNB.tsx +++ b/src/layouts/VGNB.tsx @@ -1,5 +1,5 @@ import LogoButton from 'commons/LogoButton'; -import { getLoginState, removeToken } from 'commons/cookie/getUser'; +import UserSelectBox from 'commons/UserDropdownBox'; import { Link } from 'react-router-dom'; export interface VGNBProps { @@ -36,19 +36,8 @@ const VGNB = (props: VGNBProps) => {
- - - - - - + {/* 여기서 변경 */} +
    diff --git a/src/layouts/VLargeGNB.tsx b/src/layouts/VLargeGNB.tsx index 8a3f7737..9e603832 100644 --- a/src/layouts/VLargeGNB.tsx +++ b/src/layouts/VLargeGNB.tsx @@ -1,6 +1,6 @@ import { Link } from 'react-router-dom'; import LogoButton from 'commons/LogoButton'; -import { getLoginState, removeToken } from 'commons/cookie/getUser'; +import UserSelectBox from 'commons/UserDropdownBox'; export interface VLargeGNBProps { handleCategoryButtonClick: () => void; @@ -59,22 +59,8 @@ const VLargeGNB = (props: VLargeGNBProps) => { 등록하기
- -
- - - - - - -
+ {/* 여기서 변경 */} +
); From 4f495aecec3098915615ab9e592f795a60ed4b7b Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Fri, 3 Nov 2023 23:12:28 +0900 Subject: [PATCH 10/23] =?UTF-8?q?fix:=20=EC=9C=A0=EC=A0=80=20Selectbox?= =?UTF-8?q?=EC=9D=98=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EC=8B=9C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=EB=8B=A4=EC=9D=B4=EB=A0=89=ED=8A=B8=20?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그아웃 시 로그인 페이지로 이동하는 것이 부자연스러워서 페이지 이동 없이 토큰만 제거하는 것으로 수정 --- src/commons/UserDropdownBox.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commons/UserDropdownBox.tsx b/src/commons/UserDropdownBox.tsx index f3f3afe9..1a6bd76d 100644 --- a/src/commons/UserDropdownBox.tsx +++ b/src/commons/UserDropdownBox.tsx @@ -1,6 +1,6 @@ import { useNavigate } from 'react-router-dom'; import { useState } from 'react'; -import { getCookie, removeCookie } from './cookie/cookie'; +import { getCookie, removeCookie, setCookie } from './cookie/cookie'; // 로그인 되었을 때 상태를 보여주는 SelectBox 제작 const UserDropdownBox = () => { @@ -30,7 +30,8 @@ const UserDropdownBox = () => { break; case '로그아웃': removeToken(); - navigate('/login'); + window.location.reload(); + setCookie('userAccountInfo', 'Not Login'); break; default: break; From 99b802424839ffe676a386a95527f0823e7a206d Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Sat, 4 Nov 2023 20:49:31 +0900 Subject: [PATCH 11/23] =?UTF-8?q?rechore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20ts=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 검증을 위한 로직 수정으로 인해 쓰지 않는 파일 삭제했습니다. --- src/commons/cookie/getUser.ts | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/commons/cookie/getUser.ts diff --git a/src/commons/cookie/getUser.ts b/src/commons/cookie/getUser.ts deleted file mode 100644 index f7117212..00000000 --- a/src/commons/cookie/getUser.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getCookie, removeCookie } from './cookie'; - -export const getLoginState = () => { - const token = getCookie('loginToken'); - if (token) { - return '로그아웃'; - } - return '로그인'; -}; - -export const removeToken = () => { - removeCookie('loginToken'); -}; From b12e2f16d7f8c89b9c39d700978d9fb86251f52b Mon Sep 17 00:00:00 2001 From: JeonDoGyun Date: Sat, 4 Nov 2023 20:50:23 +0900 Subject: [PATCH 12/23] =?UTF-8?q?fix:=20=ED=85=8C=EC=8A=A4=ED=8C=85?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=20=EB=B9=84=EB=94=94=EC=98=A4=20?= =?UTF-8?q?=EC=9D=8C=EC=86=8C=EA=B1=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/home/VHome.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/home/VHome.tsx b/src/pages/home/VHome.tsx index af31eaeb..8eb3cc91 100644 --- a/src/pages/home/VHome.tsx +++ b/src/pages/home/VHome.tsx @@ -71,6 +71,7 @@ const VHome = (homeProps: HomeProps) => {