Skip to content

Commit

Permalink
Feature : 로그인, 로그아웃 모듈화 (#603)
Browse files Browse the repository at this point in the history
* feat : useLogin 커스텀훅으로 만들기

* feat : useLogout 커스텀훅으로 만들기

* refactor : 타입명 수정

* refactor : 불필요한 코드 줄이기

* refactor : useLogin커스텀훅으로 교체

* feat : useLoginMuation추상화

* feat : 로그인 및 토큰 설정 비동기 처리

* feat : 토큰 제거 후 로그아웃

* feat : RN에서 메시지 수신 커스텀 훅으로 뺴기

* feat : 로그인 시 토큰 전송
  • Loading branch information
guesung committed Jan 30, 2024
1 parent ae71d65 commit e02fd94
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 100 deletions.
29 changes: 20 additions & 9 deletions src/apis/auth/mutations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
LoginResponse,
SignUpResponse,
postEmail,
postEmailVerify,
Expand All @@ -8,12 +9,23 @@ import {
postSMSVerify,
postSignUp,
} from '.';
import useAppRouter from '@/hooks/useAppRouter';
import { setTokenAtCookie } from '@/utils/auth/tokenController';
import sendMessageToReactNative from '@/utils/sendMessageToReactNative';
import useLogin from '@/hooks/token/useLogin';
import { useMutation } from '@tanstack/react-query';

export const useLoginMutation = () => useMutation({ mutationFn: postLogin });
export const useLoginMutation = () => {
const { login } = useLogin();

return useMutation({
mutationFn: postLogin,
onSuccess: async (response: LoginResponse) => {
await login({
accessToken: response.token.accessToken,
refreshToken: response.token.refreshToken,
userId: response.userId,
});
},
});
};

export const useReissueMutation = () => useMutation({ mutationFn: postReissue });

Expand All @@ -26,17 +38,16 @@ export const useEmailMutation = () => useMutation({ mutationFn: postEmail });
export const useEmailVerifyMutation = () => useMutation({ mutationFn: postEmailVerify });

export const useSignUpMutation = () => {
const { push } = useAppRouter();
const { login } = useLogin();

return useMutation({
mutationFn: postSignUp,
onSuccess: (data: SignUpResponse) => {
sendMessageToReactNative({ type: 'SIGN_IN', data: 'LOGIN_SUCCESS' });
onSuccess: async (data: SignUpResponse) => {
const {
userId,
token: { accessToken, refreshToken },
} = data;
setTokenAtCookie({ accessToken, refreshToken, userId });
push('/grouping');
await login({ accessToken, refreshToken, userId });
},
});
};
4 changes: 2 additions & 2 deletions src/apis/config/privateApi.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ApiError } from './customError';
import { postReissue } from '../auth';
import { BASE_API_URL } from '@/constants';
import { AUTH_ERROR_CODES } from '@/constants/errorCode';
import { getTokenFromCookie } from '@/utils/auth/tokenController';
import sendMessageToReactNative from '@/utils/sendMessageToReactNative';
import axios, { AxiosError, AxiosResponse, type InternalAxiosRequestConfig } from 'axios';

import type { CustomInstance, ErrorType } from './type';
Expand Down Expand Up @@ -45,6 +44,7 @@ privateApi.interceptors.response.use(
error.response.status === AUTH_ERROR_CODES.UNAUTHORIZED ||
error.response.status === AUTH_ERROR_CODES.NOT_FOUND
) {
sendMessageToReactNative({ type: 'AUTH', data: 'LOG_OUT' });
window.location.href = '/join?step=1';
}
return Promise.reject(error.response.data.reason);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import { useTranslation } from '@/app/i18n/client';
import { Icon } from '@/components/Icon';
import { Modal } from '@/components/Modal';
import { Spacing } from '@/components/Spacing';
import { AUTH_KEYS } from '@/constants/token';
import useLogout from '@/hooks/token/useLogout';
import { useModal } from '@/hooks/useModal';
import { removeLocalCookie } from '@/utils/cookieController';

interface DeleteModalProps {
onCancelClick: () => void;
Expand All @@ -16,11 +15,10 @@ export default function DeleteModal({ onCancelClick }: DeleteModalProps) {
const { open } = useModal();
const { t } = useTranslation('profile');
const { mutate } = usePatchSignOut();
const { logout } = useLogout();
const handleDeleteClick = () => {
mutate();
removeLocalCookie(AUTH_KEYS.accessToken);
removeLocalCookie(AUTH_KEYS.refreshToken);
removeLocalCookie(AUTH_KEYS.userId);
logout();
open(() => <DeleteCompleteModal />);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,9 @@ export default function Step1InputForm({ onPrev }: Step1InputFormProps) {
const onSubmit = (data: ProfileRequest) => {
if (!isAllTyped) return;

const makeProfileEditDTO = (data: ProfileRequest) => {
const { imageUrl, name, gender, introduce, personalities, countryName, countryImage, birth } =
data;

return {
imageUrl,
name,
gender,
introduce,
personalities,
countryName,
countryImage,
birth: formatBirthDTO(birth),
};
};

mutate(makeProfileEditDTO({ ...data }));
const { birth } = data;

mutate({ ...data, birth: formatBirthDTO(birth) });
};

const { open: openBottomSheet, close: closeBottomSheet } = useModal();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { useJoinContext } from '../../../components/JoinContext.client';
import { formatNumber, formatNumberBackSpace } from '../util';
import { LoginResponse, useLoginMutation, useSMSMutation } from '@/apis/auth';
import { useLoginMutation, useSMSMutation } from '@/apis/auth';
import { useTranslation } from '@/app/i18n/client';
import { Button, ButtonGroup } from '@/components/Button';
import { useTimerContext } from '@/components/Provider';
import { Spacing } from '@/components/Spacing';
import { TextFieldController } from '@/components/TextField';
import { regexr } from '@/constants/regexr';
import useAppRouter from '@/hooks/useAppRouter';
import { useToast } from '@/hooks/useModal';
import { setTokenAtCookie } from '@/utils/auth/tokenController';
import { ElementType, KeyboardEventHandler } from 'react';

import type { SignUpState } from '../../../type';
Expand All @@ -29,7 +27,6 @@ export default function NumberForm({ inputStatus, setInputStatus }: NumberSectio
const { start: timerStart, status: timerStatus } = useTimerContext();
const { openToast } = useToast();
const { mutate: mutateLogin } = useLoginMutation();
const { refresh, replace } = useAppRouter();

const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement> | React.KeyboardEvent<HTMLInputElement>
Expand All @@ -49,20 +46,7 @@ export default function NumberForm({ inputStatus, setInputStatus }: NumberSectio
timerStart();
const phoneNumberWithoutHyphen = data.phoneNumber.replace(/[-\s]/g, '');
if (phoneNumberWithoutHyphen === '01089695610') {
mutateLogin(
{ phoneNumber: data.phoneNumber },
{
onSuccess: async (response: LoginResponse) => {
await setTokenAtCookie({
accessToken: response.token.accessToken,
refreshToken: response.token.refreshToken,
userId: response.userId,
});
refresh();
replace('/grouping');
},
}
);
mutateLogin({ phoneNumber: data.phoneNumber });
return;
}
mutateSMS({ number: phoneNumberWithoutHyphen });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import { LayerLoading } from '@/components/Loading';
import { useTimerContext } from '@/components/Provider';
import { TextFieldController } from '@/components/TextField';
import { regexr } from '@/constants/regexr';
import useAppRouter from '@/hooks/useAppRouter';
import { setTokenAtCookie } from '@/utils/auth/tokenController';
import sendMessageToReactNative from '@/utils/sendMessageToReactNative';
import useLogin from '@/hooks/token/useLogin';

import type { SignUpState } from '../../../type';
import type { SubmitHandler } from 'react-hook-form';
Expand All @@ -21,10 +19,10 @@ interface NumberVerifyFormProps {

export default function NumberVerifyForm({ setInputStatus }: NumberVerifyFormProps) {
const { t } = useTranslation('join');
const { refresh, replace } = useAppRouter();
const hookForm = useJoinContext();
const { handleSubmit, setError, register, watch, resetField } = hookForm;
const { time, reset, start } = useTimerContext();
const { login } = useLogin();

const { nextStep } = useFunnelContext();
const { mutate: mutateSMSVerify } = useSMSVerifyMutation();
Expand Down Expand Up @@ -73,14 +71,11 @@ export default function NumberVerifyForm({ setInputStatus }: NumberVerifyFormPro
return;
}

sendMessageToReactNative({ type: 'SIGN_IN', data: 'LOGIN_SUCCESS' });
await setTokenAtCookie({
await login({
accessToken: response.token.accessToken,
refreshToken: response.token.refreshToken,
userId: response.userId,
});
refresh();
replace('/grouping');
},
}
);
Expand Down
21 changes: 0 additions & 21 deletions src/app/[lng]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
'use client';
import { useTranslation } from '../i18n/client';
import { cookieName } from '../i18n/settings';
import { postFCMToken } from '@/apis/notifications';
import { useDidMount } from '@/hooks/common/useDidMount';
import useAppRouter from '@/hooks/useAppRouter';
import { hasToken } from '@/utils/auth/tokenController';
import { getLocalCookie, setLocalCookie } from '@/utils/cookieController';
import { afterDay60 } from '@/utils/date';
import { getIsApp } from '@/utils/getIsApp';

export default function Home() {
const { i18n } = useTranslation('common');
const { replace } = useAppRouter();
const isapp = getIsApp();

useDidMount(() => {
if (!isapp) return;
const listener = async (event: any) => {
const { data, type } = JSON.parse(event.data);
switch (type) {
case 'FCM_TOKEN':
postFCMToken({ token: data });
}
};

document.addEventListener('message', listener);
window.addEventListener('message', listener);
return () => {
document.removeEventListener('message', listener);
window.removeEventListener('message', listener);
};
});

useDidMount(async () => {
const cookieLanguage = getLocalCookie(cookieName);
Expand Down
11 changes: 4 additions & 7 deletions src/app/[lng]/template.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
'use client';
import { SENTRY_DSN } from '@/constants';
import useDisableScrollBounce from '@/hooks/useDisableScrollBounce';
import { useEasterEgg } from '@/hooks/useEasterEgg';
import useListenMessageToReactNative from '@/hooks/useListenMessageToReactNative';
import { useSentry } from '@/hooks/useSentry';

export default function Step4Layout({ children }: { children: React.ReactNode }) {
export default function Template({ children }: { children: React.ReactNode }) {
useEasterEgg();
useDisableScrollBounce();

useSentry({
dsn: SENTRY_DSN,
allowUrls: ['https://gloddy.vercel.app'],
});
useSentry();
useListenMessageToReactNative();

return children;
}
12 changes: 3 additions & 9 deletions src/components/Error/BaseError.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Flex } from '@/components/Layout';
import { Spacing } from '@/components/Spacing';
import { removeToken } from '@/utils/auth/tokenController';
import { useRouter } from 'next/navigation';
import useLogout from '@/hooks/token/useLogout';
import { useEffect } from 'react';

export interface BaseErrorProps {
Expand All @@ -10,16 +9,11 @@ export interface BaseErrorProps {
}

export default function BaseError({ error, reset }: BaseErrorProps) {
const router = useRouter();
const { logout } = useLogout();
useEffect(() => {
console.error(error);
}, [error]);

const handleLogOut = () => {
router.push('/join');
removeToken();
};

return (
<Flex align="center" justify="center" className="h-full text-sign-tertiary " direction="column">
<p className="text-30 flex h-48 w-48 items-center justify-center rounded-full bg-sign-caption text-white">
Expand All @@ -40,7 +34,7 @@ export default function BaseError({ error, reset }: BaseErrorProps) {
</button>
<button
className="bg-grey-200 text-13 mt-16 block rounded-lg px-12 py-8 font-bold text-red-300"
onClick={handleLogOut}
onClick={logout}
>
로그아웃하기
</button>
Expand Down
29 changes: 29 additions & 0 deletions src/hooks/token/useLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import sendMessageToReactNative from '../../utils/sendMessageToReactNative';
import useAppRouter from '../useAppRouter';
import { setTokenAtCookie } from '@/utils/auth/tokenController';

interface LoginProps {
accessToken: string;
refreshToken: string;
userId: number;
}
export default function useLogin() {
const { refresh, replace } = useAppRouter();

const login = async ({ accessToken, refreshToken, userId }: LoginProps) => {
await setTokenAtCookie({ accessToken, refreshToken, userId });
sendMessageToReactNative({ type: 'AUTH', data: 'LOG_IN' });
sendMessageToReactNative({
type: 'TOKEN',
data: {
accessToken,
refreshToken,
userId,
},
});
refresh();
replace('/grouping');
};

return { login };
}
15 changes: 15 additions & 0 deletions src/hooks/token/useLogout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { removeToken } from '@/utils/auth/tokenController';
import sendMessageToReactNative from '@/utils/sendMessageToReactNative';
import { useRouter } from 'next/navigation';

export default function useLogout() {
const router = useRouter();

const logout = () => {
router.push('/join');
removeToken();
sendMessageToReactNative({ type: 'AUTH', data: 'LOG_OUT' });
};

return { logout };
}
32 changes: 32 additions & 0 deletions src/hooks/useListenMessageToReactNative.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useDidMount } from './common/useDidMount';
import { postFCMToken } from '@/apis/notifications';
import { setTokenAtCookie } from '@/utils/auth/tokenController';
import { getIsApp } from '@/utils/getIsApp';

export default function useListenMessageToReactNative() {
const isapp = getIsApp();
useDidMount(() => {
if (!isapp) return;
const listener = async (event: any) => {
const { type, data } = JSON.parse(event.data);
alert('test');
switch (type) {
case 'FCM_TOKEN':
postFCMToken({ token: data });
break;
case 'TOKEN': {
const { accessToken, refreshToken, userId } = data;
setTokenAtCookie({ accessToken, refreshToken, userId });
break;
}
}
};

document.addEventListener('message', listener);
window.addEventListener('message', listener);
return () => {
document.removeEventListener('message', listener);
window.removeEventListener('message', listener);
};
});
}
Loading

0 comments on commit e02fd94

Please sign in to comment.