6.2 최근/인기 글 리스트 및 무한스크롤 연동 o

This commit is contained in:
koreacomp5
2025-10-09 16:31:46 +09:00
parent b1557851ab
commit cdafc2908f
4 changed files with 78 additions and 5 deletions

View File

@@ -0,0 +1,65 @@
"use client";
import useSWRInfinite from "swr/infinite";
type Item = {
id: string;
title: string;
createdAt: string;
isPinned: boolean;
status: string;
stat?: { recommendCount: number; views: number; commentsCount: number } | null;
};
type Resp = {
total: number;
page: number;
pageSize: number;
items: Item[];
sort: "recent" | "popular";
};
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export function PostList({ boardId, sort = "recent" }: { boardId?: string; sort?: "recent" | "popular" }) {
const pageSize = 10;
const getKey = (index: number, prev: Resp | null) => {
if (prev && prev.items.length === 0) return null;
const page = index + 1;
const sp = new URLSearchParams({ page: String(page), pageSize: String(pageSize), sort });
if (boardId) sp.set("boardId", boardId);
return `/api/posts?${sp.toString()}`;
};
const { data, size, setSize, isLoading } = useSWRInfinite<Resp>(getKey, fetcher);
const items = data?.flatMap((d) => d.items) ?? [];
const canLoadMore = (data?.at(-1)?.items.length ?? 0) === pageSize;
return (
<div>
<div style={{ display: "flex", gap: 8, marginBottom: 8 }}>
<span>:</span>
<a href={`/?sort=recent`} style={{ textDecoration: sort === "recent" ? "underline" : "none" }}></a>
<a href={`/?sort=popular`} style={{ textDecoration: sort === "popular" ? "underline" : "none" }}></a>
</div>
<ul style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{items.map((p) => (
<li key={p.id} style={{ padding: 12, border: "1px solid #eee", borderRadius: 8 }}>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<strong>{p.title}</strong>
<span style={{ opacity: 0.7 }}>{new Date(p.createdAt).toLocaleString()}</span>
</div>
<div style={{ fontSize: 12, opacity: 0.8 }}>
{p.stat?.recommendCount ?? 0} · {p.stat?.views ?? 0} · {p.stat?.commentsCount ?? 0}
</div>
</li>
))}
</ul>
<div style={{ marginTop: 12 }}>
<button disabled={!canLoadMore || isLoading} onClick={() => setSize(size + 1)}>
{isLoading ? "로딩 중..." : canLoadMore ? "더 보기" : "끝"}
</button>
</div>
</div>
);
}