Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fe-059 로그인 여부에 따른 기능제한 추가 #82

Merged
merged 9 commits into from
Jul 7, 2023
8 changes: 1 addition & 7 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,8 @@ const nextConfig = {
},
// 프록시 설정
async rewrites(){
const rewriteTargets = [];
const API_ENDPOINT = process.env.API_URL ?? process.env.NEXT_PUBLIC_API_URL;

const rewriteTargets = [
{
source: '/api/login',
destination: `${API_ENDPOINT}/v1/users/login`
},
]

if(process.env.NODE_ENV === 'development') {
rewriteTargets.push({
Expand Down
4 changes: 4 additions & 0 deletions src/api/user/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class UserAPI {
})
.json<Profile | undefined>();
}

async postLogout() {
return request.post('v1/users/logout').json();
}
}

export const userAPI = new UserAPI();
2 changes: 2 additions & 0 deletions src/api/user/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as useProfile } from './useProfile';

export { default as useLogin } from './useLogin';

export { default as useLogout } from './useLogout';
16 changes: 16 additions & 0 deletions src/api/user/hooks/useLogout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useQueryClient } from '@tanstack/react-query';
import { useMutation } from '@/hooks';
import { userKeys } from '@/api/user/queryKeys';
import { userAPI } from '@/api/user/api';

const useLogout = () => {
const queryClient = useQueryClient();

return useMutation(userAPI.postLogout, {
onSuccess: () => {
void queryClient.invalidateQueries(userKeys.profile());
},
});
};

export default useLogout;
4 changes: 3 additions & 1 deletion src/app/board/components/AsideSection/AsideSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useRouter } from 'next/navigation';
import { Button, Typography, Dropdown, SvgIcon, Toast } from '@/components';
import { JoinModal, DeleteModal, CancelJoinModal, ParticipantsList } from '@/app/board/components';
import { useProfile } from '@/api/hooks';
import { useOverlay, useClipBoard } from '@/hooks';
import { useOverlay, useClipBoard, useLoginRedirect } from '@/hooks';
import { BOARD_STATUS, TOAST_TEXT } from '@/app/board/constants';
import { BoardDetail } from '@/api/types';
import { wrapper, counterText, listBottomSpace, dropdown, dropdownMenu, buttonGroup } from './AsideSection.css';
Expand All @@ -16,6 +16,7 @@ interface Props {
const AsideSection = ({ board }: Props) => {
const router = useRouter();
const { data: profile } = useProfile();
const { redirectToLogin } = useLoginRedirect();
const [openModal, closeModal] = useOverlay();
const [openToast, closeToast] = useOverlay();
const [, copyToClipBoard] = useClipBoard();
Expand All @@ -32,6 +33,7 @@ const AsideSection = ({ board }: Props) => {
};

const handleClickJoinButton = () => {
if (!profile) return redirectToLogin();
openModal(
<JoinModal
boardId={boardId}
Expand Down
9 changes: 8 additions & 1 deletion src/app/home/components/HeroSection/HeroSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
'use client';

import Link from 'next/link';
import { useProfile } from '@/api/user';
import { Button, Typography } from '@/components';
import { GradientText } from '@/app/home/components';
import { section, title, subTitle, button } from './HeroSection.css';
import { useLoginRedirect } from '@/hooks';

const HeroSection = () => {
const { data: profile } = useProfile();
const { loginPath } = useLoginRedirect();

return (
<section className={section}>
<Typography variant="display2" as="h1" className={title}>
Expand All @@ -20,7 +27,7 @@ const HeroSection = () => {
가볍게 대화해보세요!
</Typography>

<Link href={'/write'}>
<Link href={profile ? '/write' : loginPath}>
<Button color="primary" className={button}>
<Typography color="white" variant="title2">
우리 회사 먹팟 만들기
Expand Down
5 changes: 4 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import '@/styles/globals.css';
import localFont from 'next/font/local';
import Script from 'next/script';
import { OverlayProvider, QueryProvider } from '@/providers';
import { ProfileProvider } from '@/providers/server';

const pretendardFont = localFont({
src: '../../public/PretendardVariable.woff2',
Expand Down Expand Up @@ -42,7 +43,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
/>
</noscript>
<QueryProvider>
<OverlayProvider>{children}</OverlayProvider>
<ProfileProvider>
<OverlayProvider>{children}</OverlayProvider>
</ProfileProvider>
</QueryProvider>
</body>
</html>
Expand Down
6 changes: 3 additions & 3 deletions src/app/login/components/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { useRouter } from 'next/navigation';
import { useLogin } from '@/api/hooks';
import { useLoginRedirect } from '@/hooks';
import { Input, InputSection } from '@/components';
import { wrapper, form } from './LoginForm.css';
import { FormProvider, FieldValues, SubmitHandler } from 'react-hook-form';
Expand All @@ -10,8 +10,8 @@ import { useLoginContext } from '../../contexts/LoginContext';
import { LoginButton } from '../../components';

const LoginForm = () => {
const router = useRouter();
const { mutate: login } = useLogin();
const { redirectBack } = useLoginRedirect();
const { keep } = useLoginContext();
const { method, errors, setSubmitError, resetSubmitError } = useLoginForm();

Expand All @@ -22,7 +22,7 @@ const LoginForm = () => {
{ email, password, keep },
{
onSuccess: () => {
router.replace('/');
redirectBack();
},
onError: (error) => {
setSubmitError(error.message);
Expand Down
9 changes: 2 additions & 7 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Logo } from '@/components';
import { Suspense } from '@suspensive/react';
import HeaderWrapper from './HeaderWrapper';
import HydratedHeaderActions from './HydratedHeaderActions';
import HeaderActions from './HeaderActions';

interface Props {
/** 헤더의 action buttons가 필요한지의 여부 */
Expand All @@ -12,11 +11,7 @@ const Header = async ({ actionRequired = true }: Props) => {
return (
<HeaderWrapper>
<Logo />
{actionRequired && (
<Suspense>
<HydratedHeaderActions />
</Suspense>
)}
{actionRequired && <HeaderActions />}
</HeaderWrapper>
);
};
Expand Down
7 changes: 6 additions & 1 deletion src/components/Header/LoginActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SvgIcon,
Typography,
} from '@/components';
import { useLogout } from '@/api/user';
import { type Profile as ProfileData } from '@/types/data';
import { dropdownToggle } from './Header.css';
import Link from 'next/link';
Expand All @@ -17,6 +18,8 @@ interface Props {
}

const LoginActions = ({ profile }: Props) => {
const { mutate: logout } = useLogout();

return (
<>
<Link href={'/write'}>
Expand All @@ -32,7 +35,9 @@ const LoginActions = ({ profile }: Props) => {
<SvgIcon id="chevrondown" width={24} height={24} />
</DropdownToggle>
<DropdownMenu placement="bottomRight" style={{ width: '236px' }}>
<DropdownItem itemKey="logout">로그아웃</DropdownItem>
<DropdownItem itemKey="logout" onClick={logout}>
로그아웃
</DropdownItem>
</DropdownMenu>
</Dropdown>
</>
Expand Down
4 changes: 3 additions & 1 deletion src/components/Header/UnloginActions.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Button } from '@/components';
import { useLoginRedirect } from '@/hooks';
import Link from 'next/link';

const UnloginActions = () => {
const { loginPath } = useLoginRedirect();
return (
<Link href="/login">
<Link href={`${loginPath}`}>
<Button color="explain" size="paddingSmall">
로그인
</Button>
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export { default as useIntersectObserver } from './useIntersectObserver/useInter
export { default as useOverlay } from './useOverlay/useOverlay';

export { default as useClipBoard } from './useClipBoard/useClipBoard';

export { default as useLoginRedirect } from './useLoginRedirect/useLoginRedirect';
30 changes: 30 additions & 0 deletions src/hooks/useLoginRedirect/useLoginRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useCallback } from 'react';
import { useRouter, usePathname, useSearchParams } from 'next/navigation';

const HOME_PATH = '/';
const LOGIN_PATH = '/login';
const REDIRECT_QUERY = 'redirectPath';

const useLoginRedirect = () => {
const router = useRouter();
const searchParams = useSearchParams();
const currentPathname = usePathname();

const loginPath = `${LOGIN_PATH}?${REDIRECT_QUERY}=${currentPathname}`;

const redirectToLogin = useCallback(() => {
router.push(`${LOGIN_PATH}?${REDIRECT_QUERY}=${currentPathname}`);
}, [router, currentPathname]);

const redirectBack = useCallback(() => {
if (searchParams.has(REDIRECT_QUERY)) {
router.replace(searchParams.get(REDIRECT_QUERY) as string);
} else {
router.replace(HOME_PATH);
}
}, [router, searchParams]);

return { loginPath, redirectToLogin, redirectBack };
};

export default useLoginRedirect;
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { ReactNode } from 'react';
import { dehydrate, Hydrate } from '@tanstack/react-query';
import getQueryClient from '@/utils/getQueryClients';
import { api, queryKeys } from '@/api';
import HeaderActions from './HeaderActions';

const HydratedHeaderActions = async () => {
const ProfileProvider = async ({ children }: { children: ReactNode }) => {
const queryClient = getQueryClient();
await queryClient.prefetchQuery(queryKeys.user.profile(), () => api.user.getProfile());
const dehydratedState = dehydrate(queryClient);

return (
<Hydrate state={dehydratedState}>
<HeaderActions />
</Hydrate>
);
return <Hydrate state={dehydratedState}>{children}</Hydrate>;
};

export default HydratedHeaderActions;
export default ProfileProvider;
1 change: 1 addition & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as QueryProvider } from './QueryProvider';

export { OverlayProvider } from './OverlayProvider';
1 change: 1 addition & 0 deletions src/providers/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ProfileProvider } from './ProfileProvider';