문제점 : 모바일 버전 디테일 페이지에서는 header 컴포넌트에서 게시글 삭제 기능이 있는데 데스크탑 버전에서는 postContent 컴포넌트에서 직접 게시글 삭제가 되어야 한다.
똑같은 로직을 그대로 가져와서 쓰는데 헤더에서는 되고 posrContent 컴포넌트에선 안됨
'use client';
import { BackButtonIcon, MeatballMenuIcon, PencilIcon, RecruitmentIcon, TrashBinIcon } from '@/components/icons/Icons';
import { useGetPostById } from '@/hooks/useGetPostById';
import { useUserStore } from '@/utils/store/userStore';
import { useQueryClient } from '@tanstack/react-query';
import dayjs from 'dayjs';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { BottomSheet } from 'react-spring-bottom-sheet-updated';
import 'react-spring-bottom-sheet-updated/dist/style.css';
import Swal from 'sweetalert2';
interface PostDetailProps {
postPageId: string;
}
const Header = ({ postPageId }: PostDetailProps) => {
const { data: post, deletePostById, updateCompletedById } = useGetPostById(postPageId);
const { user } = useUserStore();
const [isSheetOpen, setIsSheetOpen] = useState(false);
const router = useRouter();
const queryClient = useQueryClient();
const handleDelete = () => {
closeSheet();
Swal.fire({
title: `게시물 삭제`,
text: `정말 삭제하시겠습니까?`,
icon: 'question',
showCancelButton: true,
confirmButtonText: '삭제하기',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
deletePostById.mutate(postPageId, {
onSuccess: () => {
// 삭제 후 관련 데이터를 무효화
queryClient.invalidateQueries({ queryKey: ['infinitePosts'] });
queryClient.invalidateQueries({ queryKey: ['posts'] });
router.push('/list');
}
});
}
});
return;
};
return (
<div className="flex h-12 items-center justify-between border-b px-2 py-2">
<button onClick={handleBack}>
<BackButtonIcon />
</button>
{/* 수정 삭제 버튼은 로그인 유저가 포스트 유저와 같은 id일때만 보여줌. */}
{user?.id === post?.user_id && (
<button onClick={openSheet}>
<MeatballMenuIcon />
</button>
)}
{/* 바텀 시트*/}
<BottomSheet
open={isSheetOpen}
onDismiss={closeSheet}
snapPoints={({ maxHeight }) => [maxHeight * 0.3, maxHeight * 0.4]} // 스냅 포인트 설정(참고로 첫번째는 처음 열리는 위치, 두번쨰는 사용자가 올릴 수 있는 만큼 올린 위치임)
defaultSnap={({ maxHeight }) => maxHeight * 0.3} // 기본 열림 위치 설정
>
<div className="p-4">
<button onClick={handleToggleRecruitment} className="flex w-full items-center gap-2 px-4 py-2 text-left">
<RecruitmentIcon />
{post?.completed ? '모집 마감 해제' : '모집 마감'}
</button>
<button onClick={handleEdit} className="flex w-full items-center gap-2 px-4 py-2 text-left">
<PencilIcon />
게시물 수정하기
</button>
<button onClick={handleDelete} className="flex w-full items-center gap-2 px-4 py-2 text-left text-red-500">
<TrashBinIcon />
게시물 삭제하기
</button>
</div>
</BottomSheet>
</div>
);
};
export default Header;
게시글 수정, 모집 마감 처리는 두 컴포넌트에서 모두 잘 작동하는데 삭제 로직만 안됨.
PostContent 컴포넌트에서 사용중인 삭제 로직에 콘솔을 와장창 찍어봄
// 게시글 삭제 핸들러
const handleDelete = () => {
closeSheet();
Swal.fire({
title: `게시물 삭제`,
text: `정말 삭제하시겠습니까?`,
icon: 'question',
showCancelButton: true,
confirmButtonText: '삭제하기',
cancelButtonText: '취소'
}).then((result) => {
console.log('result', result);
if (result.isConfirmed) {
console.log('11', postPageId);
deletePostById.mutate(postPageId, {
onSuccess: () => {
console.log('test');
// 삭제 후 관련 데이터를 무효화
queryClient.invalidateQueries({ queryKey: ['infinitePosts'] });
queryClient.invalidateQueries({ queryKey: ['posts'] });
router.push('/list');
},
onError: (error) => {
console.log(error);
}
});
}
});
return;
};
콘솔이 위에서부터 잘 찍히다가
onSuccess: () => {
console.log('test');
이 부분의 콘솔이 안찍힘. 즉 onSuccess에서 뭔가 문제가 있는것이다.
그렇다는것은 deletePostById.mutate 여기서 onSuccess를 반화하지 못하는것이니까 로직을 확인하러 가보자.
import { deletePost } from '@/lib/posts/deletePostbyID';
import { getPost, updateCompleted } from '@/lib/posts/updatePost';
import { PostType } from "@/types/PostType";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
export const useGetPostById = (postId: string) => {
const queryClient = useQueryClient();
const deletePostById = useMutation({
mutationFn: async (postId: string) => await deletePost(postId),
onMutate: async (postId) => {
console.log('id',postId)
await queryClient.cancelQueries({
queryKey: ["post", postId],
})
const previousPost = queryClient.getQueryData<PostType>(["post", postId])
console.log('previousPost', previousPost)
queryClient.setQueryData<PostType | null>(['post', postId], null);
console.log('???????')
return { previousPost };
},
onError: (context : {previousPost : PostType}) => {
queryClient.setQueryData( ["post", postId], context.previousPost)
},
onSettled: (postId) => {
queryClient.invalidateQueries({
queryKey: ["post", postId],
})
},
})
return { data, isPending, isError, deletePostById, updateCompletedById };
}
여기서 문제 발견! 삭제해주는 로직에서 낙관적 업데이트를 해줄 필요가 없는데 onMutate로 낙관적 업데이트를 해주고 있기 때문에 삭제가 안되고 무한 로딩이 발생했던것!
이 삭제 로직이 header 컴포넌트에 있었을 때는 삭제되는것이 postContent이기 때문에 삭제 시 낙관적 업데이트가 되어도 이 로직을 담당한느 해당 컴포넌트인 header에는 아무런 영향을 미치지 않기 때문에 삭제가 문제없이 잘 이루어졌지만,
실제로 게시글이 삭제된 시점에서 postContent 컴포넌트는 낙관적 업데이트를 할 수 없기 때문에 계속 406 에러가 났던것이다.
import { deletePost } from '@/lib/posts/deletePostbyID';
import { getPost, updateCompleted } from '@/lib/posts/updatePost';
import { PostType } from "@/types/PostType";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
export const useGetPostById = (postId: string) => {
const queryClient = useQueryClient();
const deletePostById = useMutation({
mutationFn: async (postId: string) => await deletePost(postId),
})
return { data, isPending, isError, deletePostById, updateCompletedById };
}
이렇게 onMutate 부분을 삭제했더니 header, postContent 모두 삭제가 잘 된다