[ํฌ์ผ๋ชฌ ๋๊ฐ ํ๋ก์ ํธ] #8: ํ์ด์ง๋ค์ด์ ๊ธฐ๋ฅ ๊ตฌํ
๐ [ํฌ์ผ๋ชฌ ๋๊ฐ ํ๋ก์ ํธ] #7: ํ์ด์ง๋ค์ด์ ๊ธฐ๋ฅ ๊ตฌํ
๐ฏ ๋ชฉํ
๐ ํ์ด์ง ๊ธฐ๋ฅ: ํ ๋ฒ์ 20๊ฐ์ฉ๋ง ๋ณด์ฌ์ฃผ๊ธฐ
โ ์ฃผ์ ๊ธฐ์ ํฌ์ธํธ
๐ ํ์ด์ง ๊ธฐ๋ฅ
- ํ ํ์ด์ง์ 20๊ฐ์ ๋ฐ์ดํฐ๋ง ํ์
- ํ์ด์ง ๋ฒํธ๋ฅผ ๋๋ฅด๋ฉด ํด๋น ํ์ด์ง ๋ฐ์ดํฐ๊ฐ ๋ ๋๋ง๋ฉ๋๋ค. (URL ์ฟผ๋ฆฌ์คํธ๋ง๋ ๊ฐ์ด ์ ๋ฐ์ดํธ)
- ์ด์ /๋ค์ ๋ฒํผ์ ์ ํ ํ์ด์ง์ ์ฒ์/๋ง์ง๋ง ์ผ๋ก ์ด๋
๐น ํ์ด์ง๋ค์ด์ ๊ตฌ์ฑ
- totalItems: ์ ์ฒด ๋ฐ์ดํฐ์
- limit : ํํ์ด์ง๋น ๋ณด์ฌ์ง ๊ฐฏ์ (20๊ฐ)
- pageCount : ํ์ด์ง ๊ทธ๋ฃน ๊ฐฏ์(5๊ฐ์ฉ)
- totalPages: ์ ์ฒด ํ์ด์ง์
- pageGroup: ํ์ฌํ์ด์ง์ ๊ทธ๋ฃน ์ธ๋ฑ์ค
- startIdx: ํ์ฌํ์ด์ง ๊ทธ๋ฃน์ ์ฒซ๋ฒ์งธ ํ์ด์ง ๋๋ฒ
const totalPages = Math.ceil(totalItems / pageItems);
totalPages: ์ ์ฒด ๋ฐ์ดํฐ์ ์์ ํํ์ด์ง๋น ๋ณด์ฌ์ง ์์ดํ
์ผ๋ก ๋๋ ๊ฐฏ์, ๋ง์ง๋ง ํ์ด์ง๋ ๋ณด์ฌ์ผ ๋๊ธฐ ๋๋ฌธ์ ์์ซ์ ๋ฐ์ฌ๋ฆผ์ผ๋ก ๊ณ์ฐ
ex) 11๊ฐ์ ์์ดํ
์ด ์์๋, ํ๋ฒ์ 5๊ฐ์ฉ ๋ณด์ฌ์ง๋ฉด ๋ง์ง๋ง ํ์ด์ง๊น์ง ์ด 3ํ์ด์ง๊ฐ ๋ณด์ฌ์ ธ์ผ ๋๋ค.
const pageGroup = Math.ceil(p / pageCount);
pageGroup: ํ์ฌํ์ด์ง๊ฐ ์ด๋ ํ์ด์ง๊ทธ๋ฃน์ ์์นํด ์๋์ง ํ์ด์ง๊ทธ๋ฃน์ ์์๋ฅผ ๋ํ๋ธ๋ค.
ex) ํ์ด์ง๊ทธ๋ฃน์ 5๋ก ๋๋ด์๋ ํ์ฌ 2ํ์ด์ง๋ ์ฒซ๋ฒ์งธ ๊ทธ๋ฃน, 6ํ์ด์ง๋ ๋๋ฒ์งธ ๊ทธ๋ฃน์ด ๋๋ค.
const startIdx = (pageGroup - 1) * pageCount + 1;
startIdx: ํ์ฌ ๋ณด์ฌ์ง ํ์ด์ง๊ทธ๋ฃน์ ์ฒซ๋ฒ์งธ ์ธ๋ฑ์ค, ์ด์ ํ์ด์ง๊ทธ๋ฃน์ ๋ง์ง๋ง์ซ์๋ฅผ ๊ตฌํด์ +1์ ์์ผ์ฃผ๋ ํ์์ผ๋ก ๊ณ์ฐํ์๋ค.
ex) 5๊ฐ๋ก ํ์ด์ง ๊ทธ๋ฃน์ ๋๋ด์๋, 1 ~ 5 ์ 1, 6 ~ 10์ 6 ์ด ๋๋ค.
๐ ํ์ด์ง๋ค์ด์ ๋ก์ง
const searchParams = useSearchParams();
const p = Number(searchParams.get("p")) || 1;
์ด ๋ฐ์ดํฐ ๊ฐฏ์, ํํ์ด์ง๋น ๋ณด์ฌ์ค ์์ดํ
์, ํ์ด์ง๊ทธ๋ฃน์ ์๋ ํ๋กญ์ผ๋ก ๋ฐ์์ค๊ณ ํ์ฌํ์ด์ง๋ searchParams๋ฅผ ์ฌ์ฉํด ๋ถ๋ฌ์๋ค.
ํ์ด์ง ๊ณ์ฐ ๋ก์ง์ ์ง์ผ๋๊ธฐ ๋๋ฌธ์ searchParams๋ก ๋ถ๋ฌ์จ ์ฟผ๋ฆฌ์คํธ๋ง์ Number๋ฅผ ์ฌ์ฉํด ์ซ์๋ก ๋ณํํด์ค๋ค.
const pageArr = Array.from({ length: totalPages }, (_, i) => i + 1).splice(
startIdx - 1,
pageCount,
);
pokemon api๋ ์ด ์์ดํ
์๊ฐ "count: 1302" ์์ผ๋ก ๋ง ๋์ค๊ธฐ ๋๋ฌธ์ ๋ฐฐ์ด์ ๋ง๋ค์ด ์ค์ผํ๋ค.
๐ Array.from์ ์ฌ์ฉํด ์ธ๋ฑ์ค์์ 1์ฉ ํ๋ฌ์ค ๋๋ length๊ฐ totalPages์ธ ๋ฐฐ์ด์ ์์ฑ
๐ splice ํจ์๋ฅผ ์ฌ์ฉํด ํ์ฌ ํ์ด์ง ๊ทธ๋ฃน๋ง ๋ ๋๋ง ๋๋๋ก ํด์ค๋ค.
//์ด์ ํ์ด์ง๊ทธ๋ฃน ์ด๋
{pageGroup !== 1 && (
<li>
<button
onClick={() => goToPage(startIdx - 1)}
className="cursor-pointer rounded px-2 py-1"
>
<
</button>
</li>
)}
// ๋ค์ํ์ด์ง ๊ทธ๋ฃน ์ด๋
{startIdx + pageCount - 1 < totalPages && (
<li>
<button
onClick={() => goToPage(startIdx + pageCount)}
className="cursor-pointer rounded px-2 py-1"
>
>
</button>
</li>
)}
๐ ์ด์ ๋ฒํผ: ์ด์ ํ์ด์ง์ ๋ง์ง๋ง์ผ๋ก ์ด๋, ํ์ฌํ์ด์ง๊ฐ 1์ด ์๋๊ฒฝ์ฐ(์์ํ์ด์ง)๋ง ๋ฒํผ์ด ํ์ฑํ ๋๋ค.
๐ ๋ค์ ๋ฒํผ: ๋ค์ ํ์ด์ง์ ์ฒ์์ผ๋ก ์ด๋, ํ์ฌํ์ด์ง์ ๋ง์ง๋ง ํ์ด์ง ๋ฒํธ๊ฐ ์ด ํ์ด์ง ๊ฐฏ์๋ณด๋ค ์ ์๋๋ง ๋ฒํผ์ด ํ์ฑํ๋๋ค.
๐ ์ ์ฒด ์ฝ๋
"use client";
import { useRouter, useSearchParams } from "next/navigation";
export default function Pagination({
totalItems,
pageItems,
pageCount,
}: {
totalItems: number;
pageItems: number;
pageCount: number;
}) {
const searchParams = useSearchParams();
const p = Number(searchParams.get("p")) || 1;
const router = useRouter();
const totalPages = Math.ceil(totalItems / pageItems);
const pageGroup = Math.ceil(p / pageCount);
const startIdx = (pageGroup - 1) * pageCount + 1;
const pageArr = Array.from({ length: totalPages }, (_, i) => i + 1).splice(
startIdx - 1,
pageCount,
);
const goToPage = (num: number) => {
router.push(`/?p=${num}`);
};
return (
<ul className="mt-6 flex justify-center gap-2 text-sm">
{pageGroup !== 1 && (
<li>
<button
onClick={() => goToPage(startIdx - 1)}
className="cursor-pointer rounded px-2 py-1"
>
<
</button>
</li>
)}
{pageArr.map((page) => (
<li key={page}>
<button
className={`rounded px-3 py-1 ${p === page ? "bg-blue-500 text-white" : "bg-gray-200 text-black"}`}
onClick={() => goToPage(page)}
>
{page}
</button>
</li>
))}
{startIdx + pageCount - 1 < totalPages && (
<li>
<button
onClick={() => goToPage(startIdx + pageCount)}
className="cursor-pointer rounded px-2 py-1"
>
>
</button>
</li>
)}
</ul>
);
}
๐ ๊ฒฐ๊ณผ
๐ totalItems={150} pageItems={20} pageCount={5} ์ค์ ์ ๊ฒฐ๊ณผ
๐ง ์ค๋์ ํ๊ณ
์ด๋ฒํธ์์๋ ํ์ด์ง๋ค์ด์
์ ๊ตฌํํ๋๋ฐ, ๋ฒํผ ํด๋ฆญ ์์ฒด๋ ์ฌ์ด ๋ฐ๋ฉด, ๊ธฐ๋ฅ๊ตฌํ ์ ์ ํ์ด์ง๋ค์ด์
์๊ณ ๋ฆฌ์ฆ์ ์ง๋ ๋ถ๋ถ์ ๊ตฌ์ฑํ๋๊ฒ ์ค์ํ๋ค๊ณ ์๊ฐ ๋์๋ค.