Skip to content

Commit

Permalink
refactor : useMutation으로 api 호출 방식 통일 (#299)
Browse files Browse the repository at this point in the history
* feat: mutation을 사용하여 훅 분리

* feat: onSubmit에 mutateAsync적용

* feat: formState로 중복 제출 방지

* fix: hook 분리

* fix: 비밀번호 재설정 훅의 api 호출 패턴 통일

* fix: postSignUpMutate 적용

* fix: isPending을 통해 버튼 disabled
  • Loading branch information
jonique98 authored Sep 22, 2024
1 parent 079d92e commit d525291
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 155 deletions.
50 changes: 0 additions & 50 deletions src/home/apis/authVerification.ts

This file was deleted.

16 changes: 16 additions & 0 deletions src/home/apis/getAuthVerificationCheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { authClient } from '@/apis';

import { GetAuthVerificationCheckData } from '../types/Auth.type';

interface VerificationCheckParams {
session: string;
}

export const getAuthVerificationCheck = async (
verificationCheckParams: VerificationCheckParams
): Promise<GetAuthVerificationCheckData> => {
const res = await authClient.get('/auth/verification/check', {
params: verificationCheckParams,
});
return res.data;
};
15 changes: 4 additions & 11 deletions src/home/apis/postAuthSignUp.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { AxiosError } from 'axios';

import { authClient } from '@/apis';

import { AuthErrorData, PostAuthResponse } from '../types/Auth.type';
import { PostAuthSignInData } from '@/home/types/Auth.type';

interface SignUpParams {
email: string;
Expand All @@ -11,11 +8,7 @@ interface SignUpParams {
sessionToken: string;
}

export const postAuthSignUp = async (signUpParams: SignUpParams): Promise<PostAuthResponse> => {
try {
const res = await authClient.post(`/auth/sign-up`, signUpParams);
return { data: res.data };
} catch (error: unknown) {
return { error: error as AxiosError<AuthErrorData> };
}
export const postAuthSignUp = async (signUpParams: SignUpParams): Promise<PostAuthSignInData> => {
const res = await authClient.post(`/auth/sign-up`, signUpParams);
return res.data;
};
15 changes: 15 additions & 0 deletions src/home/apis/postAuthVerificationEmail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { authClient } from '@/apis';

import { PostAuthVerificationEmailData } from '../types/Auth.type';

interface EmailVerificationParams {
email: string;
verificationType: 'SIGN_UP' | 'PASSWORD';
}

export const postAuthVerificationEmail = async (
emailVerificationParams: EmailVerificationParams
): Promise<PostAuthVerificationEmailData> => {
const res = await authClient.post(`/auth/verification/email`, emailVerificationParams);
return res.data;
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface EmailInputProps {
}

export const EmailInput = ({ email, onConfirm }: EmailInputProps) => {
const { register, handleSubmit, errors, isSubmitting, handleOnSubmit } = useEmailInputForm({
const { register, handleSubmit, errors, handleOnSubmit, isPending } = useEmailInputForm({
email,
onConfirm,
});
Expand Down Expand Up @@ -41,9 +41,9 @@ export const EmailInput = ({ email, onConfirm }: EmailInputProps) => {
variant="filled"
rounding={8}
onClick={handleSubmit(handleOnSubmit)}
disabled={isSubmitting}
disabled={isPending}
>
{isSubmitting ? '잠시만 기다려주세요...' : '재설정 메일 보내기'}
{isPending ? '잠시만 기다려주세요...' : '재설정 메일 보내기'}
</BoxButton>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useForm } from 'react-hook-form';

import { postAuthVerificationEmail } from '@/home/apis/authVerification';
import { usePostAuthVerificationEmail } from '@/home/hooks/usePostAuthVerificationEmail';
import { useParseFullEmail } from '@/hooks/useParseFullEmail';

interface EmailInputProps {
Expand All @@ -17,7 +17,7 @@ export const useEmailInputForm = ({ email, onConfirm }: EmailInputProps) => {
register,
handleSubmit,
watch,
formState: { errors, isSubmitting },
formState: { errors },
setError,
} = useForm<FormData>({
defaultValues: { email },
Expand All @@ -26,25 +26,32 @@ export const useEmailInputForm = ({ email, onConfirm }: EmailInputProps) => {
const localEmail = watch('email');
const parseFullEmail = useParseFullEmail();
const fullEmail = parseFullEmail(localEmail);
const postAuthVerificationEmailMutation = usePostAuthVerificationEmail();

const handleOnSubmit = async (data: FormData) => {
const response = await postAuthVerificationEmail({
email: fullEmail,
verificationType: 'PASSWORD',
});

if (response.error) {
setError('email', { type: 'manual', message: '존재하지 않는 이메일입니다.' });
} else {
onConfirm(data.email);
}
postAuthVerificationEmailMutation.mutate(
{ email: fullEmail, verificationType: 'PASSWORD' },
{
onSuccess: () => {
onConfirm(data.email);
},
onError: () => {
setError('email', {
type: 'manual',
message: '존재하지 않는 이메일입니다.',
});
},
}
);
};

const isPending = postAuthVerificationEmailMutation.isPending;

return {
register,
handleSubmit,
errors,
isSubmitting,
handleOnSubmit,
isPending,
};
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useState, useEffect } from 'react';

import { STORAGE_KEYS } from '@/constants/storage.constant';
import { getAuthVerificationCheck, postAuthVerificationEmail } from '@/home/apis/authVerification';
import { useGetAuthVerificationCheck } from '@/home/hooks/useGetAuthVerificationCheck';
import { usePostAuthVerificationEmail } from '@/home/hooks/usePostAuthVerificationEmail';
import { useParseFullEmail } from '@/hooks/useParseFullEmail';
import { useSecondTimer } from '@/hooks/useSecondTimer';

Expand All @@ -18,6 +19,9 @@ export const useEmailVerification = ({ email, onConfirm }: UseEmailVerificationP
const [error, setError] = useState<string | null>(null);
const [isResending, setIsResending] = useState(false);

const getAuthVerificationCheckMutation = useGetAuthVerificationCheck();
const postAuthVerificationEmailMutation = usePostAuthVerificationEmail();

useEffect(() => {
const session = sessionStorage.getItem(STORAGE_KEYS.EMAIL_AUTH_SESSION_TOKEN);
if (session) {
Expand All @@ -26,13 +30,22 @@ export const useEmailVerification = ({ email, onConfirm }: UseEmailVerificationP
}, []);

const verifyEmailSession = async (session: string, isInitialCheck: boolean = false) => {
const { data, error } = await getAuthVerificationCheck({ session });
if (data?.isVerified) {
setError(null);
onConfirm();
} else if (!isInitialCheck || error) {
setError('이메일 인증을 완료해주세요.');
}
getAuthVerificationCheckMutation.mutate(
{ session },
{
onSuccess: (data) => {
if (data.isVerified) {
setError(null);
onConfirm();
} else if (!isInitialCheck) {
setError('이메일 인증을 완료해주세요.');
}
},
onError: () => {
setError('이메일 인증을 완료해주세요.');
},
}
);
};

const handleVerification = async () => {
Expand All @@ -58,15 +71,20 @@ export const useEmailVerification = ({ email, onConfirm }: UseEmailVerificationP
setIsResending(true);
resetTimer();

const response = await postAuthVerificationEmail({
email: fullEmail,
verificationType: 'PASSWORD',
});

if (response.error) {
setError('이메일 인증을 완료해주세요.');
}
setIsResending(false);
postAuthVerificationEmailMutation.mutate(
{ email: fullEmail, verificationType: 'PASSWORD' },
{
onSuccess: () => {
setError(null);
},
onError: () => {
setError('인증 메일 재전송에 실패했습니다.');
},
onSettled: () => {
setIsResending(false);
},
}
);
};

return {
Expand Down
57 changes: 38 additions & 19 deletions src/home/components/SignupContents/EmailAuth/useEmailAuth.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,59 @@
import { useState } from 'react';

import { AxiosError } from 'axios';

import { STORAGE_KEYS } from '@/constants/storage.constant.ts';
import {
getAuthVerificationCheck,
postAuthVerificationEmail,
} from '@/home/apis/authVerification.ts';
import { EmailAuthProps } from '@/home/components/SignupContents/EmailAuth/EmailAuth.type.ts';
import { EmailAuthProps } from '@/home/components/SignupContents/EmailAuth/EmailAuth.type';
import { useGetAuthVerificationCheck } from '@/home/hooks/useGetAuthVerificationCheck';
import { usePostAuthVerificationEmail } from '@/home/hooks/usePostAuthVerificationEmail';
import { AuthErrorData } from '@/home/types/Auth.type';
import { useSecondTimer } from '@/hooks/useSecondTimer.ts';

export const useEmailAuth = ({ onConfirm, email }: EmailAuthProps) => {
const [authed, setAuthed] = useState(true);
const { leftTime, isTimerEnd, resetTimer } = useSecondTimer(8 * 60);
const { leftTime, isTimerEnd, resetTimer } = useSecondTimer(10);
const [error, setError] = useState<string>('');
const [emailSending, setEmailSending] = useState(false);

const postAuthVerificationEmailMutation = usePostAuthVerificationEmail();
const getAuthVerificationCheckMutation = useGetAuthVerificationCheck();

const sendAuthenticationMail = async () => {
setEmailSending(true);
const { data } = await postAuthVerificationEmail({ email: email, verificationType: 'SIGN_UP' });
setEmailSending(false);
setAuthed(!!data);
setError(!data ? '인증 메일 재전송에 실패했습니다.' : '');
resetTimer();
postAuthVerificationEmailMutation.mutate(
{ email: email, verificationType: 'SIGN_UP' },
{
onSuccess: () => {
setAuthed(true);
},
onError: () => {
setAuthed(false);
setError('인증 메일 재전송에 실패했습니다.');
},
onSettled: () => {
setEmailSending(false);
resetTimer();
},
}
);
};

const onClickNext = async () => {
const session = sessionStorage.getItem(STORAGE_KEYS.EMAIL_AUTH_SESSION_TOKEN);
if (!session) return;

const res = await getAuthVerificationCheck({ session });

if (res.data) {
if (res.data.isVerified) onConfirm();
else setError('이메일 인증을 완료해주세요.');
} else if (res.error) {
setError(res.error.response?.data.message || '이메일 인증 확인에 실패했습니다.');
}
getAuthVerificationCheckMutation.mutate(
{ session: session },
{
onSuccess: (data) => {
if (data.isVerified) onConfirm();
else setError('이메일 인증을 완료해주세요.');
},
onError: (error: AxiosError<AuthErrorData>) => {
setError(error.response?.data.message || '인증에 실패했습니다.');
},
}
);
};

return { authed, leftTime, isTimerEnd, error, emailSending, sendAuthenticationMail, onClickNext };
Expand Down
24 changes: 11 additions & 13 deletions src/home/components/SignupContents/EmailForm/EmailForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BoxButton, PlainButton, SuffixTextField } from '@yourssu/design-system-react';
import { useForm } from 'react-hook-form';

import { EMAIL_DOMAIN, MAIL_SEARCH_URL } from '@/constants/email.constant';
import { EmailFormProps } from '@/home/components/SignupContents/EmailForm/EmailForm.type';
import { useEmailForm } from '@/home/components/SignupContents/EmailForm/useEmailForm';
import { usePreventDuplicateClick } from '@/hooks/usePreventDuplicateClick';
import { EmailFormProps } from '@/home/components/SignupContents/EmailForm/EmailForm.type.ts';
import { useEmailForm } from '@/home/components/SignupContents/EmailForm/useEmailForm.ts';

import {
StyledSignupButtonText,
Expand All @@ -20,16 +20,14 @@ import {
} from './EmailForm.style';

export const EmailForm = ({ onConfirm }: EmailFormProps) => {
const { email, emailError, onEmailSubmit, onChange } = useEmailForm({ onConfirm });
const { disabled, handleClick } = usePreventDuplicateClick();
const { email, emailError, onEmailSubmit, onChange, isPending } = useEmailForm({ onConfirm });
const {
handleSubmit,
formState: { isSubmitting },
} = useForm();

return (
<StyledSignupContentContainer
onSubmit={async (e) => {
e.preventDefault();
await handleClick(onEmailSubmit);
}}
>
<StyledSignupContentContainer onSubmit={handleSubmit(onEmailSubmit)}>
<StyledSignupContentTitle>회원가입</StyledSignupContentTitle>
<div>
<StyledTextFieldLabel>숭실대학교 메일을 입력해주세요.</StyledTextFieldLabel>
Expand Down Expand Up @@ -60,10 +58,10 @@ export const EmailForm = ({ onConfirm }: EmailFormProps) => {
size="large"
variant="filled"
rounding={8}
disabled={email === '' || disabled}
disabled={email === '' || isPending}
>
<StyledSignupButtonText>
{disabled ? '잠시만 기다려주세요...' : '인증 메일 받기'}
{isSubmitting ? '잠시만 기다려주세요...' : '인증 메일 받기'}
</StyledSignupButtonText>
</BoxButton>
</StyledButtonsContainer>
Expand Down
Loading

0 comments on commit d525291

Please sign in to comment.