회원가입 api

This commit is contained in:
2025-11-24 23:31:05 +09:00
parent 7e122453bb
commit 12413795f4
4 changed files with 267 additions and 20 deletions

View File

@@ -1,5 +1,7 @@
'use client';
import { useState } from "react";
import { useRouter } from "next/navigation";
import ModalCloseSvg from "../svgs/closexsvg";
type Props = {
@@ -9,6 +11,78 @@ type Props = {
};
export default function AccountDeleteModal({ open, onClose, onConfirm }: Props) {
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const handleConfirm = async () => {
if (onConfirm) {
onConfirm();
return;
}
setIsLoading(true);
try {
const token = localStorage.getItem('token');
if (!token) {
alert('로그인이 필요합니다.');
setIsLoading(false);
onClose();
router.push('/login');
return;
}
console.log('회원 탈퇴 요청 시작, 토큰 존재:', !!token);
console.log('토큰 길이:', token?.length);
console.log('토큰 시작 부분:', token?.substring(0, 20));
const response = await fetch('https://hrdi.coconutmeet.net/auth/delete/me', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
});
console.log('회원 탈퇴 응답 상태:', response.status);
if (!response.ok) {
let errorMessage = `회원 탈퇴 실패 (${response.status})`;
try {
const errorData = await response.json();
console.error('회원 탈퇴 API 오류 응답:', errorData);
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
} else if (errorData.errorMessage) {
errorMessage = errorData.errorMessage;
} else if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
} catch (parseError) {
console.error('응답 파싱 오류:', parseError);
if (response.statusText) {
errorMessage = `${response.statusText} (${response.status})`;
}
}
console.error('회원 탈퇴 실패:', errorMessage, '상태 코드:', response.status);
alert(errorMessage);
setIsLoading(false);
return;
}
// 성공 시 토큰 제거 및 로그인 페이지로 이동
localStorage.removeItem('token');
onClose();
router.push('/login');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '네트워크 오류가 발생했습니다.';
console.error('회원 탈퇴 오류:', errorMessage);
alert(errorMessage);
setIsLoading(false);
}
};
if (!open) return null;
return (
@@ -20,7 +94,7 @@ export default function AccountDeleteModal({ open, onClose, onConfirm }: Props)
<div className="w-[528px] rounded-[12px] border border-[#dee1e6] bg-white shadow-[0_10px_30px_rgba(0,0,0,0.06)]">
{/* header */}
<div className="flex items-center justify-between p-6">
<h2 className="text-[20px] font-bold leading-[1.5] text-[#333c47]"> </h2>
<h2 className="text-[20px] font-bold leading-normal text-[#333c47]"> </h2>
<button
type="button"
aria-label="닫기"
@@ -34,10 +108,10 @@ export default function AccountDeleteModal({ open, onClose, onConfirm }: Props)
{/* body */}
<div className="px-6">
<div className="rounded-[16px] border border-[#dee1e6] bg-gray-50 p-6">
<p className="mb-3 text-[15px] font-bold leading-[1.5] text-[#4c5561]">
<p className="mb-3 text-[15px] font-bold leading-normal text-basic-text">
.
</p>
<div className="text-[15px] leading-[1.5] text-[#4c5561]">
<div className="text-[15px] leading-normal text-basic-text">
<p className="mb-0">- .</p>
<p className="mb-0">- , .</p>
<p>- .</p>
@@ -50,16 +124,17 @@ export default function AccountDeleteModal({ open, onClose, onConfirm }: Props)
<button
type="button"
onClick={onClose}
className="h-12 w-[136px] rounded-[10px] bg-[#f1f3f5] px-4 text-[16px] font-semibold leading-[1.5] text-[#4c5561] cursor-pointer"
className="h-12 w-[136px] rounded-[10px] bg-[#f1f3f5] px-4 text-[16px] font-semibold leading-normal text-basic-text cursor-pointer"
>
</button>
<button
type="button"
onClick={onConfirm}
className="h-12 w-[136px] rounded-[10px] bg-red-50 px-4 text-[16px] font-semibold leading-[1.5] text-[#f64c4c] cursor-pointer"
onClick={handleConfirm}
disabled={isLoading}
className="h-12 w-[136px] rounded-[10px] bg-red-50 px-4 text-[16px] font-semibold leading-normal text-error cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? '처리 중...' : '회원 탈퇴'}
</button>
</div>
</div>

View File

@@ -1,6 +1,7 @@
'use client';
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import ChangePasswordModal from "../ChangePasswordModal";
import PasswordChangeDoneModal from "../PasswordChangeDoneModal";
import AccountDeleteModal from "../AccountDeleteModal";
@@ -8,11 +9,90 @@ import MenuAccountOption from "@/app/menu/account/MenuAccountOption";
type VerificationState = 'initial' | 'sent' | 'verified' | 'failed' | 'changed';
type UserInfo = {
email?: string;
name?: string;
phone?: string;
};
export default function AccountPage() {
const router = useRouter();
const [open, setOpen] = useState(false);
const [verificationState, setVerificationState] = useState<VerificationState>('initial');
const [doneOpen, setDoneOpen] = useState(false);
const [deleteOpen, setDeleteOpen] = useState(false);
const [userInfo, setUserInfo] = useState<UserInfo>({});
const [isLoading, setIsLoading] = useState(true);
// 페이지 로드 시 사용자 정보 가져오기
useEffect(() => {
let isMounted = true;
async function fetchUserInfo() {
try {
const token = localStorage.getItem('token');
if (!token) {
if (isMounted) {
router.push('/login');
}
return;
}
const response = await fetch('https://hrdi.coconutmeet.net/auth/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
if (!response.ok) {
if (response.status === 401) {
// 토큰이 만료되었거나 유효하지 않은 경우
localStorage.removeItem('token');
if (isMounted) {
router.push('/login');
}
return;
}
let errorMessage = `사용자 정보 조회 실패 (${response.status})`;
try {
const errorData = await response.json();
if (errorData.error) {
errorMessage = errorData.error;
} else if (errorData.message) {
errorMessage = errorData.message;
}
} catch (parseError) {
// ignore
}
console.error('사용자 정보 조회 실패:', errorMessage);
if (isMounted) {
setIsLoading(false);
}
return;
}
const data = await response.json();
if (isMounted) {
setUserInfo(data);
setIsLoading(false);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '네트워크 오류가 발생했습니다.';
console.error('사용자 정보 조회 오류:', errorMessage);
if (isMounted) {
setIsLoading(false);
}
}
}
fetchUserInfo();
return () => {
isMounted = false;
};
}, []);
// 개발 옵션에서 'changed'로 전환하면 완료 모달 표시
useEffect(() => {
@@ -22,7 +102,7 @@ export default function AccountPage() {
return (
<main className="flex w-full flex-col">
<div className="flex h-[100px] items-center px-8">
<h1 className="text-[24px] font-bold leading-[1.5] text-[#1b2027]"> </h1>
<h1 className="text-[24px] font-bold leading-normal text-[#1b2027]"> </h1>
</div>
<div className="px-8 pb-20">
<div className="rounded-lg border border-[#dee1e6] bg-white p-8">
@@ -31,7 +111,9 @@ export default function AccountPage() {
()
</label>
<div className="h-10 rounded-lg border border-[#dee1e6] bg-neutral-50 px-3 py-2">
<span className="text-[16px] leading-[1.5] text-[#333c47]">skyblue@edu.com</span>
<span className="text-[16px] leading-normal text-[#333c47]">
{isLoading ? '로딩 중...' : (userInfo.email || '이메일 정보 없음')}
</span>
</div>
</div>
<div className="mt-6 flex flex-col gap-2">
@@ -40,7 +122,7 @@ export default function AccountPage() {
</label>
<div className="flex items-center gap-3">
<div className="h-10 flex-1 rounded-lg border border-[#dee1e6] bg-neutral-50 px-3 py-2">
<span className="text-[16px] leading-[1.5] text-[#333c47]"></span>
<span className="text-[16px] leading-normal text-[#333c47]"></span>
</div>
<button
type="button"
@@ -89,9 +171,47 @@ export default function AccountPage() {
<AccountDeleteModal
open={deleteOpen}
onClose={() => setDeleteOpen(false)}
onConfirm={() => {
// TODO: 탈퇴 API 연동
setDeleteOpen(false);
onConfirm={async () => {
try {
const token = localStorage.getItem('token');
const response = await fetch('https://hrdi.coconutmeet.net/auth/delete/me', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
},
});
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);
alert(errorMessage);
return;
}
// 성공 시 토큰 제거 및 로그인 페이지로 이동
localStorage.removeItem('token');
setDeleteOpen(false);
router.push('/login');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '네트워크 오류가 발생했습니다.';
console.error('회원 탈퇴 오류:', errorMessage);
alert(errorMessage);
}
}}
/>
</main>