diff --git a/app/(route)/admin/user/_components/UserTable/index.tsx b/app/(route)/admin/user/_components/UserTable/index.tsx
index 16d49ce..ed96fc8 100644
--- a/app/(route)/admin/user/_components/UserTable/index.tsx
+++ b/app/(route)/admin/user/_components/UserTable/index.tsx
@@ -13,12 +13,15 @@ import {
TableRow,
} from '@app/_shadcn/components/ui/table';
import { Input } from '@app/_shadcn/components/ui/input';
+import ButtonWithDialogCheck from '@app/_components/common/WithDialogCheck';
+import useWithdrawalMutation from '@app/_hooks/apis/user/useWithdrawalMutation';
import RoleFilterSelect from './RoleFilterSelect';
import RoleUpdateSelect from './RoleUpdateSelect';
function UserTable() {
const roleFilter = useRecoilValue(roleState);
const { data } = useUserQuery(roleFilter);
+ const { mutate: withdrawMutate } = useWithdrawalMutation();
return (
<>
@@ -35,6 +38,7 @@ function UserTable() {
닉네임
아이디
이메일
+ 회원 탈퇴
@@ -47,6 +51,17 @@ function UserTable() {
{nickname}
{id}
{email}
+
+ withdrawMutate(id)}
+ >
+ 탈퇴
+
+
))}
diff --git a/app/(route)/mypage/_components/AccountSettingSection.tsx b/app/(route)/mypage/_components/AccountSettingSection.tsx
new file mode 100644
index 0000000..1119e80
--- /dev/null
+++ b/app/(route)/mypage/_components/AccountSettingSection.tsx
@@ -0,0 +1,39 @@
+'use client';
+
+import ButtonWithDialogCheck from '@app/_components/common/WithDialogCheck';
+import useWithdrawalMutation from '@app/_hooks/apis/user/useWithdrawalMutation';
+import { Button } from '@app/_shadcn/components/ui/button';
+import { myProfileState } from '@app/_store/permissionAtoms';
+import { useRecoilValue } from 'recoil';
+
+function AccountSettingSection() {
+ const { mutate: withdrawMutate } = useWithdrawalMutation();
+ const myProfile = useRecoilValue(myProfileState);
+
+ const onWithdrawalClick = () => {
+ if (!myProfile?.id) {
+ throw new Error('[개발자 문의 바람] myProfile id 상태를 확인해주세요.');
+ }
+ withdrawMutate(myProfile.id);
+ };
+
+ return (
+
+
+
+ 회원 탈퇴
+
+
+ );
+}
+
+export default AccountSettingSection;
diff --git a/app/(route)/mypage/_components/ProfileForm.tsx b/app/(route)/mypage/_components/ProfileForm.tsx
index 21db98b..671ea99 100644
--- a/app/(route)/mypage/_components/ProfileForm.tsx
+++ b/app/(route)/mypage/_components/ProfileForm.tsx
@@ -40,10 +40,7 @@ function ProfileForm({ children, defaultValues }: Props) {
return (
-
diff --git a/app/(route)/mypage/layout.tsx b/app/(route)/mypage/layout.tsx
index 960375c..563344f 100644
--- a/app/(route)/mypage/layout.tsx
+++ b/app/(route)/mypage/layout.tsx
@@ -20,7 +20,7 @@ function Layout({ children }: Props) {
if (!isLoggedIn) return redirect(PATH.user.login.url);
return (
-
+
diff --git a/app/(route)/mypage/page.tsx b/app/(route)/mypage/page.tsx
index 4e40624..1df10eb 100644
--- a/app/(route)/mypage/page.tsx
+++ b/app/(route)/mypage/page.tsx
@@ -3,11 +3,13 @@
import Link from 'next/link';
import { useRecoilValue } from 'recoil';
import { myProfileState } from '@app/_store/permissionAtoms';
+import { Separator } from '@app/_shadcn/components/ui/separator';
import { PATH } from '@app/_constants/urls';
import { Button, buttonVariants } from '@app/_shadcn/components/ui/button';
import MyAvatarInput from './_components/MyAvatarInput';
import MyInfoSection from './_components/MyInfoSection';
import ProfileForm from './_components/ProfileForm';
+import AccountSettingSection from './_components/AccountSettingSection';
function MyPage() {
const myProfile = useRecoilValue(myProfileState);
@@ -33,13 +35,17 @@ function MyPage() {
};
return (
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+ >
);
}
diff --git a/app/_components/common/WithDialogCheck.tsx b/app/_components/common/WithDialogCheck.tsx
new file mode 100644
index 0000000..4bf4ec5
--- /dev/null
+++ b/app/_components/common/WithDialogCheck.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '@app/_shadcn/components/ui/dialog';
+import { Button, ButtonProps } from '@app/_shadcn/components/ui/button';
+
+interface Props extends ButtonProps {
+ title: string;
+ description: string;
+ confirmVariant?: ButtonProps['variant'];
+ cancelVariant?: ButtonProps['variant'];
+}
+
+function ButtonWithDialogCheck({
+ title,
+ description,
+ onClick,
+ confirmVariant,
+ cancelVariant,
+ ...props
+}: Props) {
+ return (
+
+ );
+}
+
+export default ButtonWithDialogCheck;
diff --git a/app/_hooks/apis/user/useWithdrawalMutation.ts b/app/_hooks/apis/user/useWithdrawalMutation.ts
new file mode 100644
index 0000000..ef5ea8c
--- /dev/null
+++ b/app/_hooks/apis/user/useWithdrawalMutation.ts
@@ -0,0 +1,28 @@
+import userService from '@app/_service/userService';
+import { useToast } from '@app/_shadcn/components/ui/use-toast';
+import { useMutation } from '@tanstack/react-query';
+
+function useWithdrawalMutation() {
+ const { toast } = useToast();
+
+ return useMutation({
+ mutationFn: async (userId: string) => {
+ await userService.withdrawal(userId);
+ },
+
+ onSuccess: () =>
+ toast({
+ title: '회원 탈퇴',
+ description: '회원 탈퇴에 성공했어요.',
+ }),
+
+ onError: () =>
+ toast({
+ variant: 'destructive',
+ title: '회원 탈퇴',
+ description: '회원 탈퇴에 실패했어요.',
+ }),
+ });
+}
+
+export default useWithdrawalMutation;
diff --git a/app/_service/userService.ts b/app/_service/userService.ts
index af5c109..11c0247 100644
--- a/app/_service/userService.ts
+++ b/app/_service/userService.ts
@@ -20,6 +20,13 @@ class UserService extends Service {
);
return data;
}
+
+ async withdrawal(userId: string) {
+ const { data } = await this.axiosExtend.delete(
+ `/api/user/withdrawal/${userId}`,
+ );
+ return data;
+ }
}
const userService = new UserService();