페이지네이션(with server)

Donggu(oo)·2023년 11월 18일
0

React 기능 구현

목록 보기
9/14
post-thumbnail

1. 전체 데이터와 한 페이지에 보여줄 데이터 불러오기


  • 서버로부터 전체 데이터와 한 페이지에 보여줄 데이터를 각각 불러온다.

  • 전체 데이터를 불러오는 이유는 전체 데이터의 개수(length)를 구하여 총 몇 페이지가 필요한지 계산하기 위함이다.

  • 그리고 Pagination 컴포넌트로 전체 페이지의 길이(allService.length), 페이지 번호(page), 페이지 당 보여줄 데이터 개수(limit)을 props로 넘겨준다.

import axios from "axios";
import ServiceItem from "@/components/service/manage/ServiceItem";
import Pagination from "@/components/common/Pagination";

// 전체 데이터
const getAllService = async () => {
  const res = await axios.get(`http://localhost:3001/service`);
  const data = await res.data;
  return data;
};

// 한 페이지에 보여줄 데이터
const getService = async (page: any, limit: any) => {
  const res = await axios.get(`http://localhost:3001/service?_page=${page}&_limit=${limit}`);
  const data = await res.data;
  return data;
};

export default async function Manage({ searchParams: { page, limit } }: any) {
  const allService = await getAllService();
  const service = await getService(page, limit);

  return (
    <div className='flex flex-col gap-y-[40px]'>
      <ul className='border-y border-solid border-[#DFDFDF]'>
        {service.map((data: any) => {
          return <ServiceItem key={data.id} data={data} />;
        })}
      </ul>
	  // 전체 페이지의 길이(allService.length), 페이지 번호(page), 페이지 당 보여줄 데이터 개수(limit)을 props로 전달
      <Pagination allServiceLength={allService.length} page={page} limit={limit} />
    </div>
  );
}

위는 Next js server component 환경에서 개발한 코드라 searchParams props로 page와 limit의 쿼리 스트링 값을 받아왔지만 useSearchParams hook을 활용하여 구할 수도 있다.

import { useSearchParams } from 'react-router-dom';

const [serchParams] = useSearchParams();

const page = searchParams.get('_page');
const limit = searchParams.get('_limit');

2. 페이지네이션 컴포넌트 로직


  • props로 전달받은 전체 페이지 개수(allService.length)를 페이지 당 보여줄 데이터 개수(limit)로 나누어 총 몇 페이지(totalPage)가 필요한지 계산한다.

  • generatePaginationNumbers 함수로 한 페이지 블록에 몇 개의 페이지를 보여줄지 계산하여 페이지 번호가 담긴 배열을 반환한다. 아래 코드는 현재 페이지 좌우로 각각 2개의 페이지를 더 보여주어 1부터 5까지의 페이지를 보여주며 6페이지로 이동 시 6페이지부터 10페이지를 보여준다.

  • 아래 gif의 왼쪽 하단 터미널을 보면 버튼을 클릭할 때마다 페이지 번호가 담긴 배열이 생성되는 것을 확인할 수 있다. 그 후 generatePaginationNumbers 함수로 생성된 generatedNumbers를 map으로 뿌려주면 각 페이지 버튼들이 생성된다.

  • 이제 각 버튼 클릭 시 해당 번호의 페이지로 이동하도록 Link를 추가한다.
import Link from "next/link";
import ChevronLeftIcon from "@/assets/icons/ChevronLeftIcon";
import ChevronRightIcon from "@/assets/icons/ChevronRightIcon";

interface PaginationProps {
  allServiceLength: number;
  page: string;
  limit: string;
}

export default function Pagination({ allServiceLength, page, limit }: PaginationProps) {
  // 전체 페이지 개수
  let totalPage = Math.ceil(allServiceLength / Number(limit));
	
  // 페이지 블록 생성
  let generatePaginationNumbers = (page: string, totalPage: number) => {
    let paginationArray = [];

    // 현재 페이지 좌우로 2개의 버튼만 보이도록 설정
    for (let i = Number(page) - 2; i <= Number(page) + 2; i++) {
      if (i < 1) continue;
      if (i > totalPage) break;
      paginationArray.push(i);
    }

    return paginationArray;
  };

  // 각 페이지 버튼이 담긴 배열
  let generatedNumbers = generatePaginationNumbers(page, totalPage);

  return (
    <div className='flex justify-between items-center px-[20px]'>
      <div className='text-[14px] text-[#6C757D]'>{allServiceLength}</div>
      <div className='flex items-center gap-x-[10px]'>
        {Number(page) - 1 >= 1 && (
          <Link
            href={`/service/manage?page=${Number(page) - 1}&limit=${limit}`}
            className='flex justify-center items-center w-[25px] h-[30px] bg-[#DFDFDF] rounded-sm'
          >
            <ChevronLeftIcon />
          </Link>
        )}

		// 각 페이지 이동 버튼
        {generatedNumbers.map((number) => (
          <Link
            key={number}
            className={`${
              Number(page) === number ? "bg-primary text-white" : "text-[#6C757D]"
            } flex justify-center items-center w-[25px] h-[30px] text-[14px]  rounded-sm`}
            href={`/service/manage?page=${number}&limit=${limit}`}
          >
            {number}
          </Link>
        ))}

        {Number(page) + 1 <= totalPage && (
          <Link
            href={`/service/manage?page=${Number(page) + 1}&limit=${limit}`}
            className='flex justify-center items-center w-[25px] h-[30px] bg-[#DFDFDF] rounded-sm'
          >
            <ChevronRightIcon />
          </Link>
        )}
      </div>
    </div>
  );
}
{generatedNumbers.map((number) => (
  <Link
    key={number}
    className={`${
      Number(page) === number ? "bg-primary text-white" : "text-[#6C757D]"
    } flex justify-center items-center w-[25px] h-[30px] text-[14px]  rounded-sm`}
    href={`/service/manage?page=${number}&limit=${limit}`}
  >
    {number}
  </Link>
))}

0개의 댓글