Pagination 빠개기 - ganOverflow 회고

hongregii·2023년 8월 4일
0
post-thumbnail

부가 라이브러리 사용 없이 React와 NestJS (+TypeOrm)으로 Pagination을 구현해봤다.

TypeOrm + NestJS

우선 백엔드인 NestJS 코드는 이렇다. (Service만 보겠다)

  • Controller에서 queryString으로 page를 받는다. 우리 게시판은 무조건 게시물을 10개씩만 보여줄 거기 때문에, page만 받아오면 됐다.

  • TypeOrm이 제공하는 findAndCount 메서드를 사용했다. React에서 총 게시물 수를 알아야 마지막 페이지에서 버튼 렌더링을 멈출 수 있기 때문.
    findAndCount 메서드는 옵션에 맞게 SELECT 구문을 날리고, COUNT까지 해준다.
    리턴값이 두개 나오기 때문에 [posts, postCount] 로 구조 분해 할당하여 리턴값을 받아온다.

  • 다른 find 옵션도 있지만 블로그에서는 생략, Paging과 관련된 take, skip만 설명하겠다. 이건 TypeOrm의 findAndCount 뿐 아니라, find계열 메서드에는 모두 붙어있는 옵션이다.

    • take는 SQL의 LIMIT으로 바뀐다. (N개만 SELECT하기) 우리는 무조건 10개만 받기로 했기 때문에, 직관적으로 10을 넣어뒀다.
    • skip은 SQL의 OFFSET으로 바뀐다. (M번째부터 SELECT하기) 한 페이지당 게시물을 10개씩 받아오고, page 값을 queryString으로 받기 때문에, page = 1 이면 1번째부터, page = 4 라는 요청을 받으면 31번째 게시물부터 40번째 게시물을 SELECT해야 한다.

React (사실 NextJS 13.4 App Router)

fetch 방식

AppRouter의 서버컴포넌트가 권고하는 fetch 패턴대로 컴포넌트 안에 fetch를 넣었다.


// NextJS 공식문서 코드입니다. 패턴 파악을 위해...

async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
 
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }
 
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
 
  return <main></main>
}

아래는 작성한 코드. getAllChatPost 함수 안에 GET 요청 fetch가 들어있다.

PostPage 컴포넌트가 처음 렌더링 될 때에 fetch를 자동으로 실행하는 것. 그래서 fetch를 해오는 쪽을 바꾸거나 할 필요가 없었다. 다만, SearchParams 를 사용해서 클라이언트 페이지의 queryString백엔드로 가는 API 안에 그대로 넣기로 했다.

그리고 페이징 버튼마다 <Link> 태그를 달아서, href 안에 queryString만 추가하면 끝.

즉, 코드 작동 순서는 이렇다.

  1. 클라이언트에서 게시판에 접속할 때, ?page=${페이지번호} 가 URL에 붙어있는 <Link> 태그를 누른다.
  2. PostPage 컴포넌트의 fetch 함수 실행 → NestJS도 해당 page 데이터를 queryString으로 받아옴.
  3. findAndCount 메서드 실행 → DB 커넥션 이후 클라이언트로 데이터 돌려줌
  4. PostPage 컴포넌트는 totalPage 값을 계산 (10개씩이니까 Math.ceil(totalCount / 10))

Pagination 컴포넌트


currentPagetotalPagePostPage 컴포넌트에서 prop으로 내려준다.

  • pagingButtons는 길이 5개의 Array로 고정했다. 이 안의 요소마다 index 지정해서 값을 할당하는식으로 구현했다. 이 배열을 state로 선언하지 않은 이유는, prop으로 받아온 currentPagetotalPage에 따라서 렌더링이 결정되는데, 이것들이 이미 Reactive Value이기 때문이다. State를 참조하는 State를 또 만드는 것은 불필요하고, 받아온 값이 바뀔 때마다 리렌더링이 일어나기 때문에, 굳이 State로 만들 필요는 없다. 게다가 매 리렌더링마다 State를 유지할 필요가 없기 때문이기도 하다.

  • 경우의 수는 총 3가지다.

    • currentPage가 1, 2인 경우에는 [<][1] [2][3] [4][5] [>] 를 뿌려주고, currentPage인 [1] 이나 [2]에 색깔을 바꿔줘야 한다.

    • currentPage가 3 이상이면 ex. currentPage = 7
      [<][5] [6][7] [8][9] [>] 이렇게 뿌려주고, 가운데 7에 색깔을 바꿔줘야 한다.

    • currentPagetotalPage 랑 같거나, 1칸 작아지면
      ex. totalPage = 6, currentPage = 5
      [<][2] [3][4] [{{5}}][6] [>]
      이렇게 5에 체크를 해줘야 한다.

*e 값은 pagingButtons.map 안의 콜백이 받는 인자다.

profile
잡식성 누렁이 개발자

0개의 댓글