jh.nrtv

Next.js Server Component 에서 페이지네이션 구현하기 본문

Next.js

Next.js Server Component 에서 페이지네이션 구현하기

wlgus3 2023. 7. 22. 00:53

Next.js와 MongoDB를 사용한 게시판을 만들고 있으며 

게시판의 페이지네이션을 구현하는 과정을 기록해보고자 한다. 

현재 내 실력과 상황에서 현실적인 구현방법을 고민한 것이며, 분명 이보다 훨씬 나은 방법이 있을 것이라 확신한다.

 

다음은 기존에 게시판의 글 목록을 보여주는 소스코드이다. 

// /community/page.tsx

import { connectDB } from "@/util/database";

export default async function Community() {
  const client = await connectDB;
  const db = client.db("uppernight");

  const result = await db
    .collection("community")
    .find()
    .sort({ _id: -1 })
    .toArray();

  return (
    <div>
      <h2> 노력 자랑 게시판</h2>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <div> 글 목록  </div>
        ...
      </div>
      <div className="community">
        {result?.map((el, index) => {
          return (
            <Content
              element={el}
              key={index}
              // result={result}
            />
          );
        })}
      </div>
    </div>
  );
}

 

import 받은 connetDB에는 

import { MongoClient } from "mongodb";

를 비롯해서 MONGODB_URI를 이용해서 DB와 단순 연결하는 기본 세팅 코드가 있다. 

 

따라서 connectDB를 통해서 DB와 연결한 후에 원하는 데이터를 요청하고 , Array로 변환하고 .map을 통해서 화면에 뿌려주는 간단한 구성이다. 

 

이전에 React를 통해서 페이지네이션을 간단하게 구현한 적이 있기 때문에 해당 코드와 비슷하게 구현하면 쉽게 페이지네이션에 성공할 것이라고 생각했다.

문제상황

하지만 이 상황에서 문제가 발생했다. 

  • 기존 페이지네이션 코드는 useEffect와 useState를 사용해서  page를 관리하고 , page가 바뀔 때마다 데이터를 요청하는 코드로 구성되었다. 
// 아래와 같이 페이지네이션 컴포넌트에 props로 필요한 것들을 내려주는 형태였다.
<Paging page={page} setPage={setPage} />
  • 하지만 위 글목록 코드는 Next.js의 서버 컴포넌트로 useState등의 hook이 사용이 불가능했다. 
  • 따라서 PostsWrapper.ts를 따로 Client Component로 선언해서 그 안에서 hook을 사용해서 구현하려고 시도했으나
  • 위의 ConnectDB를 사용해서 데이터 요청을 하기 위해서는 async await의 비동기처리가 필요한데 Client Component에서는 사용이 불가능했다. ( 대신 use(...) 라는 비동기 대체 hook을 제공하기 위해서 준비중이라고 한다.)
  • 즉, 정상적인 Data 요청을 위해서는 Server Component를 사용해야 하는데, 페이지네이션을 구현하기 위해서는 시시때때로 바뀌는 page 숫자를 반영하기 위해서는 Client Component를 사용해야 했기 때문에 두 가지 모두를 만족하면서 구현하기가 나에게는 어려웠다. 

 

해결? 

결국 기존에 생각했던 페이지네이션 방법을 포기했다. 

그 대신에 url parameter를 사용해서 페이지네이션 구현을 시도했다. 

먼저 아래와 같은 파일 구성으로 Next.js가 parameter를 이용해서 라우팅 하도록 유도해준다.

이렇게 세팅하고 커뮤니티 탭을 누르면 '.../community/1' 로 라우팅되도록 해서 1페이지가 초기값이 되도록 한다.

다음으로는 서버 컴포넌트에서 url parameter에 접근할 수 있도록 세팅해주고 

//  /community/[page]

export default async function Community({
  params, //쿼리데이터 가져오기위한 코드
}: {
  params: { page: string };
})
{
return (
<div>
...
<div/>)
}

파라미터에 있는 page 값에 접근해서 현재 페이지의 값을 넣어준다. 

  console.log(params);
  let page = Number(params.page); //파라미터에서 페이지 가져와서 현재 페이지 값 넣어주기

다음으로는 .skip() .limit() 등을 통해서 원하는 데이터가 올 수 있도록 한다. 

  const client = await connectDB;
  const db = client.db("uppernight");
  const postcount: number = await db.collection("community").count();

  console.log(params);
  let page = Number(params.page); //파라미터에서 페이지 가져와서 현재 페이지 값 넣어주기 
  
  const result = await db
    .collection("community")
    .find()
    .sort({ _id: -1 })
    .skip((page - 1) * 6)	//원하는 데이터 가져오도록 설정->이전 페이지의 글들은 스킵하도록 
    .limit(6)
    .toArray();

 

이전페이지, 다음페이지에 접근할 수 있는 버튼을 만들어준다. 

단, 현 페이지가 1페이지라면 이전 페이지 버튼은 안보이도록 해준다. 

<div className="pagination_box">
      {page != 1 ? (			
        <Link href={`community/${page - 1}`}>
          <button>이전 페이지</button>
        </Link>
      ) : null}

      <div> {page}</div>	// 현 페이지 표시 

      <Link href={`community/${page + 1}`}>
        <button>다음 페이지</button>
      </Link>
</div>

 

 

전체 코드

//  /community/[page]

export default async function Community({
  params, //쿼리데이터 가져오기위한 코드
}: {
  params: { page: string };
}) {
  const client = await connectDB;
  const db = client.db("uppernight");
  const postcount: number = await db.collection("community").count();

  console.log(params);
  let page = Number(params.page); //파라미터에서 페이지 가져와서 현재 페이지 값 넣어주기 
  
  const result = await db
    .collection("community")
    .find()
    .sort({ _id: -1 })
    .skip((page - 1) * 6)	//원하는 데이터 가져오도록 설정->이전 페이지의 글들은 스킵하도록 
    .limit(6)
    .toArray();

  return (
    <div>
      <h2> 노력 자랑 게시판</h2>
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <div> 오늘의 노력에 대해서 자랑해주세요!</div>
      </div>
      <div className="community">
        {result?.map((el, index) => {
          return (
            <Content
              element={el}
              key={index}
              // result={result}
            />
          );
        })}
      </div>
      <div className="pagination_box">
      {page != 1 ? (			
        <Link href={`community/${page - 1}`}>
          <button>이전 페이지</button>
        </Link>
      ) : null}

      <div> {page}</div>

      <Link href={`community/${page + 1}`}>
        <button>다음 페이지</button>
      </Link>
      </div>
    </div>
  );
}

 

실행화면 ( .../community/2 )

 

구현은 됐으나, 하면서도 이게 맞나 .. 하는 생각이 든다. 

분명 더 나은 방법이 있을 거라고 생각한다.

더 공부하면서 고민해보아야겠다. 🥹

 

 

 

 


참조링크 

 

[MongoDB] 강좌 4편 find() 메소드 활용 – sort(), limit(), skip() | VELOPERT.LOG

이번 강좌에선 find() 메소드를 더욱 더 활용하기 위해 필요한 sort(), limit(), skip() 메소드에 대해 배워보겠습니다. 그냥 find() 메소드를 사용하면 criteria 에 일치하는 모든 document 들을 출력해주기

velopert.com

 

next.js 서버컴포넌트에서 쿼리 파라미터 접근 

 

How to get query params using Server component (next 13)

Next13 is out, and they advise to use the new app directory where every component is by default a "Server Component" Inside a "server Component", you can use: async/await to fe...

stackoverflow.com

 

 

'Next.js' 카테고리의 다른 글

Next.js 프로젝트에 Quill Editor 사용하기  (0) 2023.07.25
Next.js 13에 metadata 설정하기  (0) 2023.07.19