Skip to content

Commit

Permalink
Feat: #194 프로필 이미지 조회 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Yoonyesol committed Oct 19, 2024
1 parent 75f2931 commit 8c3a3ea
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
20 changes: 19 additions & 1 deletion src/components/user/auth-form/ProfileImageContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { JPG, PNG, SVG, WEBP } from '@constants/mimeFileType';
import useToast from '@hooks/useToast';
import { useUploadProfileImage } from '@hooks/query/useUserQuery';
import useStore from '@stores/useStore';
import { getProfileImage } from '@services/userService';
import useAxios from '@/hooks/useAxios';

type ProfileImageContainerProps = {
imageUrl: string | null;
Expand All @@ -15,8 +17,24 @@ type ProfileImageContainerProps = {

export default function ProfileImageContainer({ imageUrl, setImageUrl }: ProfileImageContainerProps) {
const { toastWarn } = useToast();
const { mutate: uploadImageMutate } = useUploadProfileImage();
const { editUserInfo, userInfo } = useStore();
const { mutate: uploadImageMutate } = useUploadProfileImage();
const { fetchData } = useAxios(getProfileImage);
const { toastError } = useToast();

useEffect(() => {
const handleGetProfileImage = async (uploadName: string) => {
const response = await fetchData(uploadName);
if (response == null)
return toastError('프로필 이미지 조회 도중 문제가 발생했습니다. 잠시 후 다시 시도해주세요.');

const blob = new Blob([response.data], { type: response.headers['content-type'] });
const profileImageUrl = URL.createObjectURL(blob);
setImageUrl(profileImageUrl);
};

if (userInfo.profileImageName) handleGetProfileImage(userInfo.profileImageName);
}, [userInfo.profileImageName]);

useEffect(() => {
return () => {
Expand Down
25 changes: 25 additions & 0 deletions src/hooks/query/useUserQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,31 @@ export function useUploadProfileImage() {

return mutation;
}
export function useGetProfileImage() {
const queryClient = useQueryClient();
const { toastSuccess, toastError } = useToast();
const { userInfo, editUserInfo } = useStore();
const userProfileImageQueryKey = generateProfileFileQueryKey(userInfo.userId);

const mutation = useMutation({
mutationFn: ({ file }: { file: File }) =>
uploadProfileImage(file, {
headers: { 'Content-Type': 'multipart/form-data' },
}),
onError: () => toastError('이미지 업로드에 실패했습니다. 다시 시도해 주세요.'),
onSuccess: (response) => {
const { imageName } = response.data;

if (!imageName) return toastError('이미지 업로드에 실패했습니다. 다시 시도해 주세요.');

toastSuccess('이미지가 업로드되었습니다.');
editUserInfo({ profileImageName: imageName });
queryClient.invalidateQueries({ queryKey: userProfileImageQueryKey });
},
});

return mutation;
}

export function useUpdateLinks() {
const { userInfo } = useStore();
Expand Down
34 changes: 34 additions & 0 deletions src/mocks/services/userServiceHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,40 @@ const userServiceHandler = [

return HttpResponse.json({ imageName: uploadName }, { status: 200 });
}),
// 유저 프로필 이미지 조회 API
http.get(`${BASE_URL}/file/profile/:fileName`, async ({ request, params }) => {
const { fileName } = params;

const accessToken = request.headers.get('Authorization');
if (!accessToken) return new HttpResponse(null, { status: 401 });

const userId = convertTokenToUserId(accessToken);
if (!userId) {
return HttpResponse.json({ message: '토큰에 유저 정보가 존재하지 않습니다.' }, { status: 401 });
}

const userIndex = USER_DUMMY.findIndex((user) => user.userId === userId);
if (userIndex === -1) {
return HttpResponse.json(
{ message: '해당 사용자를 찾을 수 없습니다. 입력 정보를 확인해 주세요.' },
{ status: 401 },
);
}

const decodedFileName = decodeURIComponent(fileName.toString());
const fileInfo = PROFILE_IMAGE_DUMMY.find((file) => file.uploadName === decodedFileName);
if (!fileInfo) return new HttpResponse(null, { status: 404 });

if (fileInfo.userId !== Number(userId))
return HttpResponse.json({ message: '해당 파일에 접근 권한이 없습니다.' }, { status: 401 });

const buffer = await fileInfo.file.arrayBuffer();
return HttpResponse.arrayBuffer(buffer, {
headers: {
'Content-Type': fileInfo.file.type,
},
});
}),
// 전체 팀 목록 조회 API (가입한 팀, 대기중인 팀)
http.get(`${BASE_URL}/user/team`, ({ request }) => {
const accessToken = request.headers.get('Authorization');
Expand Down
18 changes: 17 additions & 1 deletion src/services/userService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export async function updateLinks(links: EditUserLinksForm, axiosConfig: AxiosRe
}

/**
* 유저 프로필 업로드 API
* 유저 프로필 이미지 업로드 API
*
* @export
* @async
Expand All @@ -44,6 +44,22 @@ export async function uploadProfileImage(file: File, axiosConfig: AxiosRequestCo
return authAxios.postForm(`/user/profile/image`, fileFormData, axiosConfig);
}

/**
* 유저 프로필 이미지 조회 API
*
* @export
* @async
* @param {string} fileName - 파일명
* @param {AxiosRequestConfig} [axiosConfig={}] - axios 요청 옵션 설정 객체
* @returns {Promise<AxiosResponse<Blob>>}
*/
export async function getProfileImage(fileName: string, axiosConfig: AxiosRequestConfig = {}) {
return authAxios.get<Blob>(`/file/profile/${fileName}`, {
...axiosConfig,
responseType: 'blob',
});
}

/**
* 유저 목록을 검색하는 API
*
Expand Down

0 comments on commit 8c3a3ea

Please sign in to comment.