From 0a4dc5b193954b2852882f59d3b613dda91aa3ab Mon Sep 17 00:00:00 2001 From: harry kim <73218463+hanyugeon@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:11:13 +0900 Subject: [PATCH] =?UTF-8?q?[#608]=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=20=EA=B6=8C=ED=95=9C=20=EB=B6=80=EC=97=AC=20?= =?UTF-8?q?(#611)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: accessToken 유무에 따라 내 그룹 목록 요청이가도록 쿼리 옵션 추가 * feat: 권한이 없는 페이지 접근 시 redirect 시키는 고차 컴포넌트 작성 * feat: 접근 권한이 필요한 페이지 withAuthRequired 고차 컴포넌트 적용 * refactor: HOC를 export default 부분에서 사용 --- src/app/group/[groupId]/edit/page.tsx | 18 +++++----- src/app/group/[groupId]/join/page.tsx | 6 ++-- src/app/group/create/page.tsx | 6 +++- src/app/profile/me/add/page.tsx | 8 +++-- src/app/profile/me/edit/page.tsx | 14 ++++---- src/app/profile/me/group/page.tsx | 6 ++-- src/hocs/withAuthRequired.tsx | 36 +++++++++++++++++++ .../profile/group/ProfileGroupContainer.tsx | 9 +++-- 8 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 src/hocs/withAuthRequired.tsx diff --git a/src/app/group/[groupId]/edit/page.tsx b/src/app/group/[groupId]/edit/page.tsx index beb48496..607b6ac4 100644 --- a/src/app/group/[groupId]/edit/page.tsx +++ b/src/app/group/[groupId]/edit/page.tsx @@ -10,12 +10,10 @@ import { import type { APIGroupDetail, APIEditBookGroup } from '@/types/group'; import { SERVICE_ERROR_MESSAGE } from '@/constants'; -import { - checkAuthentication, - isAxiosErrorWithCustomCode, -} from '@/utils/helpers'; - +import { isAxiosErrorWithCustomCode } from '@/utils/helpers'; import useToast from '@/v1/base/Toast/useToast'; + +import withAuthRequired from '@/hocs/withAuthRequired'; import BookGroupEditDateForm from '@/v1/bookGroup/edit/BookGroupEditDateForm'; import BookGroupEditIntroduceForm from '@/v1/bookGroup/edit/BookGroupEditIntroduceForm'; import BookGroupEditTitleForm from '@/v1/bookGroup/edit/BookGroupEditTitleForm'; @@ -28,13 +26,15 @@ const BookGroupEditPage = ({ }) => { const router = useRouter(); - const isAuthenticated = checkAuthentication(); - const { data: bookGroupData } = useBookGroupEditCurrentInfo(groupId); const { isOwner, title, description, maxMemberCount, startDate, endDate } = bookGroupData; - if (!isAuthenticated || !isOwner) { + /** + * @todo + * 401 페이지 만들기 (접근 권한이 없어요) + */ + if (!isOwner) { notFound(); } @@ -99,4 +99,4 @@ const BookGroupEditPage = ({ ); }; -export default BookGroupEditPage; +export default withAuthRequired(BookGroupEditPage); diff --git a/src/app/group/[groupId]/join/page.tsx b/src/app/group/[groupId]/join/page.tsx index 8750b3bc..08de9554 100644 --- a/src/app/group/[groupId]/join/page.tsx +++ b/src/app/group/[groupId]/join/page.tsx @@ -6,6 +6,8 @@ import { SubmitHandler, useForm } from 'react-hook-form'; import useJoinBookGroup from '@/hooks/group/useJoinBookGroup'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; +import withAuthRequired from '@/hocs/withAuthRequired'; + import Loading from '@/v1/base/Loading'; import Input from '@/v1/base/Input'; import InputLength from '@/v1/base/InputLength'; @@ -36,6 +38,8 @@ const JoinBookGroupPage = ({ ); }; +export default withAuthRequired(JoinBookGroupPage); + const BookGroupJoinForm = ({ groupId }: { groupId: number }) => { const router = useRouter(); const { isMember, hasPassword, question, joinBookGroup } = @@ -100,5 +104,3 @@ const BookGroupJoinForm = ({ groupId }: { groupId: number }) => { ); }; - -export default JoinBookGroupPage; diff --git a/src/app/group/create/page.tsx b/src/app/group/create/page.tsx index 9151feea..da9a1401 100644 --- a/src/app/group/create/page.tsx +++ b/src/app/group/create/page.tsx @@ -1,7 +1,11 @@ +'use client'; + +import withAuthRequired from '@/hocs/withAuthRequired'; + import CreateBookGroupFunnel from '@/v1/bookGroup/create/CreateBookGroupFunnel'; const GroupCreateFunnelPage = () => { return ; }; -export default GroupCreateFunnelPage; +export default withAuthRequired(GroupCreateFunnelPage); diff --git a/src/app/profile/me/add/page.tsx b/src/app/profile/me/add/page.tsx index 8cbfef82..ad62e746 100644 --- a/src/app/profile/me/add/page.tsx +++ b/src/app/profile/me/add/page.tsx @@ -4,8 +4,10 @@ import useAllJobQuery from '@/queries/job/useAllJobQuery'; import { checkAuthentication } from '@/utils/helpers'; -import AddJobProfile from '@/v1/profile/AddJobProfile'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; +import withAuthRequired from '@/hocs/withAuthRequired'; + +import AddJobProfile from '@/v1/profile/AddJobProfile'; const AddJobProfilePage = () => { return ( @@ -15,6 +17,8 @@ const AddJobProfilePage = () => { ); }; +export default withAuthRequired(AddJobProfilePage); + const Contents = () => { const isAuthenticated = checkAuthentication(); const allJobQuery = useAllJobQuery({ enabled: isAuthenticated }); @@ -23,5 +27,3 @@ const Contents = () => { ) : null; }; - -export default AddJobProfilePage; diff --git a/src/app/profile/me/edit/page.tsx b/src/app/profile/me/edit/page.tsx index 4016132d..970f2f7f 100644 --- a/src/app/profile/me/edit/page.tsx +++ b/src/app/profile/me/edit/page.tsx @@ -5,22 +5,22 @@ import useMyProfileQuery from '@/queries/user/useMyProfileQuery'; import { checkAuthentication } from '@/utils/helpers'; -import EditProfile from '@/v1/profile/EditProfile'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; +import withAuthRequired from '@/hocs/withAuthRequired'; -/** - * @todo - * Fallback UI 추가하기 - */ +import EditProfile from '@/v1/profile/EditProfile'; +import Loading from '@/v1/base/Loading'; const EditProfilePage = () => { return ( - + }> ); }; +export default withAuthRequired(EditProfilePage); + const Contents = () => { const isAuthenticated = checkAuthentication(); const allJobQuery = useAllJobQuery({ enabled: isAuthenticated }); @@ -30,5 +30,3 @@ const Contents = () => { ) : null; }; - -export default EditProfilePage; diff --git a/src/app/profile/me/group/page.tsx b/src/app/profile/me/group/page.tsx index cde1a832..71358673 100644 --- a/src/app/profile/me/group/page.tsx +++ b/src/app/profile/me/group/page.tsx @@ -4,6 +4,8 @@ import useMyGroupsQuery from '@/queries/group/useMyGroupQuery'; import { checkAuthentication } from '@/utils/helpers'; import SSRSafeSuspense from '@/components/SSRSafeSuspense'; +import withAuthRequired from '@/hocs/withAuthRequired'; + import BackButton from '@/v1/base/BackButton'; import TopNavigation from '@/v1/base/TopNavigation'; import DetailBookGroupCard from '@/v1/bookGroup/DetailBookGroupCard'; @@ -24,6 +26,8 @@ const UserGroupPage = () => { ); }; +export default withAuthRequired(UserGroupPage); + const UserGroupContent = () => { const isAuthenticated = checkAuthentication(); const { data } = useMyGroupsQuery({ enabled: isAuthenticated }); @@ -65,8 +69,6 @@ const UserGroupContent = () => { ); }; -export default UserGroupPage; - const PageSkeleton = () => (
    {Array.from({ length: 4 }).map((_, index) => ( diff --git a/src/hocs/withAuthRequired.tsx b/src/hocs/withAuthRequired.tsx new file mode 100644 index 00000000..db655b3a --- /dev/null +++ b/src/hocs/withAuthRequired.tsx @@ -0,0 +1,36 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; + +import { checkAuthentication } from '@/utils/helpers'; + +const withAuthRequired =

    ( + WrappedComponent: React.ComponentType

    +) => { + const Component = (props: P) => { + const router = useRouter(); + + const [isAuthenticated, setIsAuthenticated] = useState(false); + + useEffect(() => { + const hasAccessToken = checkAuthentication(); + + if (!hasAccessToken) { + router.push('/login'); + } else { + setIsAuthenticated(hasAccessToken); + } + }, [router]); + + if (!isAuthenticated) { + return null; + } else { + return ; + } + }; + + return Component; +}; + +export default withAuthRequired; diff --git a/src/v1/profile/group/ProfileGroupContainer.tsx b/src/v1/profile/group/ProfileGroupContainer.tsx index d066fa50..cb033e86 100644 --- a/src/v1/profile/group/ProfileGroupContainer.tsx +++ b/src/v1/profile/group/ProfileGroupContainer.tsx @@ -1,6 +1,9 @@ import useMyGroupsQuery from '@/queries/group/useMyGroupQuery'; import useMyProfileQuery from '@/queries/user/useMyProfileQuery'; -import { APIUser } from '@/types/user'; +import type { APIUser } from '@/types/user'; + +import { checkAuthentication } from '@/utils/helpers'; + import ProfileGroupPresenter from './ProfileGroupPresenter'; const ProfileGroupContainer = ({ @@ -8,7 +11,9 @@ const ProfileGroupContainer = ({ }: { userId: 'me' | APIUser['userId']; }) => { - const { data } = useMyGroupsQuery(); + const isAuthenticated = checkAuthentication(); + + const { data } = useMyGroupsQuery({ enabled: isAuthenticated }); const { data: { userId: myId }, } = useMyProfileQuery({ enabled: userId === 'me' });