Skip to content

Commit e4f08f1

Browse files
authored
Merge pull request #180 from prgrms-web-devcourse-final-project/feat/175-delete-member
[feat] 회원탈퇴 구현
2 parents ce3915d + 301b629 commit e4f08f1

File tree

5 files changed

+106
-52
lines changed

5 files changed

+106
-52
lines changed

src/apis/user.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,15 @@ export const patchEditProfile = async (editInfo: Partial<UserProfileEdit>) => {
7272
const { data } = await axiosInstance.patch(`/user`, editInfo);
7373
return data;
7474
};
75+
76+
// 회원 정보 조회
77+
export const getUserInfo = async () => {
78+
const { data } = await axiosInstance.get('/user');
79+
return data;
80+
};
81+
82+
// 회원 탈퇴
83+
export const deleteAccount = async () => {
84+
const { data } = await axiosInstance.delete('/user');
85+
return data;
86+
};

src/components/KaKaoRedirection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function KaKaoRedirection() {
1111

1212
const handleKaKaoLogin = async () => {
1313
try {
14+
console.log('kakaoCode', kakaoCode);
1415
const { code, data } = await getKakaoLogin(kakaoCode as string);
1516
if (code === 200) {
1617
setAccessToken(data.accessToken);
@@ -24,7 +25,7 @@ function KaKaoRedirection() {
2425
};
2526

2627
useEffect(() => {
27-
handleKaKaoLogin();
28+
if (kakaoCode) handleKaKaoLogin();
2829
}, []);
2930
return <Loading />;
3031
}

src/hooks/useMoreOptions.ts

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
import { postLogout } from '@/apis/auth';
2+
import { addBlockList } from '@/apis/blockList';
3+
import { deleteAccount, getUserInfo } from '@/apis/user';
24
import { useAuthStore } from '@/store/authStore';
3-
import { useLocation, useNavigate } from 'react-router';
5+
import { useModalStore } from '@/store/modalStore';
6+
import { useUserStore } from '@/store/userStore';
7+
import { useLocation, useNavigate, useParams } from 'react-router';
48

59
// url에 따라서 헤더의 moreOptions 선택
610
export const useMoreOptions = () => {
711
const navigate = useNavigate();
812
const location = useLocation();
13+
const param = useParams();
14+
915
const { logout } = useAuthStore();
16+
const { openModal, closeModal } = useModalStore();
17+
const { userData } = useUserStore(); // 차단할 유저 정보
1018

1119
const handleEditProfile = () => navigate('/mypage/edit');
1220
const handleBlockList = () => navigate('/mypage/blocklist');
1321
const handleLogout = async () => {
1422
try {
1523
const { code } = await postLogout();
1624
if (code === 200) {
17-
logout();
25+
logout(); // 토큰 초기화
1826
useAuthStore.persist.clearStorage(); // 로컬스토리지에서 persist 데이터 삭제
1927
navigate('/');
2028
}
@@ -23,18 +31,86 @@ export const useMoreOptions = () => {
2331
}
2432
};
2533

34+
const handleBlockUser = async () => {
35+
if (!param.userId) {
36+
console.log('차단 실패');
37+
return;
38+
}
39+
40+
openModal({
41+
title: `${userData?.nickname}을 차단할까요?`,
42+
message: '차단된 사용자는 더이상 피드에 나타나지 않습니다',
43+
onConfirm: async () => {
44+
if (param.userId) {
45+
try {
46+
const data = await addBlockList(param.userId);
47+
closeModal();
48+
console.log(data);
49+
} catch (error) {
50+
console.log(error);
51+
}
52+
}
53+
},
54+
onCancel: () => {
55+
closeModal();
56+
},
57+
});
58+
};
59+
60+
const handleDeleteAccount = async () => {
61+
try {
62+
const { code, data } = await getUserInfo();
63+
if (code === 200) {
64+
openModal({
65+
title: [
66+
{ text: data.nickName, className: 'text-primary-normal' },
67+
{ text: '님 떠나시는 건가요?' },
68+
],
69+
message: '탈퇴 버튼 선택 시, 모든 활동 정보가 삭제됩니다',
70+
confirmText: '탈퇴',
71+
onConfirm: async () => {
72+
try {
73+
const { code } = await deleteAccount();
74+
if (code === 200) {
75+
logout();
76+
useAuthStore.persist.clearStorage(); // 로컬스토리지에서 persist 데이터 삭제
77+
closeModal();
78+
navigate('/');
79+
}
80+
} catch (error) {
81+
console.log(error);
82+
}
83+
},
84+
onCancel: () => {
85+
closeModal();
86+
},
87+
});
88+
} else {
89+
throw new Error('정보를 불러오는 중 오류가 생겼습니다.');
90+
}
91+
} catch (error) {
92+
console.error(error);
93+
}
94+
};
95+
2696
// url에 따라서 다른 items값 return
2797
const getMoreOptionsItems = () => {
28-
switch (location.pathname) {
29-
case '/mypage':
30-
return [
31-
{ label: '프로필 수정', onClick: handleEditProfile },
32-
{ label: '차단 목록', onClick: handleBlockList },
33-
{ label: '로그아웃', onClick: handleLogout },
34-
];
35-
default:
36-
return [{ label: '로그아웃', onClick: handleLogout }];
98+
if (location.pathname.startsWith('/user/')) {
99+
return [{ label: '차단', onClick: handleBlockUser }];
37100
}
101+
102+
if (location.pathname === '/mypage/edit') {
103+
return [{ label: '탈퇴하기', onClick: handleDeleteAccount }];
104+
}
105+
if (location.pathname === '/mypage') {
106+
return [
107+
{ label: '프로필 수정', onClick: handleEditProfile },
108+
{ label: '차단 목록', onClick: handleBlockList },
109+
{ label: '로그아웃', onClick: handleLogout },
110+
];
111+
}
112+
113+
return [{ label: '로그아웃', onClick: handleLogout }];
38114
};
39115

40116
return getMoreOptionsItems();

src/layouts/Layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function Layout() {
3333
'/post': <HeaderWithBack text="글 등록" />,
3434
'/signup': <HeaderWithBack text="회원가입" />,
3535
'/mypage/blocklist': <HeaderWithBack text="차단 목록" />,
36-
'/mypage/edit': <HeaderWithBack text="내 정보 수정" />,
36+
'/mypage/edit': <HeaderWithBack text="내 정보 수정" showMoreOptions />,
3737
};
3838

3939
const renderHeader = () => {

src/layouts/header/HeaderWithBack.tsx

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { addBlockList } from '@/apis/blockList';
21
import MoreOptionsSelect from '@/components/MoreOptionsSelect';
2+
import { useMoreOptions } from '@/hooks/useMoreOptions';
33
import HedaerLayout from '@/layouts/header/HedaerLayout';
4-
import { useModalStore } from '@/store/modalStore';
5-
import { useUserStore } from '@/store/userStore';
64
import backIcon from '@assets/icons/back-icon.svg';
7-
import { useNavigate, useParams } from 'react-router';
5+
import { useNavigate } from 'react-router';
86

97
interface HeaderWithBackProps {
108
showMoreOptions?: boolean; // 더보기 메뉴를 표시할지 여부
@@ -14,38 +12,7 @@ interface HeaderWithBackProps {
1412
// 뒤로 가기 있는 헤더
1513
function HeaderWithBack({ showMoreOptions = false, text }: HeaderWithBackProps) {
1614
const navigate = useNavigate();
17-
const param = useParams();
18-
19-
const { openModal, closeModal } = useModalStore();
20-
const { userData } = useUserStore(); // 차단할 유저 정보
21-
22-
// 임시함수
23-
const handleBlockUser = async () => {
24-
if (!param.userId) {
25-
console.log('차단 실패');
26-
return;
27-
}
28-
29-
openModal({
30-
title: `${userData?.nickname}을 차단할까요?`,
31-
message: '차단된 사용자는 더이상 피드에 나타나지 않습니다',
32-
onConfirm: async () => {
33-
if (param.userId) {
34-
try {
35-
const data = await addBlockList(param.userId);
36-
closeModal();
37-
console.log(data);
38-
} catch (error) {
39-
console.log(error);
40-
}
41-
}
42-
},
43-
onCancel: () => {
44-
closeModal();
45-
},
46-
});
47-
};
48-
15+
const moreOptionsItems = useMoreOptions();
4916
return (
5017
<HedaerLayout>
5118
<div className="flex items-center justify-between w-full">
@@ -64,9 +31,7 @@ function HeaderWithBack({ showMoreOptions = false, text }: HeaderWithBackProps)
6431
</div>
6532

6633
{/* 더보기 메뉴 */}
67-
{showMoreOptions && (
68-
<MoreOptionsSelect items={[{ label: '차단', onClick: handleBlockUser }]} />
69-
)}
34+
{showMoreOptions && <MoreOptionsSelect items={moreOptionsItems} />}
7035
</div>
7136
</HedaerLayout>
7237
);

0 commit comments

Comments
 (0)