도찐개찐

[React] Pagination 페이징 작성 하기 본문

React

[React] Pagination 페이징 작성 하기

도개진 2022. 4. 28. 15:22

1. Pagination 전용 앱 생성

# 앱설치
$ yarn create react-app pagination
# 설치 된 어플리케이션 경로 이동
$ cd pagination
# 필요 컴포넌트 설치
$ yarn add react-icons prop-types

react-icons : 

react에서 자체 배포하고 있는 아이콘 콤포넌트로 버튼 아이콘 사용에 활용하기 위해 설치 합니다.

아이콘 사용이 불필요하신 분들 께서는 설치 하지 않으셔도 됩니다.

 

prop-types : 

props 전달에 자료형을 체크 하기 위해 설치 하였으나 자료형 체크가 불필요하신 분들께서는 설치 하지 않으셔도 됩니다.

 

2. 작업 파일 및 import 파일 구조

아래 구초 내용을 확인 하셔서 미생성 구조만 생성해 주시면 됩니다.

.
├── App.js   # 기본 앱실행 컴포넌트로 Pagination 컴포넌트를 호출
├── css/
| └── Pagination.css
└── components/
  └── Pagination.js
 
 

3. App.js

 * 소스내 주석내용을 확인해 주시면 이해가 편하실 수 있을 거 같습니다.
// App.js

/*
* 필요 컴포넌트 및 css import
*/
import React, { useEffect, useState } from "react";
import Pagination from "./components/Pagination";
import './css/Pagination.css';

/**
* totalPosts - 총 데이터수 ( api 연동 및 데이터 베이스 값 등록 필요 임의 367)
* currentPage - 현재 페이지 위치 - 기본 1페이지
* postsPerPage - 페이지 내 리스팅 갯수 - 기본 10개 리스팅
* pageLimit - 페이징 버튼 출력 갯수 - 기본 5개 리스팅
*/
function App() {
  const [totalPosts, setTotalPosts] = useState(367); // 임의 값(데이터 연동 필요)
  const [currentPage, setCurrentPage] = useState(1);
  const [postsPerPage, setPostsPerPage] = useState(10);
  const [pageLimit, setPageLimit] = useState(5);
  
  // 자식(Pagination) 컴포넌트에서 자신(App)의 현재 페이지 값을 변경 할 수 있도록 함수화 처리
  const updateCurrentPage = (page) => {
    setCurrentPage(page);
  }
  
  /*
  * <Pagination /> 생성시 아래와 같은 props 값 전달
  * 설명은 상단 주석 내용 확인
  */
  return (
    <div className="App">
      <Pagination
          currentPage={currentPage}
          postsPerPage={postsPerPage}
          pageLimit={pageLimit}
          totalPosts={totalPosts}
          paginate={updateCurrentPage}
      />
    </div>
  );
}

export default App;
 
 

4. components/Pagination.js

// components/Pagination.css
/*
* 필요 컴포넌트 임포트
* react-icons 는 별도 아이콘 필요시 사용 불필요한 경우 삭제
*/
import React, { Component } from 'react';
import { MdChevronLeft, MdChevronRight, MdFirstPage, MdLastPage } from "react-icons/md";
import datatype from "prop-types";

/**
 * @author Dev-Truly
 * @param currentPage       현재 페이지 번호
 * @param postsPerPage      페이지 리스팅 수
 * @param totalPosts        게시글 총 갯수
 * @param pageLimit         페이징 버튼 출력 갯수  
 * @param paginate          부모 상태 조정 함수
 * @returns {JSX.Element}
 */

class Pagination extends Component {
	/**
    * GetBlockInfo() - 전달 받은 props 데이터 활용하여 페이징 알고리즘 처리 함수
    */
    GetBlockInfo = () => {
        // 최종 페이지 번호 = 올림(총 데이터 건수 : 367 / 페이지 리스팅 수 : 10 = 36.7 올림 37 페이지) 
        let lastPage = Math.ceil(this.props.totalPosts / this.props.postsPerPage);
		
        // 현재 페이징 블럭 계산 = 올림(현재 페이지 위치 : 3 / 페이징 버튼 출력수 5 = 0.6 올림 1페이지)
        let block = Math.ceil(this.props.currentPage / this.props.pageLimit);
        
        // 블럭내 시작 페이지 계산 = ((현재페이징 블럭 : 1 - 1 = 0) * 페이징 버튼 수 : 5 = 0) + 1 = 1 페이지 시작
        let start = ((block - 1) * this.props.pageLimit) + 1;
        
        // 블럭내 종료 페이지 계산 = 낮은 수(마지막 페이지, (시작페이지 + 페이징 버튼수));
        let end = Math.min(lastPage, (start + this.props.pageLimit) - 1);

        return {lastPage, start, end}
    }
	
    /**
    * GetPagination() 계산된 페이징 정보 활용하여 components 생성
    */
    GetPagination({...blockInfo}) {

        //console.log();
        
        // 담을 배열 변수 생성
        let components = [];
        
        // 시작 페이지가 1보다 크면 출력
        // 1번 페이지 이동 버튼 활성화
        // icon MdFirstPage 컴포넌트 확인 불필요시 삭제 후 대체 필요
        if (blockInfo.start > 1)
            components.push(
                <li
                    className="page-first"
                    onClick={e=>this.props.paginate(1)}>
                    <MdFirstPage size={20} />
                </li>
            );
		
        // 현재 페이지가 1보다 크면 생성
        // 직전 페이지 이동 버튼 활성화
        // icon MdChevronLeft 컴포넌트 확인 불필요시 삭제 후 대체 필요
        if (1 < this.props.currentPage)
            components.push(
                <li
                    className="page-previous"
                    onClick={e=>this.props.paginate(this.props.currentPage - 1)}
                >
                    <MdChevronLeft size={20} />
                </li>
            );
		
        // 블록 시작 부터 종료 지점까지 반복 실행
        // 일반 숫자 버튼 활성화
        // currentPage(현재 페이지)와 반복중 증가 연산자의 값이 같으면 클릭 기능 비활성화
        // this.props.paginate(i) 부모 컴포넌트에서 전달 받은 setState 함수 처리
        for (let i = blockInfo.start; i <= blockInfo.end; i++) {
            let currentPageClass = "";
            if (this.props.currentPage === i) currentPageClass = "current-page";
            components.push(
                <li
                    className={`page-number ${currentPageClass}` }
                    onClick={
                        (currentPageClass === "") ?
                            (e) => this.props.paginate(i) : () => null
                    }
                >
                    {i}
                </li>
            );
        }
		
        // 현재 페이지가 마지막페이지보다 작으면 생성
        // 직후 페이지 이동 버튼 활성화
        // icon MdChevronRight 컴포넌트 확인 불필요시 삭제 후 대체 필요
        if (this.props.currentPage < blockInfo.lastPage)
            components.push(
                <li
                    className="page-next"
                    onClick={e=>this.props.paginate(this.props.currentPage + 1)
                    }>
                    <MdChevronRight size={20} />
                </li>
            );
		
        // 블록 종료점 페이지가 마지막페이지보다 작으면 생성
        // 마지막 페이지 이동 버튼 활성화
        // icon MdLastPage 컴포넌트 확인 불필요시 삭제 후 대체 필요
        if (blockInfo.end < blockInfo.lastPage)
            components.push(
                <li
                    className="page-last"
                    onClick={e=>this.props.paginate(blockInfo.lastPage)}
                >
                    <MdLastPage size={20} />
                </li>
            );
		
        // 삽입 된 컴포넌트 리턴
        return (
            <ul className="page-list-group">
                {components}
            </ul>
        );
    }
	
    
    render() {
        let {
            currentPage,
            postsPerPage,
            pageLimit,
            totalPosts,
            paginate
        } = this.props;
		
        // 상기 생성 된 함수 호출
        return (
            <>
                {this.GetPagination(this.GetBlockInfo())}
            </>

        );
    }
}
// 자료형 처리
Pagination.propTypes = {
    currentPage: datatype.number,
    postsPerPage: datatype.number,
    totalPosts: datatype.number,
    paginate: datatype.func,
}

export default Pagination;
 
 

5. css/Pagination.css

아래 소스를 적용해 주시면 아래 이미지와 같이 현재 페이지(파란색), 마우스오버(빨간색), 기본(검정) 디자인을 사용하실 수 있습니다.

Pagination.js 에서 사용되고 있는 클래스들과 호환 되어 있는 소스로 디자인 변경이 필요하시면 해당 소스에서 수정해 주시면 됩니다.

/* css/Pagination.css */

.page-list-group {
    display: inline-block;
}
.page-list-group > li {
    list-style: none;
    float:left;
    padding: 6px 7px;
    margin: 5px;
    line-height: 19px;
    font-size: 15px;
    /*border:solid 1px lightgray;*/
    border-radius: 5px;
    height:20px;
    cursor: pointer;
    box-shadow: 0px 1px 3px lightgray;
}

.page-list-group > li.page-number {
    padding-left: 8px;
    padding-right: 8px;
}

.page-list-group > li:hover,
.page-list-group > li.current-page {
    padding: 5px 6.5px 5px 6.5px;
    font-weight: bold;
    border:solid 1px lightgray;
}

.page-list-group > li.current-page {
    color: cornflowerblue;
    cursor: inherit;
}

.page-list-group > li:hover:not(.current-page) {
    color: red;
    border:solid 1px lightgray;
}

.page-list-group > li.page-first:hover,
.page-list-group > li.page-previous:hover,
.page-list-group > li.page-next:hover,
.page-list-group > li.page-last:hover {
    padding: 5px 6px 5px 6px;
}

 

공유 소스 경로

https://github.com/dev-truly/react-pagination

 

GitHub - dev-truly/react-pagination

Contribute to dev-truly/react-pagination development by creating an account on GitHub.

github.com

 

728x90

'React' 카테고리의 다른 글

[React] Props 자료형(propType) 선언 하기  (0) 2022.04.28
[React] Props VS State  (0) 2022.04.28
[React] jQuery 사용하기  (0) 2022.04.25
[MacOS] React 설치 하기  (0) 2022.04.25
React 생명주기 메소드  (0) 2022.04.21
Comments