Posts [React] 리액트 페이지네이션(pagination) 구현하기 (2)
Post
Cancel

[React] 리액트 페이지네이션(pagination) 구현하기 (2)


리액트 페이지네이션 구현하기(React pagination) (2)


이전 포스팅 : https://chanhuiseok.github.io/posts/react-12/


📘 페이지네이션 컴포넌트 만들기

페이지네이션을 구현하기 위해, 숫자 리스트를 표시하는 컴포넌트를 만들어 보겠습니다.

그 전에, App.js에서 페이지네이션 관련 변수들과 함수를 정의하겠습니다.

📜 App.js 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import "./App.css";
import React, { useState, useEffect } from "react";
import axios from "axios";
import Posts from "./Posts";
import Pagination from "./Pagination";

function App() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(false);
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage, setPostsPerPage] = useState(10);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      const response = await axios.get(
        "https://jsonplaceholder.typicode.com/posts"
      );
      setPosts(response.data);
      setLoading(false);
    };
    fetchData();
  }, []);

  /* 새로 추가한 부분 */
  const indexOfLast = currentPage * postsPerPage;
  const indexOfFirst = indexOfLast - postsPerPage;
  const currentPosts = (posts) => {
    let currentPosts = 0;
    currentPosts = posts.slice(indexOfFirst, indexOfLast);
    return currentPosts;
  };
  /*                 */

  return (
    <div className="App">
      <Posts posts={currentPosts(posts)} loading={loading}></Posts>
      <Pagination
        postsPerPage={postsPerPage}
        totalPosts={posts.length}
        paginate={setCurrentPage}
      ></Pagination>
    </div>
  );
}

export default App;
  • 총 데이터를 postsPerPage 만큼 등분해서 보여줍니다.
  • 현재 예시에서는 총 100개의 데이터를 10등분해서, 1~10까지 보여주고, 그 다음 11~20까지 보여주고, 그 다음 21~30, … 이렇게 배열의 데이터를 나누어서 보여주어야 합니다.
  • 이를 위해 indexOfLast, indexOfFirst 변수를 선언합니다.

    • 이후 해당 페이지의 첫 번째와 마지막 인덱스 번호 값을 구합니다.
    • 예를 들어, 첫 번째 페이지의 가장 처음 인덱스는 1번이고, 마지막은 10번이 됩니다. 두 번째 페이지는 11번 ~ 20번이 됩니다.
  • 처음과 끝 인덱스 번호를 구한 다음, currentPosts 함수를 통해, 100개의 배열 데이터를 slice 함수로 분할해 줍니다. 이후 분할된 새로운 배열을 리턴합니다.

    • slice 함수는 begin, end 값을 인자로 넘겨받아 배열의 begin번째 ~ end-1 번째 까지의 복사본을 새롭게 반환해 주는 함수입니다. 페이지네이션을 구현할 때 적합하다고 볼 수 있습니다.
  • return 문 안에서 <pagination> 로 컴포넌트를 새로 사용하였습니다. import Pagination from './Pagination'; 을 적고 사용합니다. 이 때, 여기에 인자로 postsPerpage, totalPosts, paginate를 넘깁니다. 각각 페이지 당 포스트 수, 전체 포스트 갯수, currentPage 상태를 변경하는 setter 함수 를 넘깁니다.
  • 사용자가 선택한 페이지 넘버에 따라, currentPage 의 값이 변경되도록 구현할 것입니다. 예를 들어 사용자가 3번을 선택하면, currentPage 상태값을 사용한 indexOfLast, indexOfFirst 변수의 값도 변경되면서 분할되는 데이터들도 달라질 것입니다. 따라서 setCurrentPage 함수를 Pagination 컴포넌트에 인자로 넘긴 것입니다.
    • 즉, useState 로 상태값을 만들고, 해당 상태값을 변경하는 setter 함수를 활용한 것입니다.

📜 Pagination.js 생성 및 작성

페이지 리스트를 보여 줄 컴포넌트를 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import React from "react";
import styled from "styled-components";

const PageUl = styled.ul`
  float: left;
  list-style: none;
  text-align: center;
  border-radius: 3px;
  color: white;
  padding: 1px;
  border-top: 3px solid #186ead;
  border-bottom: 3px solid #186ead;
  background-color: rgba(0, 0, 0, 0.4);
`;

const PageLi = styled.li`
  display: inline-block;
  font-size: 17px;
  font-weight: 600;
  padding: 5px;
  border-radius: 5px;
  width: 25px;
  &:hover {
    cursor: pointer;
    color: white;
    background-color: #263a6c;
  }
  &:focus::after {
    color: white;
    background-color: #263a6c;
  }
`;

const PageSpan = styled.span`
  &:hover::after,
  &:focus::after {
    border-radius: 100%;
    color: white;
    background-color: #263a6c;
  }
`;

const Pagination = ({ postsPerPage, totalPosts, paginate }) => {
  const pageNumbers = [];
  for (let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
    pageNumbers.push(i);
  }
  return (
    <div>
      <nav>
        <PageUl className="pagination">
          {pageNumbers.map((number) => (
            <PageLi key={number} className="page-item">
              <PageSpan onClick={() => paginate(number)} className="page-link">
                {number}
              </PageSpan>
            </PageLi>
          ))}
        </PageUl>
      </nav>
    </div>
  );
};

export default Pagination;

숫자가 나타나는 컴포넌트에 약간의 css를 첨가해 보았습니다. styled-components를 활용한 것입니다. 이를 활용해 스타일 적용 자체도 컴포넌트를 작성하듯이 할 수 있습니다.

중요한 코드는 하단의 const Pagination~ 부터입니다.

  • Pagination 컴포넌트는 postsPerPage, totalPosts, paginate 를 인자로 넘겨받았습니다.
  • pageNumbers의 경우, 총 페이지 넘버 수를 계산한 뒤 웹 페이지에서 보여주기 위하여, 전체 포스트 갯수를 페이지 당 포스트 갯수로 나눈 값으로 배열을 만든 것입니다.
    • 포스트 개수가 100개이고, 페이지 당 포스트 개수를 10개로 지정할 경우 1 2 3 4 5 6 7 8 9 10 이 생성될 것입니다.
  • return문 내부를 보겠습니다.
    • 앞서 구한 pageNumbers 배열을 map 함수를 이용해 요소 하나씩 꺼내면서 <li> 로 만들고 클릭 이벤트를 걸어 주었습니다.
    • 이 때 <li> 하위에 <span>을 만들어 거기에 숫자를 표시했고({number}), <span>onClick 이벤트를 작성하였습니다. 화살표 함수로 작성한 것이며, onClick={() => paginate(number)} 부분에서 paginate 함수는 앞서 받은 setCurrentPage 함수이며, 클릭할 때 setCurrentPage 함수의 인자로 number를 넘겨 currentPage 상태값을 변경하는 것을 구현한 것입니다.
    • 따라서 사용자가 숫자를 클릭하면, currentPage 상태값이 변경되고, 이에 따라 App.js에서 작성한 대로 배열 데이터 값이 분할되어 화면에 표시될 것입니다.

이제 다 왔습니다! 실행해 보면 아래와 같이 표시될 것입니다.

실제로 리액트 프로젝트를 통해 웹 페이지를 만들 때, 다량의 데이터를 가져온 다음 간편하게 페이지네이션을 작성하는 방법에 대하여 알아보았습니다.

이것을 진행하면서 중요하게 짚고 가야 할 것들에는 useEffect, useState 등의 hook 사용과, async, await, axios 등으로 데이터를 가져오는 방법들 이라고 생각합니다.