[React] 페이지네이션(Pagination) 구현하기(ft. Typescript)

박기영·2022년 9월 30일
3

React

목록 보기
12/32

보통 게시판을 제작할 때 무한 스크롤 느낌이 아니라면
한번에 보여줄 게시물 개수를 제한하고, 페이지네이션(pagination)을 만들어 이동할 수 있게한다.
어떻게 만들 수 있을까?

필요한 것 1

  1. limit이라는 변수. 한 페이지에서 보여줄 게시물의 개수를 의미한다.
  2. page라는 state. 현재 페이지의 번호를 의미한다.
  3. offset이라는 변수. 첫 게시물의 인덱스를 의미한다.

페이지네이션 알고리즘 1

46개의 게시물이 존재한다고 가정해보자.
필자는 한 페이지에 10개의 게시물(limit)을 보여주고자한다.
그렇다면 몇 개의 페이지가 필요할까?

46 / 10 = 4.6

4.6 페이지가 필요하다. 그러나 페이지에 소수점 아래 자리는 있을 수 없으므로
반올림을 해야한다.
따라서, 5 페이지가 필요하다.

각 페이지에는 어떤 게시물들이 보여야할까?
1 페이지에는 0 ~ 9번 인덱스의 게시물이,
2 페이지에는 10 ~ 19번 인덱스의 게시물이,
3 페이지에는 20 ~ 29번 인덱스의 게시물이,
4 페이지에는 30 ~ 39번 인덱스의 게시물이,
5 페이지에는 40 ~ 45번 인덱스의 게시물이 보여야한다.

따라서, 각 페이지의 offset
0, 10, 20, 30, 40 이 된다.

여기서 offset의 식을 유추할 수 있다.

const offset = (page - 1) * limit;

이제 알고리즘을 알았으니 표현해보자.

현재 페이지에 해당하는 것만 보여주기

1 페이지에는 0 ~ 9번 인덱스의 게시물이 보여야한다.
46개의 게시물이 있는 상황에서 이를 어떻게 구현할 수 있을까?

바로 slice 메서드를 활용할 것이다.

페이지 번호와 인덱스의 관계를 따져보면 아래와 같은 코드를 만들 수 있다.

// LecturePage.tsx

lectureList
  .slice(offset, offset + limit)
  .map(
    (item: {
      userId: number;
      id: number;
      title: string;
      body: string;
    }) => (
		// ... //
    )
)

lectureList는 46개의 게시물이 들어있는 배열이다.
slice 메서드를 통해 offset 인덱스부터 offset + limit - 1번 인덱스까지를 복사한다.

1번 페이지에서는 offset이 0이었다.

0 + 10 - 1 = 9

따라서 0 ~ 9번 인덱스까지만 사용한다는 것을 알 수 있다.

어? 그러면 다른 페이지에 있는 애들은 어떻게 되는거죠?
잠시 후에 살펴보도록하자.

필요한 것 2

이번엔 페이지네이션 기능을 해줄 컴포넌트를 만들 것이다.
어떤 것이 필요할지 미리 생각해보자.

이번에는 필요한 것들을 위에서 만든 컴포넌트에서 props로 받아올 것이다.

  1. total이라는 변수. 게시물의 총 개수를 의미한다.
  2. limit이라는 변수. 한 페이지에서 보여줄 게시물의 개수를 의미한다.
  3. page라는 state. 현재 페이지의 번호를 의미한다.
  4. setPage라는 setState. page라는 state를 변경할 때 사용한다.

페이지네이션 컴포넌트에서 생성하는 것도 있다.

  1. numPages라는 변수. 페이지의 총 개수를 의미한다.

페이지네이션 알고리즘 2

앞서 살펴본 페이지네이션 알고리즘 1에서는
게시물의 개수를 분할하여, 몇 개를 어디에서, 어떤 것부터 보여줄지 정리한 바 있다.
이번에는 페이지네이션 컴포넌트를 제작할 때 어떻게 이 것들을 활용할 것인지 생각해보자.

5 페이지가 필요하니까 아래와 같은 형태로 만들어야겠다.

< 1 2 3 4 5 >

페이지 숫자를 만들기 위해서 게시물의 총 개수(total)를 알아야한다.

사실 위에서 이미 한번 구해봤다.

46 / 10 = 4.6
4.6 페이지는 없으니까 올림해서 5페이지.

즉, 아래와 같은 식이 나온다.

const numPages = Math.ceil(total / limit);

페이지의 총 개수를 알았으니 이제 더 필요한 것은 없다.
컴포넌트를 만들어보자.

페이지네이션 컴포넌트 구현

지금까지 얻은 정보들을 활용해서 우리가 만들어야하는 것은
크게 3가지다.

  1. < 버튼. 페이지를 앞으로 이동하는 버튼이다. 즉, 페이지 번호를 감소시킨다.
  2. 각 페이지 번호가 들어있는 버튼. 말그대로이다.
  3. > 버튼. 페이지를 뒤로 이동하는 버튼이다. 즉, 페이지 번호를 증가시킨다.

우선 <, > 버튼부터 만들어보자.

< 버튼, > 버튼

이 버튼들은 어떤 기능을 가져야할까?

페이지의 번호를 줄이고 늘리는 기능을 가져야한다.
그래야 1번 페이지에서 2번, 3번, 4번...페이지를 자유롭게 왔다갔다 할 수 있으니말이다.
우리는 이전 컴포넌트에서 props로 setPage를 받아왔다.
바로 이 기능을 구현하기 위해서 가져온 것이다.

그리고 페이지가 1보다 더 작아질 수 없게, numPages(최대 페이지 수)보다 커질 수 없게 만들어야한다.
이는 button 태그의 disabled 속성을 이용한다.
페이지 번호가 1이면 < 버튼을 비활성화하고,
페이지 번호가 5(최대)라면 > 버튼을 비활성화하면 된다.

이 기능들을 구현하면 아래와 같다.

// Pagination.tsx

// < 버튼
<button onClick={() => setPage(page - 1)} disabled={page === 1}>
  &lt;
</button>

// > 버튼
<button onClick={() => setPage(page + 1)} disabled={page === numPages}>
  &gt;
</button>

page라는 state는 offset이라는 변수에 영향을 준다.
따라서, 클릭 이벤트가 발생하면 page가 증가하거나 감소하게되고
이는 offset을 변경.
결과적으로 페이지에서 보여주는 게시물들이 바뀌게 되는 것이다.

1페이지에서 0 ~ 9번 인덱스가 보여지고 있을 때,
> 버튼을 클릭해서 page state를 증가시면
offset은 10으로 변하며, 이는 10 ~ 19번 인덱스를 보여주는 것을 의미한다.
왜냐? 우리는 게시물을 보여주는 페이지에서 slice를 사용해서
offset ~ offset + limit - 1의 게시물을 보여주도록 설정했기 때문이다.

각 페이지 번호 버튼

이번에는 가장 핵심인 페이지 번호 버튼을 만들어야한다.

1 2 3 4 5

이런 나열된 무엇인가를 보게되면, React를 사용하시는 분들은 아..map 쓰고싶다...
라고 하실 것 같다.
그렇다.

페이지 번호의 배열을 만들어서 map으로 찍어내면 된다.
numPages개 만큼 원소를 가지는 배열을 만들어주자.

const showedLecture = new Array(numPages).fill(0);  // [0,0,0,0,0]

이 예제에서는 5개였으니 0 ~ 4번 인덱스를 가지는 배열이 만들어질 것이다.

이 배열을 가지고 1,2,3,4,5 버튼을 만들어보자.

// Pagination.tsx

{showedLecture.map((item, index) => (
  <button
    key={index + 1}
    onClick={() => setPage(index + 1)}
    className={
      index + 1 === page
        ? // 현재 페이지의 버튼 스타일
        : // 다른 페이지 버튼 스타일
    }
  >
    {index + 1}
  </button>
))}

여기서 주의할 점은 인덱스가 0부터 시작하기 때문에
페이지 표시를 위해서는 index + 1을 해줘야한다는 것이다.

<, > 버튼에서의 onClick과 달리 page를 하나씩 증감하는게 아니라,
특정 페이지로 한번에 이동시키는 것이므로
setPage에는 index + 1을 넣어서 offset을 변경한다.

1번 페이지에서 0 ~ 9번 인덱스인 게시물을 보여주고 있다고 했을 때,
3번 페이지 버튼을 누르면 page가 3으로 변하게 되고,
이로인하여 offset이 20으로 변하게 된다.
게시물을 보여주는 컴포넌트에서는 slice로 인하여
offset ~ offset + limit - 1까지의 게시물을 보여주므로,
20 ~ 29번 인덱스인 게시물을 보여주는 것으로 바뀌게 된다.

결과

참고 동영상

참고 자료

Daleseo님 블로그

profile
나를 믿는 사람들을, 실망시키지 않도록

0개의 댓글