PF/Poketmon

[ํฌ์ผ“๋ชฌ ๋„๊ฐ ํ”„๋กœ์ ํŠธ] #8: ํŽ˜์ด์ง€๋„ค์ด์…˜ ๊ธฐ๋Šฅ ๊ตฌํ˜„

akii 2025. 6. 20. 19:00
๋ฐ˜์‘ํ˜•

 

๐Ÿ“ [ํฌ์ผ“๋ชฌ ๋„๊ฐ ํ”„๋กœ์ ํŠธ] #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"
      >
        &lt;
      </button>
    </li>
)}


// ๋‹ค์ŒํŽ˜์ด์ง€ ๊ทธ๋ฃน ์ด๋™
{startIdx + pageCount - 1 < totalPages && (
    <li>
      <button
        onClick={() => goToPage(startIdx + pageCount)}
        className="cursor-pointer rounded px-2 py-1"
      >
        &gt;
      </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"
          >
            &lt;
          </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"
          >
            &gt;
          </button>
        </li>
      )}
    </ul>
  );
}

 

 

๐ŸŽ‰ ๊ฒฐ๊ณผ

๐Ÿ‘‰ totalItems={150} pageItems={20} pageCount={5} ์„ค์ •์‹œ ๊ฒฐ๊ณผ

1ํŽ˜์ด์ง€

 

6ํŽ˜์ด์ง€

 

 

๐Ÿง  ์˜ค๋Š˜์˜ ํšŒ๊ณ 

์ด๋ฒˆํŽธ์—์„œ๋Š” ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ, ๋ฒ„ํŠผ ํด๋ฆญ ์ž์ฒด๋Š” ์‰ฌ์šด ๋ฐ˜๋ฉด, ๊ธฐ๋Šฅ๊ตฌํ˜„ ์ „์— ํŽ˜์ด์ง€๋„ค์ด์…˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์งœ๋Š” ๋ถ€๋ถ„์„ ๊ตฌ์„ฑํ•˜๋Š”๊ฒŒ ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ ๋˜์—ˆ๋‹ค.



 

๋ฐ˜์‘ํ˜•