Skip to content

Commit a480e91

Browse files
committed
♻️ [Refactor]: 유저 정보 수정 로직 useMutation으로 변경
1 parent 5fcb837 commit a480e91

File tree

8 files changed

+89
-39
lines changed

8 files changed

+89
-39
lines changed

apps/client/src/entities/user/api/userApi.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UserData } from '@/entities/user';
1+
import { UserData, MutationUserData } from '@/entities/user';
22
import { axiosInstance } from '@/shared/api';
33

44
export const getUserInfo = async (): Promise<UserData> => {
@@ -17,3 +17,12 @@ export const getUserProfileImage = async (): Promise<string> => {
1717

1818
return response.data.data.profileImage;
1919
};
20+
21+
export const patchUserInfo = async (formData: MutationUserData) => {
22+
const response = await axiosInstance.patch('/v1/members/info', formData);
23+
if (!response.data.success) {
24+
throw new Error(response.data.message);
25+
}
26+
27+
return response.data;
28+
};
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export type { UserData } from './model/types';
2-
export { getUserInfo, getUserProfileImage } from './api/userApi';
3-
export { useUserData, useProfileImage } from './model/queries';
1+
export type { UserData, MutationUserData } from './model/types';
2+
export { getUserInfo, getUserProfileImage, patchUserInfo } from './api/userApi';
3+
export { useUserData, useProfileImage, useUserDataMutation } from './model/queries';

apps/client/src/entities/user/model/queries.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { useQuery } from '@tanstack/react-query';
2-
import { getUserInfo, getUserProfileImage } from '@/entities/user';
1+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2+
import { getUserInfo, getUserProfileImage, patchUserInfo } from '@/entities/user';
33
import { userKeys } from './queryFactory';
4+
import { useToast } from '@/shared/lib';
5+
import { MutationUserData } from './types';
46

57
export const useUserData = () => {
68
const {
@@ -22,3 +24,32 @@ export const useProfileImage = (isLoggedIn: boolean) => {
2224

2325
return { profileImgUrl: data, isLoading, error };
2426
};
27+
28+
export const useUserDataMutation = (onSuccessCallback: () => void) => {
29+
const queryClient = useQueryClient();
30+
const { toast } = useToast();
31+
32+
return useMutation({
33+
mutationFn: (formData: MutationUserData) => patchUserInfo(formData),
34+
onSuccess: async data => {
35+
if (data.success) {
36+
toast({ title: '프로필 업데이트 성공', variant: 'default' });
37+
await queryClient.invalidateQueries({ queryKey: userKeys.profile(), refetchType: 'active' });
38+
onSuccessCallback();
39+
} else {
40+
toast({
41+
title: '프로필 업데이트 실패',
42+
description: data.message || '알 수 없는 오류가 발생했습니다',
43+
variant: 'destructive',
44+
});
45+
}
46+
},
47+
onError: error => {
48+
toast({
49+
title: '프로필 업데이트 실패',
50+
description: error instanceof Error ? error.message : '네트워크 오류가 발생했습니다',
51+
variant: 'destructive',
52+
});
53+
},
54+
});
55+
};

apps/client/src/entities/user/model/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,12 @@ export type UserData = {
1515
contacts: Contacts;
1616
profileImage: string;
1717
};
18+
19+
export type MutationUserData = {
20+
contacts: {
21+
email: string;
22+
github: string;
23+
blog: string;
24+
linkedin: string;
25+
};
26+
} & Pick<UserData, 'camperId' | 'name' | 'field'>;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type { FormInput } from './types';
2+
export { transformFormToApiData } from './utils';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Field } from '@/shared/types';
2+
3+
export type FormInput = {
4+
camperId: string | undefined;
5+
name: string | undefined;
6+
field: Field | undefined;
7+
email: string | undefined;
8+
github: string | undefined;
9+
blog: string | undefined;
10+
linkedIn: string | undefined;
11+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Field } from '@/shared/types';
2+
import { FormInput } from './types';
3+
import { MutationUserData } from '@/entities/user';
4+
5+
export const transformFormToApiData = (data: FormInput, selectedField: Field | undefined): MutationUserData => ({
6+
name: data.name!,
7+
camperId: data.camperId!,
8+
field: selectedField!,
9+
contacts: {
10+
email: data.email ? data.email : '',
11+
github: data.github ? data.github : '',
12+
blog: data.blog ? data.blog : '',
13+
linkedin: data.linkedIn ? data.linkedIn : '',
14+
},
15+
});

apps/client/src/features/editProfile/ui/EditUserInfo.tsx

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
11
import { useForm } from 'react-hook-form';
22
import { useState } from 'react';
33
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/shadcn/avatar';
4-
import { UserData } from '@/pages/Profile';
5-
import { Field } from '@/shared/types/sharedTypes';
4+
import { UserData, useUserDataMutation } from '@/entities/user';
5+
import { Field } from '@/shared/types';
66
import { Button } from '@/shared/ui/shadcn/button';
7-
import { axiosInstance } from '@/shared/api';
8-
import { useToast } from '@/shared/lib';
7+
import { FormInput, transformFormToApiData } from '../lib';
98

109
type EditUserInfoProps = Readonly<{
1110
userData: UserData | undefined;
1211
toggleEditing: () => void;
1312
}>;
1413

15-
export type FormInput = {
16-
camperId: string | undefined;
17-
name: string | undefined;
18-
field: Field | undefined;
19-
email: string | undefined;
20-
github: string | undefined;
21-
blog: string | undefined;
22-
linkedIn: string | undefined;
23-
};
24-
2514
export function EditUserInfo({ userData, toggleEditing }: EditUserInfoProps) {
2615
const [selectedField, setSelectedField] = useState<Field | undefined>(userData?.field);
2716
const {
@@ -39,34 +28,18 @@ export function EditUserInfo({ userData, toggleEditing }: EditUserInfoProps) {
3928
linkedIn: userData?.contacts.linkedIn,
4029
},
4130
});
42-
const { toast } = useToast();
31+
const { mutateAsync } = useUserDataMutation(toggleEditing);
4332

4433
const handleSelectField = (field: Field) => {
4534
setSelectedField(selectedField === field ? '' : field);
4635
};
4736

4837
const handlePatchUserInfo = (data: FormInput) => {
49-
const formData = {
50-
name: data.name,
51-
camperId: data.camperId,
52-
field: selectedField,
53-
contacts: {
54-
email: data.email ? data.email : '',
55-
github: data.github ? data.github : '',
56-
blog: data.blog ? data.blog : '',
57-
linkedin: data.linkedIn ? data.linkedIn : '',
58-
},
59-
};
38+
const formData = transformFormToApiData(data, selectedField);
6039

6140
if (!formData.field) return;
6241

63-
axiosInstance.patch('/v1/members/info', formData).then(response => {
64-
if (response.data.success) {
65-
toggleEditing();
66-
} else {
67-
toast({ variant: 'destructive', title: '유저 정보 수정 실패' });
68-
}
69-
});
42+
mutateAsync(formData);
7043
};
7144

7245
return (

0 commit comments

Comments
 (0)