From 72c108cdd9553c98147913d07cafdec109ae11a0 Mon Sep 17 00:00:00 2001 From: YOOJS1205 Date: Sun, 28 Jan 2024 22:42:47 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/api-config.ts | 26 +++++++++++++++++++---- src/constants/endpoint.ts | 1 + src/middleware.ts | 2 ++ src/utils/{getTokenRefresh.ts => auth.ts} | 4 ++++ 4 files changed, 29 insertions(+), 4 deletions(-) rename src/utils/{getTokenRefresh.ts => auth.ts} (73%) diff --git a/src/api/api-config.ts b/src/api/api-config.ts index 083c4b1d..596cf7fd 100644 --- a/src/api/api-config.ts +++ b/src/api/api-config.ts @@ -1,8 +1,8 @@ import axios from 'axios'; import Cookies from 'js-cookie'; -import { TOKEN_REFRESH_URL } from '@constants/endpoint'; -import { getTokenRefresh } from '@utils/getTokenRefresh'; +import { TOKEN_REFRESH_URL, LOGOUT_URL } from '@constants/endpoint'; +import { getTokenRefresh, logout } from '@utils/auth'; type Method = 'get' | 'post' | 'put' | 'delete' | 'patch'; @@ -62,6 +62,11 @@ axiosInstance.interceptors.request.use( delete config.headers['Authorization']; } + // NOTE: 로그아웃 요청 시 헤더 변경 + if (config.url === LOGOUT_URL) { + config.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + return config; }, function (error) { @@ -88,12 +93,25 @@ axiosInstance.interceptors.response.use( const refreshToken = Cookies.get('refreshToken'); // NOTE: 토큰 재발급 요청 - if (response.data.code === 401 && refreshToken) { + if ( + config.url !== TOKEN_REFRESH_URL && + response.data.code === 401 && + refreshToken + ) { const { data } = await getTokenRefresh(); Cookies.set('accessToken', data.accessToken); config.headers['Authorization'] = data.accessToken; + return axios(config); + } + + // NOTE: 토큰 재발급 요청이 유효하지 않으면, 쿠키의 토큰을 삭제하고 로그아웃 처리. 로그인 페이지로 이동 + if (config.url === TOKEN_REFRESH_URL && response.data.code === 401) { + Cookies.remove('accessToken'); + Cookies.remove('refreshToken'); + logout(); + + window.location.href = '/login'; } - return axios(config); }, ); diff --git a/src/constants/endpoint.ts b/src/constants/endpoint.ts index da246a12..5d6b986a 100644 --- a/src/constants/endpoint.ts +++ b/src/constants/endpoint.ts @@ -1 +1,2 @@ export const TOKEN_REFRESH_URL = '/api/v1/auth/token/reissue'; +export const LOGOUT_URL = '/api/v1/auth/logout'; diff --git a/src/middleware.ts b/src/middleware.ts index e600fabf..877c0ef6 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -17,6 +17,8 @@ export function middleware(request: NextRequest) { url.pathname = '/login'; } + url.search = ''; + const response = NextResponse.redirect(url); return response; diff --git a/src/utils/getTokenRefresh.ts b/src/utils/auth.ts similarity index 73% rename from src/utils/getTokenRefresh.ts rename to src/utils/auth.ts index e0aee678..7cf90b5b 100644 --- a/src/utils/getTokenRefresh.ts +++ b/src/utils/auth.ts @@ -10,3 +10,7 @@ export const getTokenRefresh = async (): Promise< > => { return await axiosRequest('post', '/api/v1/auth/token/reissue'); }; + +export const logout = async (): Promise => { + return axiosRequest('post', '/api/v1/auth/logout'); +}; From 27b4baea46285b6bfbf37787932ce683d3490160 Mon Sep 17 00:00:00 2001 From: YOOJS1205 Date: Sun, 28 Jan 2024 23:13:47 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EC=84=A4=EC=A0=95=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/icon24/right_arrow_24.svg | 3 +++ src/app/settings/layout.tsx | 14 ++++++++++++ src/app/settings/page.tsx | 30 +++++++++++++++++++++++++ src/constants/settings.ts | 25 +++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 public/assets/icon24/right_arrow_24.svg create mode 100644 src/app/settings/layout.tsx create mode 100644 src/app/settings/page.tsx create mode 100644 src/constants/settings.ts diff --git a/public/assets/icon24/right_arrow_24.svg b/public/assets/icon24/right_arrow_24.svg new file mode 100644 index 00000000..25cf4e08 --- /dev/null +++ b/public/assets/icon24/right_arrow_24.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/settings/layout.tsx b/src/app/settings/layout.tsx new file mode 100644 index 00000000..7811a1e0 --- /dev/null +++ b/src/app/settings/layout.tsx @@ -0,0 +1,14 @@ +import Header from '@components/common/Header'; + +interface TermsLayoutProps { + children: React.ReactNode; +} + +export default function layout({ children }: TermsLayoutProps) { + return ( +
+
+ {children} +
+ ); +} diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx new file mode 100644 index 00000000..30968d37 --- /dev/null +++ b/src/app/settings/page.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { useRouter } from 'next/navigation'; + +import { SETTINGS, Setting } from '@constants/settings'; +import RightArrowIcon from 'public/assets/icon24/right_arrow_24.svg'; + +export default function Page() { + const { push } = useRouter(); + + const handleClickSettingItem = (setting: Setting) => () => { + if (setting.url) { + push(setting.url); + } + }; + return ( +
    + {SETTINGS.map((setting, index) => ( +
  • +

    {setting.title}

    + {setting.url && } +
  • + ))} +
+ ); +} diff --git a/src/constants/settings.ts b/src/constants/settings.ts new file mode 100644 index 00000000..705b53f0 --- /dev/null +++ b/src/constants/settings.ts @@ -0,0 +1,25 @@ +export interface Setting { + url?: string; + title: string; +} + +export const SETTINGS: Setting[] = [ + { + url: '/service-terms', + title: '이용약관', + }, + { + url: '/privacy-policy', + title: '개인정보처리방침', + }, + { + url: '/location-terms', + title: '위치기반 서비스 이용약관', + }, + { + title: '로그아웃', + }, + { + title: '회원탈퇴', + }, +]; From d11b6e7a0895dc1621021eeb8a19f1379bb6c321 Mon Sep 17 00:00:00 2001 From: YOOJS1205 Date: Sun, 28 Jan 2024 23:50:10 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EC=84=A4=EC=A0=95=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=9E=91=EC=84=B1,=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/assets/icon16/arrow_square_16.svg | 4 ++++ src/app/settings/layout.tsx | 4 +++- src/app/settings/page.tsx | 15 +++++++++++++-- src/hooks/api/useLogout.ts | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 public/assets/icon16/arrow_square_16.svg create mode 100644 src/hooks/api/useLogout.ts diff --git a/public/assets/icon16/arrow_square_16.svg b/public/assets/icon16/arrow_square_16.svg new file mode 100644 index 00000000..e5f1521d --- /dev/null +++ b/public/assets/icon16/arrow_square_16.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/settings/layout.tsx b/src/app/settings/layout.tsx index 7811a1e0..a7797995 100644 --- a/src/app/settings/layout.tsx +++ b/src/app/settings/layout.tsx @@ -7,7 +7,9 @@ interface TermsLayoutProps { export default function layout({ children }: TermsLayoutProps) { return (
-
+
+

설정

+
{children}
); diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 30968d37..86c9d30d 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -4,24 +4,35 @@ import { useRouter } from 'next/navigation'; import { SETTINGS, Setting } from '@constants/settings'; import RightArrowIcon from 'public/assets/icon24/right_arrow_24.svg'; +import ArrowSquareIcon from 'public/assets/icon16/arrow_square_16.svg'; +import { useLogout } from '@hooks/api/useLogout'; export default function Page() { const { push } = useRouter(); + const { mutate: logout } = useLogout(); const handleClickSettingItem = (setting: Setting) => () => { if (setting.url) { push(setting.url); } + + if (setting.title === '로그아웃') { + logout(); + } }; + return (
    {SETTINGS.map((setting, index) => (
  • -

    {setting.title}

    +
    +

    {setting.title}

    + {setting.url && } +
    {setting.url && }
  • ))} diff --git a/src/hooks/api/useLogout.ts b/src/hooks/api/useLogout.ts new file mode 100644 index 00000000..38830a7a --- /dev/null +++ b/src/hooks/api/useLogout.ts @@ -0,0 +1,19 @@ +import { useMutation, UseMutationResult } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; +import Cookies from 'js-cookie'; +import { useRouter } from 'next/navigation'; + +import { logout } from '@utils/auth'; + +export const useLogout = (): UseMutationResult => { + const { push } = useRouter(); + return useMutation({ + mutationKey: ['logout'], + mutationFn: logout, + onSuccess: () => { + Cookies.remove('accessToken'); + Cookies.remove('refreshToken'); + push('/login'); + }, + }); +};