You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

195 lines
6.3 KiB

"use client";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/lib/api";
import { zodResolver } from "@hookform/resolvers/zod";
import axios from "axios";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
import z, { email } from "zod";
const SignupSchema = z
.object({
name: z.string().min(1, "아이디를 입력하세요."),
password: z.string().min(4, "비밀번호는 4자 이상이어야 합니다."),
confirmPassword: z.string().min(1, "비밀번호 확인을 입력하세요."),
email: z.email("이메일 형식이 아닙니다.").optional(),
})
.refine((v) => v.password === v.confirmPassword, {
path: ["confirmPassword"],
message: "비밀번호가 일치하지 않습니다.",
});
type SignupInput = z.infer<typeof SignupSchema>;
export default function SignupPage() {
const form = useForm<SignupInput>({
resolver: zodResolver(SignupSchema),
defaultValues: {
name: "",
password: "",
confirmPassword: "",
email: undefined,
},
mode: "onTouched",
});
const [error, setError] = useState<string | null>(null);
const [ok, setOk] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const router = useRouter();
const onSubmit = async (values: SignupInput) => {
setError(null);
setOk(null);
setLoading(true);
try {
const payload: Record<string, unknown> = {
name: values.name.trim(),
password: values.password,
};
const emailTrim = values.email?.trim();
if (emailTrim) payload.email = emailTrim;
await api.post("/auth/signup", payload);
setOk("회원가입이 완료되어습니다. 로그인 페이지로 이동합니다.");
setTimeout(() => router.replace("/login"), 800);
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
setError(
e.request?.data?.message ?? e.message ?? "회원가입에 실패했습니다."
);
} else if (e instanceof Error) {
setError(e.message);
} else {
setError("회원가입에 실패했습니다.");
}
} finally {
setLoading(false);
}
};
return (
<div className="flex items-center justify-center min-h-screen">
<div className="mx-auto w-full max-w-sm">
<Card className="shadow-lg">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="flex flex-col gap-y-2 pb-4">
<Form {...form}>
<form
className="space-y-4"
onSubmit={form.handleSubmit(onSubmit)}
>
{/* name */}
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
placeholder="아이디"
autoComplete="username"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* email */}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
placeholder="이메일(선택)"
type="email"
autoComplete="email"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* password */}
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
placeholder="비밀번호"
type="password"
autoComplete="new-password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* confirmPassword */}
<FormField
control={form.control}
name="confirmPassword"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
placeholder="비밀번호 확인"
type="password"
autoComplete="new-password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full" disabled={loading}>
{loading ? "처리 중..." : "가입하기"}
</Button>
{error && (
<p className="text-center text-sm text-red-600">{error}</p>
)}
{ok && (
<p className="text-center text-sm text-green-600">{ok}</p>
)}
<p className="text-center text-xs text-gray-500">
?{" "}
<Link href="/login" className="underline">
</Link>
</p>
</form>
</Form>
</CardContent>
</Card>
</div>
</div>
);
}