Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 회원탈퇴 기능 구현 #75

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
9 changes: 8 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,12 @@
"version": "detect"
}
},
"rules": {}
"rules": {
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
]
}
}
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AnnouncementDetail } from './pages/Announcement/AnnouncementDetail';
import {
Login,
Register,
Resign,
Faq,
Board,
BoardDetail,
Expand Down Expand Up @@ -57,6 +58,7 @@ export default function App() {
<Route path={PATH.HOME} element={<Home />} />
<Route path={PATH.LOGIN} element={<Login />} />
<Route path={PATH.REGISTER} element={<Register />} />
<Route path={PATH.RESIGN} element={<Resign />} />
<Route path={PATH.RESET_PASSWORD} element={<ResetPassword />} />
<Route path={PATH.COMPETITION_LIST} element={<div>CompetitionList</div>} />

Expand Down
2 changes: 2 additions & 0 deletions src/constants/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const BASE_PATH: { [key: string]: string } = {
LOGIN: '/login',
REGISTER: '/register',
RESET_PASSWORD: '/reset-password',
RESIGN: '/resign',

COMPETITION: '/competition',
CLASS: '/class',
Expand Down Expand Up @@ -74,6 +75,7 @@ export const PAGE: { [key: string]: string } = {
LOGIN: '로그인',
REGISTER: '회원가입',
RESET_PASSWORD: '비밀번호 재설정',
RESIGN: '회원탈퇴',

CLASS_LIST: '수업 및 시험',
CLASS_ALL_PROBLEMS: '전체 문제 목록',
Expand Down
29 changes: 29 additions & 0 deletions src/pages/User/Resign.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ErrorMessage, Heading } from '@/components';
import { PAGE } from '@/constants';
import { ResignForm } from './components/ResignForm';
import { RESIGN_ERROR } from './constants';
import { useResignForm } from './hooks';
import { useResignMutation } from './hooks/query/useResignMutation';

export default function Resign() {
const inputList = useResignForm();
const { mutate, isError } = useResignMutation();

const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

const [currentPassword] = Object.values(inputList).map(({ value }) => value);

mutate({
current_password: currentPassword,
});
};

return (
<div className="container max-w-md mx-auto">
<Heading className="text-2xl font-bold">{PAGE['RESIGN']}</Heading>
<ResignForm inputList={inputList} onSubmit={handleFormSubmit} />
{isError && <ErrorMessage className="mt-4">{RESIGN_ERROR.RESIGN_FAILED}</ErrorMessage>}
</div>
);
}
12 changes: 11 additions & 1 deletion src/pages/User/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,14 @@ const resetPassword = (payload: ResetPasswordRequest): Promise<AxiosResponse> =>
return api.patch(`${API_URL}/{username}/`, payload);
};

export { loginUser, registerUser, resetPassword };
const findPassword = (payload: FindPasswordRequest): Promise<AxiosResponse> => {
// Todo: 전역 username 가져오도록 변경하기
return api.patch(`${API_URL}/{username}/`, payload);
};

const resignUser = (): Promise<AxiosResponse> => {
// Todo: 전역 username 가져오도록 변경하기
return api.delete(`${API_URL}/{username}/`);
};

export { loginUser, registerUser, resetPassword, findPassword, resignUser };
35 changes: 35 additions & 0 deletions src/pages/User/components/ResignForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Label, Input, Button, ErrorMessage } from '@/components';
import { PAGE } from '@/constants';

import { INPUT, RESIGN_ERROR } from '../constants';
import { useResignForm } from '../hooks';

type ResignFormProps = {
inputList: ReturnType<typeof useResignForm>;
onSubmit: (e: React.FormEvent<HTMLFormElement> & { target: HTMLFormElement }) => void;
};

export function ResignForm({ inputList, onSubmit }: ResignFormProps) {
const isFormValid = Object.values(inputList).every(({ value, error }) => value && !error);

return (
<form onSubmit={onSubmit} className="flex flex-col gap-3">
{Object.entries(inputList).map(([key, { type, value, error, handleInputChange }]) => (
<>
<Label htmlFor="email">현재 비밀번호를 입력해주세요.</Label>
<Input
type={type}
id={key}
value={value}
placeholder="비밀번호를 입력해주세요"
onChange={(e) => {
handleInputChange(e, inputList.currentPassword.value);
}}
/>
{error && <ErrorMessage>{RESIGN_ERROR[key.toUpperCase()]}</ErrorMessage>}
</>
))}
<Button disabled={!isFormValid}>{PAGE['RESIGN']}</Button>
</form>
);
}
1 change: 1 addition & 0 deletions src/pages/User/components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './AuthForm';
export * from './ResignForm';
5 changes: 5 additions & 0 deletions src/pages/User/constants/errorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ export const RESETPASSWORD_ERROR: { [key: string]: string } = {
NEWPASSWORDCHECK: '비밀번호와 일치하지 않습니다.',
RESET_PASSWORD_FAILED: '비밀번호 변경에 실패하였습니다.',
};

export const RESIGN_ERROR: { [key: string]: string } = {
CURRENTPASSWORD: '비밀번호는 영문자, 숫자 포함 8자 이상입니다.',
RESIGN_FAILED: '회원 탈퇴에 실패하였습니다.',
};
1 change: 1 addition & 0 deletions src/pages/User/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useInput';
export * from './useAuthForm';
export * from './useResetPasswordForm';
export * from './useResignForm';
1 change: 1 addition & 0 deletions src/pages/User/hooks/query/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useLoginMutation';
export * from './useRegisterMutation';
export * from './useResetPasswordMutation';
export * from './useResignMutation';
27 changes: 27 additions & 0 deletions src/pages/User/hooks/query/useResignMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AuthContext } from '@/contexts';
import { STORAGE, PATH } from '@/constants';

import { AxiosError, AxiosResponse } from 'axios';

import { useContext } from 'react';
import { useMutation, UseMutationOptions } from 'react-query';
import { useNavigate } from 'react-router-dom';
import { resignUser } from '../../api';

export const useResignMutation = (
options?: UseMutationOptions<AxiosResponse, AxiosError, ResignUserRequest>
) => {
const { setIsLogin } = useContext(AuthContext);

const navigate = useNavigate();
return useMutation(() => resignUser(), {
...options,
onSuccess: () => {
localStorage.removeItem(STORAGE.REFRESH_TOKEN);

setIsLogin(false);

navigate(PATH.HOME);
},
});
};
19 changes: 19 additions & 0 deletions src/pages/User/hooks/useResignForm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { REGEXP } from '../constants';
import { useInput } from './useInput';

const passwordInput = {
type: 'password',
required: true,
};

export const useResignForm = () => {
const resignForm = {
currentPassword: {
...passwordInput,
...useInput(),
regexp: REGEXP['PASSWORD'],
},
};

return resignForm;
};
1 change: 1 addition & 0 deletions src/pages/User/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as Login } from './Login';
export { default as Register } from './Register';
export { default as Resign } from './Resign';
8 changes: 8 additions & 0 deletions src/types/user.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@ type ResetPasswordRequest = {
new_password: string;
new_password2: string;
};

type FindPasswordRequest = {
email: string;
};

type ResignUserRequest = {
current_password: string;
};