간단한 알고리즘을 통해서 배우는데
저는 일단 컴포넌트화 해서 이번 포트폴리오 때 계속 재사용 예정이라 그것을 염두하고 설계 하겠습니다.
전에 했지만 그래도 다시 페이징 할 때 사용할 알고리즘을 정리해보겠습니다.
예를 들어보면 57개의 글 목록을 받았을 때
한 페이지에 10개씩 보여준다고 했다면 총 6페이지가 되고 마지막 페이지는 7개의 글만 나올 것입니다.
크게 두 구역을 생각하면 됩니다.
State를 먼저 생각해보겠습니다.
구현해야 할 목록
1. 한 페이지당 몇 개의 글을 보여줄 것 인가.
2. 최대 페이지 버튼은 몇 개로 지정할 것 인가.
3.
TotalPost => 받아온 총 게시판 글들
ViewNumber => 한 페이지에 보여줄 게시판 글
Page => 현재 페이지 (기본 1)
그리고 Pagenaition의 페이지 번호는 10(얼마든지 변경가능)개를 넘을 시 다음 버튼을 통해
예를 들어 1,2,3,4,5,6,7,8,9,10 이고 Page가 10을 넘어가거나 다음버튼을 눌렀을 때
다음 11,12,13,14,15,16,17,18,19,20 이런 식으로 넘어가게끔 보여줍니다.
slice함수와 map 함수를 사용해서 간단하게 만들어 봅니다.
PostLength : axios든 ajax든 가져온 게시판 배열 길이를 그대로 넣어줍니다.
oneViewNumber : 한페이지에 몇 개를 보여줄지 정해주는 글 입니다.=
page : state를 사용했습니다. 현재 페이지가 언제인지 나타냅니다.
setPage : page state의 set state를 말합니다.
inMaxPageListNumber : 페이지 버튼을 몇 개를 보여줄지 정해주는 숫자입니다.
위의 사진처럼 Page를 그대로 받은 상태이기 때문에 다음버튼을 누르거나
현재 버튼을 눌렀을 때 setPage(i)값이거나 +1 해주면 됩니다.
정말 중요한 점
useEffect를 사용하고 감지는 page의 값 변경을 감지로 두는 게 이 코드에 가장 중요한 부분입니다.
setState를 마치더라 하더라도 리액트는 개발자가 원하는 대로 바로 state변경, 즉 자주 변경하는 걸 싫어하기에
동작하지 않을 수 있습니다. 그럴 때 마다 useEffect를 이용해서 state변경이 감지 될 때 제가 생각해놓은
페이징 알고리즘을 바로바로 실행해서 resultList의 값이 변경 되도록 만들어 줍니다. 알고리즘은 대략 이렇습니다.
예를 들어
총 게시물 320개
원하는 페이지 10개 n
현재 페이지 p
페이지 네이션의 페이지 pp
총 나온 페이지 수
320 / 10 = 32페이지 나옴
그럼 여기서 원하는 페이지는 10 이니
1~10
11~20
21~30
31~32
이렇게 페이징됩니다.
p = 1
기존 페이지에 원하는 범위의 숫자를뺌
ex>
n 이 10때
p = 22
그럼 if p-n 을 두번해야 p < n이됨 그럼 페이지가 2페이지라는걸 알게됨
그럼 2페이지니깐 pp라는걸 알게됨
그래서 공식은 pp(=2)*n ~ n*pp + n을 한번더
1*10 1+n*pp(0) ~ n * (pp+1)
11~20 1+n*pp(1) ~ n * (pp+1)
21~30 1+n*pp(2) ~ n * (pp+1)
31~32 1+pp*n ~ n+=n
이렇게 진행됩니다.
결과는 이렇게 나오며 하나에 10개의 글이 나오지만 너무 큰 바람에 이렇게 담아넣습니다.
코드
ListPageNation.jsximport axios from "axios";import { React, useState, useEffect } from "react";import "bootstrap/dist/js/bootstrap.bundle";export default function ListPageNation({postLength, // 포스트 배열의 길이 (게시판 길이)oneViewNumber, // 한 페이지의 보여줄 Numberpage, // 현재페이지setPage,inMaxPageListNumber, // 예 10이면) 페이지 몇개 이전 1,2,3,4,5,6,7,9,10 다음}) {const [numPages, setNumPages] = useState(Math.ceil(postLength / oneViewNumber));const middleList = Array(numPages).fill().map((v, i) => i + 1);const [start, setStart] = useState(0);const [end, setEnd] = useState(inMaxPageListNumber);const [resultList, setResultList] = useState(middleList.slice(start, end));useEffect(() => {// 얼마나 반복했는지 횟수 알기위함// 즉 현재 페이지, page의 page라 해서 ppvar fpage = page;var fInMaxPageListNumber = inMaxPageListNumber;var start = 0;var end = inMaxPageListNumber;var pp = 0;while (fpage > fInMaxPageListNumber) {fpage = fpage - fInMaxPageListNumber;pp++;if (fpage < fInMaxPageListNumber) {break;}}// 페이지가 도출됨start = inMaxPageListNumber * pp + 1;end = inMaxPageListNumber * (pp + 1);setStart(start);setEnd(end);setResultList(middleList.slice(start - 1, end));}, [page]);return (<ul className="pagination mt-5 justify-content-center"><li className="page-item"><buttonclassName="page-link"aria-label="Next"onClick={() => {setPage(1);}}disabled={page === 1}><span aria-hidden="true">처음span>button>li><li className="page-item">li><buttonclassName="page-link"aria-label="Previous"onClick={() => {setPage(page - 1);}}disabled={page === 1}><span aria-hidden="true">이전span>button>{resultList.map((i) => (<li key={i} className="page-item"><buttonclassName="page-link"key={i}onClick={() => {setPage(i);}}aria-current={page === i ? "page" : null}>{i}button>li>))}<li className="page-item"><buttonclassName="page-link"aria-label="Next"onClick={() => {setPage(page + 1);}}disabled={page === numPages}><span aria-hidden="true">다음span>button>li><li className="page-item"><buttonclassName="page-link"aria-label="Next"onClick={() => {setPage(numPages);}}disabled={page === numPages}><span aria-hidden="true">마지막span>button>li>ul>);}
이건 Post를 slice해주는 파일 입니다.
MainAnnouncement.jsx
import "bootstrap/dist/js/bootstrap.bundle";import axios from "axios";import { React, useState, useEffect } from "react";import { Link } from "react-router-dom";import BoardBlock from "./AnnouncementComponents/BoardBlock";// 내가 직접만든 페이지네이션 양식import ListPageNation from "../../components/PageNation/ListPageNation";export default function MainAnnouncement(props) {const [announcementList, setList] = useState([]); // 빈 배열// 게시판 단순 페이징const [oneViewNumber, setOneViewNumber] = useState(10);const [page, setPage] = useState(1);const offset = (page - 1) * oneViewNumber;const adminIn = localStorage.getItem("adminSuccess", "imadmin");// 여긴 페이징 안의 페이징// 페이징const [pageNationPage, setPageNationPage] = useState(1);const pageNationOffset = (pageNationPage - 1) * oneViewNumber;useEffect(() => {// 자동적으로 리스트 반환const requestList = axios.get("/find-all-announcement-board").then((res) => {setList(res.data.reverse());}).catch((Error) => {console.log(Error);});}, []);return (<div className="container mt-5">{announcementList.length === 0 && <div>공지글이 없습니다div>}{announcementList.length > 0 &&announcementList.slice(offset, offset + oneViewNumber).map((announcement) => {return (<BoardBlockannouncement={announcement}key={announcement.id}>BoardBlock>);})}{announcementList.length > 0 && (<ListPageNationpostLength={announcementList.length}oneViewNumber={oneViewNumber}page={page}setPage={setPage}inMaxPageListNumber={10}/>)}{!!adminIn && (<Link className="btn btn-dark" to="/announcement-write">글쓰기Link>)}div>);}
최대한 다른 분들이 편하게 볼 수 있도록 노력했지만 조금 더 글쓰기 실력을 늘리거나
다듬는 게 필요할 것 같습니다.
더 나은 글로 찾아오겠습니다. 감사합니다.
참고
- https://www.daleseo.com/react-pagination/
-