'use client'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { getPost, deletePost } from '@/api/posts'; // ๐Ÿ› ๏ธ getPostsByCategory ์ œ๊ฑฐ import { getProfile } from '@/api/profile'; import MarkdownRenderer from '@/components/post/MarkdownRenderer'; import CommentList from '@/components/comment/CommentList'; import TOC from '@/components/post/TOC'; import { Loader2, Calendar, Eye, Folder, User, Edit2, Trash2, ArrowLeft, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { useAuthStore } from '@/store/authStore'; import Link from 'next/link'; interface PostDetailClientProps { slug: string; } export default function PostDetailClient({ slug }: PostDetailClientProps) { const router = useRouter(); const queryClient = useQueryClient(); const { role, _hasHydrated } = useAuthStore(); const isAdmin = _hasHydrated && role?.includes('ADMIN'); // 1. ๊ฒŒ์‹œ๊ธ€ ์ƒ์„ธ ์กฐํšŒ (์ด์ œ ์—ฌ๊ธฐ์— prevPost, nextPost๊ฐ€ ํฌํ•จ๋จ) const { data: post, isLoading: isPostLoading, error } = useQuery({ queryKey: ['post', slug], queryFn: () => getPost(slug), enabled: !!slug, retry: 1, }); // ๐Ÿ—‘๏ธ ์‚ญ์ œ๋จ: ๋” ์ด์ƒ ํ”„๋ก ํŠธ์—์„œ ์•ž๋’ค ๊ธ€์„ ์ฐพ๊ธฐ ์œ„ํ•ด ๋ชฉ๋ก์„ ์กฐํšŒํ•  ํ•„์š”๊ฐ€ ์—†์Œ! /* const { data: neighborPosts } = useQuery({ queryKey: ['posts', 'category', post?.categoryName], queryFn: () => getPostsByCategory(post!.categoryName, 0, 100), enabled: !!post?.categoryName, staleTime: 1000 * 60 * 5, }); */ const { data: profile } = useQuery({ queryKey: ['profile'], queryFn: getProfile, }); const deleteMutation = useMutation({ mutationFn: deletePost, onSuccess: () => { alert('๊ฒŒ์‹œ๊ธ€์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); queryClient.invalidateQueries({ queryKey: ['posts'] }); router.push('/'); }, onError: (err: any) => { alert('์‚ญ์ œ ์‹คํŒจ: ' + (err.response?.data?.message || err.message)); }, }); if (isPostLoading) { return (
); } if (error || !post) { const errorStatus = (error as any)?.response?.status; const errorMessage = (error as any)?.response?.data?.message || error?.message || '๊ฒŒ์‹œ๊ธ€์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.'; const isAuthError = errorStatus === 401 || errorStatus === 403; return (

{isAuthError ? '์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.' : '๊ฒŒ์‹œ๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.'}

{isAuthError ? '๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๋น„๊ณต๊ฐœ ๊ฒŒ์‹œ๊ธ€์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.' : errorMessage}

); } // ๐Ÿ—‘๏ธ ์‚ญ์ œ๋จ: ์ธ๋ฑ์Šค ๊ณ„์‚ฐ ๋กœ์ง ์ œ๊ฑฐ /* const currentIndex = neighborPosts?.content.findIndex((p) => p.id === post.id) ?? -1; const newerPost = ... const olderPost = ... */ // ๐Ÿ†• ๋ฐฑ์—”๋“œ ๋ฐ์ดํ„ฐ ์ง์ ‘ ์‚ฌ์šฉ // ๋ณดํ†ต '์ด์ „ ๊ธ€'์€ ๊ณผ๊ฑฐ ๊ธ€(prev), '๋‹ค์Œ ๊ธ€'์€ ์ตœ์‹  ๊ธ€(next)์ž…๋‹ˆ๋‹ค. // ๋ฐฑ์—”๋“œ ๊ตฌํ˜„์— ๋”ฐ๋ผ prevPost/nextPost ์œ„์น˜๊ฐ€ ๋ฐ˜๋Œ€์ผ ์ˆ˜ ์žˆ์œผ๋‹ˆ ํ™•์ธ ํ›„ ์œ„์น˜๋งŒ ๋ฐ”๊ฟ”์ฃผ์„ธ์š”. const prevPost = post.prevPost; // ์ด์ „ ๊ธ€ (์™ผ์ชฝ ๋ฒ„ํŠผ) const nextPost = post.nextPost; // ๋‹ค์Œ ๊ธ€ (์˜ค๋ฅธ์ชฝ ๋ฒ„ํŠผ) const handleDelete = () => { if (confirm('์ •๋ง๋กœ ์ด ๊ฒŒ์‹œ๊ธ€์„ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋ณต๊ตฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.')) { deleteMutation.mutate(post.id); } }; const handleEdit = () => { router.push(`/write?slug=${post.slug}`); }; return (
๋ชฉ๋ก์œผ๋กœ
{post.categoryName || 'Uncategorized'}
{isAdmin && (
)}

{post.title}

{profile?.imageUrl ? Author :
} {profile?.name || 'Dev Park'}
{new Date(post.createdAt).toLocaleDateString()}
{post.viewCount} views
{/* ๐Ÿ› ๏ธ ๋„ค๋น„๊ฒŒ์ด์…˜ ์˜์—ญ ์ˆ˜์ •: prevPost / nextPost ์ง์ ‘ ์‚ฌ์šฉ */}
); }