register api
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState, useEffect } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import LoginCheckboxActiveSvg from "@/app/svgs/logincheckboxactivesvg";
|
import LoginCheckboxActiveSvg from "@/app/svgs/logincheckboxactivesvg";
|
||||||
import LoginCheckboxInactiveSvg from "@/app/svgs/logincheckboxinactivesvg";
|
import LoginCheckboxInactiveSvg from "@/app/svgs/logincheckboxinactivesvg";
|
||||||
import LoginInputSvg from "@/app/svgs/inputformx";
|
import LoginInputSvg from "@/app/svgs/inputformx";
|
||||||
|
|
||||||
type Gender = "male" | "female" | "";
|
type Gender = "MALE" | "FEMALE" | "";
|
||||||
|
|
||||||
type RegisterFormProps = {
|
type RegisterFormProps = {
|
||||||
onOpenDone: () => void;
|
onOpenDone: () => void;
|
||||||
@@ -73,10 +73,129 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
return Object.keys(nextErrors).length === 0;
|
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();
|
e.preventDefault();
|
||||||
if (!validateAll()) return;
|
if (!validateAll()) return;
|
||||||
|
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();
|
onOpenDone();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : "네트워크 오류가 발생했습니다.";
|
||||||
|
console.error("회원가입 오류:", errorMessage);
|
||||||
|
onOpenCodeError(errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -87,7 +206,6 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
회원가입
|
회원가입
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
{/* 이름 */}
|
{/* 이름 */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -161,9 +279,10 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
onFocus={() => setFocused((p) => ({ ...p, email: true }))}
|
onFocus={() => setFocused((p) => ({ ...p, email: true }))}
|
||||||
onBlur={() => setFocused((p) => ({ ...p, email: false }))}
|
onBlur={() => setFocused((p) => ({ ...p, email: false }))}
|
||||||
placeholder="이메일을 입력해 주세요."
|
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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onMouseDown={(e) => { e.preventDefault(); setEmail(""); }}
|
onMouseDown={(e) => { e.preventDefault(); setEmail(""); }}
|
||||||
@@ -176,15 +295,9 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!isEmailValid}
|
disabled={!isEmailValid || emailCodeVerified}
|
||||||
className={`h-[40px] px-[12px] rounded-[8px] text-[16px] font-semibold ${isEmailValid ? "bg-inactive-button text-white" : "bg-gray-50 text-input-placeholder-text"}`}
|
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={() => {
|
onClick={sendEmailCode}
|
||||||
if (!isEmailValid) return;
|
|
||||||
alert("인증번호 전송 (가상 동작)");
|
|
||||||
setEmailCodeSent(true);
|
|
||||||
setEmailCode("");
|
|
||||||
setEmailCodeVerified(false);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
인증번호 전송
|
인증번호 전송
|
||||||
</button>
|
</button>
|
||||||
@@ -207,7 +320,8 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
onFocus={() => setFocused((p) => ({ ...p, emailCode: true }))}
|
onFocus={() => setFocused((p) => ({ ...p, emailCode: true }))}
|
||||||
onBlur={() => setFocused((p) => ({ ...p, emailCode: false }))}
|
onBlur={() => setFocused((p) => ({ ...p, emailCode: false }))}
|
||||||
placeholder="인증번호 6자리"
|
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 && (
|
{emailCode.trim().length > 0 && focused.emailCode && !emailCodeVerified && (
|
||||||
<button
|
<button
|
||||||
@@ -224,18 +338,22 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={emailCodeVerified}
|
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"}`}
|
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={() => {
|
onClick={verifyEmailCode}
|
||||||
// 가상 검증: 6자리면 성공, 아니면 에러 모달
|
|
||||||
if (emailCode.length !== 6) {
|
|
||||||
onOpenCodeError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setEmailCodeVerified(true);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{emailCodeVerified ? "인증완료" : "인증하기"}
|
{emailCodeVerified ? "인증완료" : "인증하기"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-[13px] leading-[normal] text-[#384fbf]">
|
||||||
|
{emailCodeVerified ? (
|
||||||
|
"인증이 완료됐습니다"
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
인증 확인을 위해 작성한 이메일로 인증번호를 발송했습니다.
|
||||||
|
<br />
|
||||||
|
이메일을 확인해 주세요.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -306,13 +424,13 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="gender"
|
name="gender"
|
||||||
value="male"
|
value="MALE"
|
||||||
checked={gender === "male"}
|
checked={gender === "MALE"}
|
||||||
onChange={() => setGender("male")}
|
onChange={() => setGender("MALE")}
|
||||||
className="sr-only"
|
className="sr-only"
|
||||||
/>
|
/>
|
||||||
<span className={`inline-block rounded-full size-[18px] border ${gender === "male" ? "border-active-button" : "border-[#8c95a1]"}`}>
|
<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]" />}
|
{gender === "MALE" && <span className="block size-[9px] rounded-full bg-active-button m-[4.5px]" />}
|
||||||
</span>
|
</span>
|
||||||
남성
|
남성
|
||||||
</label>
|
</label>
|
||||||
@@ -320,13 +438,13 @@ export default function RegisterForm({ onOpenDone, onOpenCodeError }: RegisterFo
|
|||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="gender"
|
name="gender"
|
||||||
value="female"
|
value="FEMALE"
|
||||||
checked={gender === "female"}
|
checked={gender === "FEMALE"}
|
||||||
onChange={() => setGender("female")}
|
onChange={() => setGender("FEMALE")}
|
||||||
className="sr-only"
|
className="sr-only"
|
||||||
/>
|
/>
|
||||||
<span className={`inline-block rounded-full size-[18px] border ${gender === "female" ? "border-active-button" : "border-[#8c95a1]"}`}>
|
<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]" />}
|
{gender === "FEMALE" && <span className="block size-[9px] rounded-full bg-active-button m-[4.5px]" />}
|
||||||
</span>
|
</span>
|
||||||
여성
|
여성
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export default function RegisterPage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="min-h-screen w-full flex flex-col items-center justify-between">
|
<div className="min-h-screen w-full flex flex-col items-center justify-between">
|
||||||
|
|
||||||
<RegisterForm
|
<RegisterForm
|
||||||
onOpenDone={() => setDoneOpen(true)}
|
onOpenDone={() => setDoneOpen(true)}
|
||||||
onOpenCodeError={(msg) => {
|
onOpenCodeError={(msg) => {
|
||||||
@@ -21,6 +22,7 @@ export default function RegisterPage() {
|
|||||||
setCodeErrorOpen(true);
|
setCodeErrorOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RegisterOption
|
<RegisterOption
|
||||||
doneOpen={doneOpen}
|
doneOpen={doneOpen}
|
||||||
setDoneOpen={setDoneOpen}
|
setDoneOpen={setDoneOpen}
|
||||||
|
|||||||
Reference in New Issue
Block a user