register api

This commit is contained in:
2025-11-24 22:14:46 +09:00
parent 071a686ffa
commit 74eee0a3c0
2 changed files with 155 additions and 35 deletions

View File

@@ -1,12 +1,12 @@
"use client";
import { useMemo, useState } from "react";
import { useMemo, useState, useEffect } from "react";
import Link from "next/link";
import LoginCheckboxActiveSvg from "@/app/svgs/logincheckboxactivesvg";
import LoginCheckboxInactiveSvg from "@/app/svgs/logincheckboxinactivesvg";
import LoginInputSvg from "@/app/svgs/inputformx";
type Gender = "male" | "female" | "";
type Gender = "MALE" | "FEMALE" | "";
type RegisterFormProps = {
onOpenDone: () => void;
@@ -73,10 +73,129 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
return Object.keys(nextErrors).length === 0;
}
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
// 입력 필드가 유효해지면 해당 필드의 에러를 자동으로 지움
useEffect(() => {
setErrors((prev) => {
const next = { ...prev };
if (name.trim().length > 0 && prev.name) delete next.name;
if (isPhoneValid && prev.phone) delete next.phone;
if (isEmailValid && prev.email) delete next.email;
if (isPasswordValid && prev.password) delete next.password;
if (isPasswordConfirmValid && prev.passwordConfirm) delete next.passwordConfirm;
if (gender !== "" && prev.gender) delete next.gender;
if (birthdate.trim().length > 0 && prev.birthdate) delete next.birthdate;
if (allAgree && prev.agreements) delete next.agreements;
return next;
});
}, [name, isPhoneValid, isEmailValid, isPasswordValid, isPasswordConfirmValid, gender, birthdate, allAgree]);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
console.log("handleSubmit");
e.preventDefault();
if (!validateAll()) return;
onOpenDone();
await RegisterUser();
}
async function verifyEmailCode() {
try{
const response = await fetch(
"https://hrdi.coconutmeet.net/auth/verify-email/confirm",
{
method: "POST", headers: {"Content-Type": "application/json",},
body: JSON.stringify({email: email,emailCode: emailCode})
});
if (!response.ok) {
console.error("이메일 인증번호 검증 실패:", response.statusText);
onOpenCodeError();
return;
}
// 인증 성공 시 상태 업데이트
setEmailCodeVerified(true);
}
catch(error){
console.error("이메일 인증번호 검증 오류:", error);
onOpenCodeError();
}
}
async function sendEmailCode() {
if (!isEmailValid) return;
// INSERT_YOUR_CODE
try {
const response = await fetch(
"https://hrdi.coconutmeet.net/auth/verify-email/send",
{
method: "POST",
headers: {"Content-Type": "application/json",},
body: JSON.stringify({email: email})
}
);
if (!response.ok) {
console.error("이메일 인증번호 전송 실패:", response.statusText);
alert("인증번호 전송실패");
return;
}
// 성공 시에만 상태 업데이트
setEmailCodeSent(true);
setEmailCode("");
setEmailCodeVerified(false);
} catch (error) {
console.error("이메일 인증번호 전송 오류:", error);
alert("인증번호 전송실패");
return;
}
}
async function RegisterUser() {
if (!emailCodeVerified) {
onOpenCodeError("이메일 인증을 완료해주세요.");
return false;
}
try {
const response = await fetch("https://hrdi.coconutmeet.net/auth/signup", {
method: "POST",
headers: {"Content-Type": "application/json",},
body: JSON.stringify({
email: email,
emailCode: emailCode,
password: password,
passwordConfirm: passwordConfirm,
name: name,
phone: phone,
gender: gender,
birthDate: birthdate
})
});
if (!response.ok) {
let errorMessage = `회원가입 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
} else if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
} catch (parseError) {
if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
}
console.error("회원가입 실패:", errorMessage);
onOpenCodeError(errorMessage);
return false;
}
onOpenDone();
return true;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "네트워크 오류가 발생했습니다.";
console.error("회원가입 오류:", errorMessage);
onOpenCodeError(errorMessage);
return false;
}
}
return (
@@ -87,7 +206,6 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</div>
</div>
<form onSubmit={handleSubmit} className="space-y-6">
{/* 이름 */}
<div className="space-y-2">
@@ -161,9 +279,10 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onFocus={() => setFocused((p) => ({ ...p, email: true }))}
onBlur={() => setFocused((p) => ({ ...p, email: false }))}
placeholder="이메일을 입력해 주세요."
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]"
disabled={emailCodeVerified}
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] disabled:bg-gray-100 disabled:cursor-not-allowed"
/>
{email.trim().length > 0 && focused.email && (
{email.trim().length > 0 && focused.email && !emailCodeVerified && (
<button
type="button"
onMouseDown={(e) => { e.preventDefault(); setEmail(""); }}
@@ -176,15 +295,9 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
</div>
<button
type="button"
disabled={!isEmailValid}
className={`h-[40px] px-[12px] rounded-[8px] text-[16px] font-semibold ${isEmailValid ? "bg-inactive-button text-white" : "bg-gray-50 text-input-placeholder-text"}`}
onClick={() => {
if (!isEmailValid) return;
alert("인증번호 전송 (가상 동작)");
setEmailCodeSent(true);
setEmailCode("");
setEmailCodeVerified(false);
}}
disabled={!isEmailValid || emailCodeVerified}
className={`h-[40px] px-[12px] rounded-[8px] text-[16px] font-semibold ${isEmailValid && !emailCodeVerified ? "bg-inactive-button text-white" : "bg-gray-50 text-input-placeholder-text"}`}
onClick={sendEmailCode}
>
</button>
@@ -207,7 +320,8 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
onFocus={() => setFocused((p) => ({ ...p, emailCode: true }))}
onBlur={() => setFocused((p) => ({ ...p, emailCode: false }))}
placeholder="인증번호 6자리"
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px]"
disabled={emailCodeVerified}
className="h-[40px] px-[12px] py-[7px] w-full rounded-[8px] border border-neutral-40 focus:outline-none focus:border-neutral-700 text-[18px] text-neutral-700 placeholder:text-input-placeholder-text pr-[40px] disabled:bg-gray-100 disabled:cursor-not-allowed"
/>
{emailCode.trim().length > 0 && focused.emailCode && !emailCodeVerified && (
<button
@@ -224,18 +338,22 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
type="button"
disabled={emailCodeVerified}
className={`h-[40px] px-[12px] rounded-[8px] text-[16px] font-semibold ${!emailCodeVerified ? "bg-active-button text-white" : "bg-gray-50 text-input-placeholder-text"}`}
onClick={() => {
// 가상 검증: 6자리면 성공, 아니면 에러 모달
if (emailCode.length !== 6) {
onOpenCodeError();
return;
}
setEmailCodeVerified(true);
}}
onClick={verifyEmailCode}
>
{emailCodeVerified ? "인증완료" : "인증하기"}
</button>
</div>
<p className="text-[13px] leading-[normal] text-[#384fbf]">
{emailCodeVerified ? (
"인증이 완료됐습니다"
) : (
<>
.
<br />
.
</>
)}
</p>
</div>
)}
</div>
@@ -306,13 +424,13 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
<input
type="radio"
name="gender"
value="male"
checked={gender === "male"}
onChange={() => setGender("male")}
value="MALE"
checked={gender === "MALE"}
onChange={() => setGender("MALE")}
className="sr-only"
/>
<span className={`inline-block rounded-full size-[18px] border ${gender === "male" ? "border-active-button" : "border-[#8c95a1]"}`}>
{gender === "male" && <span className="block size-[9px] rounded-full bg-active-button m-[4.5px]" />}
<span className={`inline-block rounded-full size-[18px] border ${gender === "MALE" ? "border-active-button" : "border-[#8c95a1]"}`}>
{gender === "MALE" && <span className="block size-[9px] rounded-full bg-active-button m-[4.5px]" />}
</span>
</label>
@@ -320,13 +438,13 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
<input
type="radio"
name="gender"
value="female"
checked={gender === "female"}
onChange={() => setGender("female")}
value="FEMALE"
checked={gender === "FEMALE"}
onChange={() => setGender("FEMALE")}
className="sr-only"
/>
<span className={`inline-block rounded-full size-[18px] border ${gender === "female" ? "border-active-button" : "border-[#8c95a1]"}`}>
{gender === "female" && <span className="block size-[9px] rounded-full bg-active-button m-[4.5px]" />}
<span className={`inline-block rounded-full size-[18px] border ${gender === "FEMALE" ? "border-active-button" : "border-[#8c95a1]"}`}>
{gender === "FEMALE" && <span className="block size-[9px] rounded-full bg-active-button m-[4.5px]" />}
</span>
</label>

View File

@@ -14,6 +14,7 @@ export default function RegisterPage() {
return (
<>
<div className="min-h-screen w-full flex flex-col items-center justify-between">
<RegisterForm
onOpenDone={() => setDoneOpen(true)}
onOpenCodeError={(msg) => {
@@ -21,6 +22,7 @@ export default function RegisterPage() {
setCodeErrorOpen(true);
}}
/>
<RegisterOption
doneOpen={doneOpen}
setDoneOpen={setDoneOpen}