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

reactor: 회원가입 폼 리팩토링 및 유효성 검사 추가 #42

Merged
merged 1 commit into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/api/authAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import api from "@/lib/api";

export const signup = async (data: { email: string; nickname: string; password: string }) => {
const response = await api.post("/user", {
email: data.email,
name: data.nickname,
password: data.password,
});
return response.data;
};
69 changes: 28 additions & 41 deletions src/app/(auth)/components/SignupForm.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use client";

import { toast } from "react-toastify";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import axios from "axios";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

import { useSignup } from "@/hook/useSignup";

import SubmitButton from "./SubmitButton";
import InputField from "./InputField";

Expand All @@ -16,67 +16,54 @@ type FormFields = "nickname" | "email" | "password" | "passwordConfirm";
type FormData = z.infer<typeof signupSchema>;

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

const {
register,
handleSubmit,
formState: { errors, isSubmitting },
trigger,
setError,
clearErrors,
reset,
watch,
} = useForm<FormData>({
resolver: zodResolver(signupSchema),
mode: "onChange", // 실시간 유효성 검사를 위해 추가
});

useEffect(() => {
const firstInput = document.getElementById("nickname") as HTMLInputElement;
if (firstInput) {
firstInput.focus();
}
}, []);
const signupMutation = useSignup();

const onSubmit = async (data: FormData) => {
try {
const response = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/user`, {
email: data.email,
name: data.nickname,
password: data.password,
});

if (response.data) {
clearErrors();
reset();
toast.success("회원가입이 완료되었습니다.");
router.push("/login");
}
} catch (error) {
console.error("회원가입 서버 오류🚨", error);
signupMutation.mutate(data, {
onError: (error) => {
console.error("회원가입 서버 오류🚨", error);

if (axios.isAxiosError(error) && error.response) {
const errorCode = error.response.status;
const errorInfo = errorMessage[errorCode];
if (axios.isAxiosError(error) && error.response) {
const errorCode = error.response.status;
const errorInfo = errorMessage[errorCode];

if (errorCode) {
setError(errorInfo.field, {
type: "manual",
message: errorInfo.message,
});
if (errorInfo) {
setError(errorInfo.field, {
type: "manual",
message: errorInfo.message,
});
}
}
}
}
},
});
};

useEffect(() => {
const firstInput = document.getElementById("nickname") as HTMLInputElement;
if (firstInput) {
firstInput.focus();
}
}, []);

return (
<form className="flex flex-col justify-center" aria-label="회원가입 양식" onSubmit={handleSubmit(onSubmit)}>
<InputField
id="nickname"
label="닉네임"
type="text"
placeholder="닉네임을 입력해 주세요."
placeholder="별명(2~20자)"
register={register}
errors={errors}
trigger={trigger}
Expand All @@ -86,7 +73,7 @@ export default function SignupForm() {
id="email"
label="아이디"
type="email"
placeholder="이메일을 입력해 주세요."
placeholder="이메일"
register={register}
errors={errors}
trigger={trigger}
Expand All @@ -96,7 +83,7 @@ export default function SignupForm() {
id="password"
label="비밀번호"
type="password"
placeholder="비밀번호를 입력해 주세요."
placeholder="비밀번호"
register={register}
errors={errors}
trigger={trigger}
Expand All @@ -106,7 +93,7 @@ export default function SignupForm() {
id="passwordConfirm"
label="비밀번호 확인"
type="password"
placeholder="비밀번호를 입력해 주세요."
placeholder="비밀번호 확인"
register={register}
errors={errors}
trigger={trigger}
Expand Down
4 changes: 2 additions & 2 deletions src/app/(auth)/components/SignupFormContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default function SignupFormContainer() {
<section aria-labelledby="signup-heading" className="flex items-center justify-center">
<Image alt="브랜드" width={106} height={35} src="/brand.webp" className="absolute left-4 top-4" priority />
<div className="w-[343px] rounded-2xl bg-white px-8 py-6 sm:w-[510px] sm:rounded-3xl sm:px-14 sm:py-8 md:mx-[102px]">
<h2 id="signup-heading" className="pb-8 text-center text-[20px] font-bold text-black">
<h1 id="signup-heading" className="pb-8 text-center text-xl font-bold text-black">
회원가입
</h2>
</h1>
<SignupForm />
<LoginPrompt />
</div>
Expand Down
22 changes: 22 additions & 0 deletions src/hook/useSignup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useRouter } from "next/navigation";
import { toast } from "react-toastify";
import { useMutation } from "@tanstack/react-query";

import { signup } from "@/api/authAPI";
import { ErrorType } from "@/api/goalAPI";

export const useSignup = () => {
const router = useRouter();
console.log("1");

return useMutation({
mutationFn: signup,
onSuccess: () => {
toast.success("회원가입이 완료되었습니다.");
router.push("/login");
},
onError: (error: ErrorType) => {
throw error;
},
});
};
Loading