diff --git a/src/entities/auth/DTO.d.ts b/src/entities/auth/DTO.d.ts index 63f69a23..e5c32547 100644 --- a/src/entities/auth/DTO.d.ts +++ b/src/entities/auth/DTO.d.ts @@ -36,6 +36,7 @@ export interface TokenRefreshResponse { export interface ResetPasswordRequest { email: string; + code: string; } export interface ResetPasswordResponse { @@ -57,3 +58,8 @@ export interface AppleCallbackRequest { id_token: string; fcmToken?: string; } + +export interface SendEmailCertificationRequest { + email: string; + certificationType: "EMAIL" | "TEMPORARY_PASSWORD"; +} diff --git a/src/entities/auth/hooks/useEmailCertification.tsx b/src/entities/auth/hooks/useEmailCertification.tsx index ba411d9a..39ff2b8e 100644 --- a/src/entities/auth/hooks/useEmailCertification.tsx +++ b/src/entities/auth/hooks/useEmailCertification.tsx @@ -7,8 +7,13 @@ import { toast } from "@/shared/hooks/useToast"; * 이메일 인증 번호 발송 API 호출 */ export const useSendEmailCertification = () => { - return useMutation({ - mutationFn: (email) => sendEmailCertification(email), + return useMutation< + boolean, + Error, + { email: string; certificationType?: "EMAIL" | "TEMPORARY_PASSWORD" } + >({ + mutationFn: ({ email, certificationType }) => + sendEmailCertification(email, certificationType), onError: (error) => { let errorMessage = "잠시 후 다시 시도해주세요."; diff --git a/src/entities/user/api.ts b/src/entities/user/api.ts index 7001d49d..bc1c7495 100644 --- a/src/entities/user/api.ts +++ b/src/entities/user/api.ts @@ -1,6 +1,7 @@ import { getDefaultStore } from "jotai/vanilla"; import { getAccessToken, signOut } from "@/entities/auth/api"; +import { SendEmailCertificationRequest } from "@/entities/auth/DTO.d"; import { userAtom } from "@/entities/auth/model"; import { apiCall } from "@/shared/api/utils"; import { API_PATHS } from "@/shared/config/api"; @@ -195,16 +196,18 @@ export const updateUserRole = async ( /** * 이메일 인증 번호 발송 * @param email 인증 번호를 받을 이메일 + * @param certificationType 인증 타입 (EMAIL | TEMPORARY_PASSWORD) * @returns 발송 성공 여부 */ export const sendEmailCertification = async ( - email: string + email: string, + certificationType: "EMAIL" | "TEMPORARY_PASSWORD" = "EMAIL" ): Promise => { try { - await apiCall<{ email: string }, void>({ + await apiCall({ method: "POST", path: API_PATHS.USER.FIND_PASSWORD, - data: { email }, + data: { email, certificationType }, withCredentials: true, }); diff --git a/src/pages/auth/find-password.tsx b/src/pages/auth/find-password.tsx index 9cdf439c..a7648472 100644 --- a/src/pages/auth/find-password.tsx +++ b/src/pages/auth/find-password.tsx @@ -1,4 +1,3 @@ -import { useResetPassword } from "@/entities/auth/hooks"; import { URL_PATHS } from "@/shared/constants/url-path"; import useFormData from "@/shared/hooks/useFormdata"; import { useStepNavigation } from "@/shared/hooks/useStepNavigation"; @@ -34,8 +33,6 @@ const FIND_PASSWORD_STEP_CONFIGS = { }; export default function FindPasswordPage() { - const { mutate: resetPassword } = useResetPassword(); - const { currentStep: step, goToPreviousStep, @@ -57,16 +54,22 @@ export default function FindPasswordPage() { goToNextStep(); }, 2: () => { - resetPassword({ email: formData.email! }); + // 임시 비밀번호 발급 완료 }, }; const stepComponents = { - 1: , + 1: ( + + ), 2: ( ), }; diff --git a/src/widgets/auth/ui/EmailCertificationForm.tsx b/src/widgets/auth/ui/EmailCertificationForm.tsx index 99a70071..d3ab8090 100644 --- a/src/widgets/auth/ui/EmailCertificationForm.tsx +++ b/src/widgets/auth/ui/EmailCertificationForm.tsx @@ -2,7 +2,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import * as z from "zod"; -import { useCheckEmailCertification } from "@/entities/auth/hooks"; +import { useCheckEmailCertification, useResetPassword } from "@/entities/auth/hooks"; import CertificationField from "@/features/form/ui/fields/CertificationField"; import SubmitButton from "@/features/form/ui/SubmitButton"; import { Form } from "@/shared/ui/form"; @@ -15,14 +15,21 @@ export type EmailCertificationFormValues = z.infer; interface EmailCertificationFormProps { email: string; - onNext: () => void; + onNext: (certification?: string) => void; + mode?: "signup" | "findPassword"; } export function EmailCertificationForm({ email, onNext, + mode = "signup", }: EmailCertificationFormProps) { - const { mutate: checkEmail, isPending } = useCheckEmailCertification(); + const { mutate: checkEmail, isPending: isCheckPending } = + useCheckEmailCertification(); + const { mutate: resetPassword, isPending: isResetPending } = + useResetPassword(); + + const isPending = isCheckPending || isResetPending; const form = useForm({ resolver: zodResolver(step2Schema), @@ -31,14 +38,27 @@ export function EmailCertificationForm({ }); const onSubmit = (data: EmailCertificationFormValues) => { - checkEmail( - { email, certification: data.certification }, - { - onSuccess: (success: boolean) => { - if (success) onNext(); - }, - } - ); + if (mode === "findPassword") { + // 비밀번호 찾기: 임시 비밀번호 발급 API 호출 + resetPassword( + { email, code: data.certification }, + { + onSuccess: () => { + onNext(data.certification); + }, + } + ); + } else { + // 회원가입: 인증번호 검증만 + checkEmail( + { email, certification: data.certification }, + { + onSuccess: (success: boolean) => { + if (success) onNext(data.certification); + }, + } + ); + } }; return ( diff --git a/src/widgets/auth/ui/EmailForm.tsx b/src/widgets/auth/ui/EmailForm.tsx index d5a03beb..796efab1 100644 --- a/src/widgets/auth/ui/EmailForm.tsx +++ b/src/widgets/auth/ui/EmailForm.tsx @@ -13,11 +13,15 @@ const step1Schema = z.object({ export type EmailFormValues = z.infer; +interface EmailFormProps { + onNext: (data: EmailFormValues) => void; + certificationType?: "EMAIL" | "TEMPORARY_PASSWORD"; +} + export function EmailForm({ onNext, -}: { - onNext: (data: EmailFormValues) => void; -}) { + certificationType = "EMAIL", +}: EmailFormProps) { const { mutate: sendEmail, isPending } = useSendEmailCertification(); const form = useForm({ @@ -27,11 +31,14 @@ export function EmailForm({ }); const onSubmit = (data: EmailFormValues) => { - sendEmail(data.email, { - onSuccess: (success: boolean) => { - if (success) onNext(data); - }, - }); + sendEmail( + { email: data.email, certificationType }, + { + onSuccess: (success: boolean) => { + if (success) onNext(data); + }, + } + ); }; return (