가입폼
This commit is contained in:
@@ -1,12 +1,7 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
import { registerSchema } from "@/lib/validation/auth";
|
import { registerSchema } from "@/lib/validation/auth";
|
||||||
import { hash } from "crypto";
|
import { hashPassword } from "@/lib/password";
|
||||||
|
|
||||||
function hashPassword(pw: string) {
|
|
||||||
// 간이 해시(데모): 실제론 bcrypt/scrypt/argon2 사용 권장
|
|
||||||
return hash("sha256", Buffer.from(pw)).digest("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ export default function LoginPage() {
|
|||||||
style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }}
|
style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }}
|
||||||
/>
|
/>
|
||||||
<Button type="submit" disabled={loading}>{loading ? "로그인 중..." : "로그인"}</Button>
|
<Button type="submit" disabled={loading}>{loading ? "로그인 중..." : "로그인"}</Button>
|
||||||
|
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||||
|
<a href="/register">회원가입</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
60
src/app/register/page.tsx
Normal file
60
src/app/register/page.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"use client";
|
||||||
|
import React from "react";
|
||||||
|
import { Button } from "@/app/components/ui/Button";
|
||||||
|
import { useToast } from "@/app/components/ui/ToastProvider";
|
||||||
|
|
||||||
|
export default function RegisterPage() {
|
||||||
|
const { show } = useToast();
|
||||||
|
const [form, setForm] = React.useState({
|
||||||
|
nickname: "",
|
||||||
|
name: "",
|
||||||
|
phone: "",
|
||||||
|
birth: "",
|
||||||
|
password: "",
|
||||||
|
confirmPassword: "",
|
||||||
|
agreeTerms: false,
|
||||||
|
});
|
||||||
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { name, value, type, checked } = e.target;
|
||||||
|
setForm((f) => ({ ...f, [name]: type === "checkbox" ? checked : value }));
|
||||||
|
};
|
||||||
|
const onSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/auth/register", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(form),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok) throw new Error(data?.error || "회원가입 실패");
|
||||||
|
show("회원가입 성공! 로그인해주세요");
|
||||||
|
location.href = "/login";
|
||||||
|
} catch (err: any) {
|
||||||
|
show(err.message || "회원가입 실패");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div style={{ maxWidth: 480, margin: "40px auto" }}>
|
||||||
|
<h1>회원가입</h1>
|
||||||
|
<form onSubmit={onSubmit} style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
||||||
|
<input name="nickname" placeholder="닉네임" value={form.nickname} onChange={onChange} style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }} />
|
||||||
|
<input name="name" placeholder="이름" value={form.name} onChange={onChange} style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }} />
|
||||||
|
<input name="phone" placeholder="전화번호" value={form.phone} onChange={onChange} style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }} />
|
||||||
|
<input name="birth" placeholder="생년월일 (YYYY-MM-DD)" value={form.birth} onChange={onChange} style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }} />
|
||||||
|
<input name="password" placeholder="비밀번호" type="password" value={form.password} onChange={onChange} style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }} />
|
||||||
|
<input name="confirmPassword" placeholder="비밀번호 확인" type="password" value={form.confirmPassword} onChange={onChange} style={{ padding: 8, border: "1px solid #ddd", borderRadius: 6 }} />
|
||||||
|
<label style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||||
|
<input name="agreeTerms" type="checkbox" checked={form.agreeTerms} onChange={onChange} /> 약관에 동의합니다
|
||||||
|
</label>
|
||||||
|
<Button type="submit" disabled={loading}>{loading ? "가입 중..." : "가입하기"}</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user