본문 바로가기
Project/향수프로젝트

[React] 페이지네이션(Pagenation) 구현하기

by junvely 2022. 12. 3.

🙌구현 목표 : 페이지네이션 구현하기

다음과 같은 페이지 네이션을 구현하여야 한다.

즉, 1~10까지 페이지 가 보여지고

페이지 단위는 10씩 넘겨진다.

◀ 클릭 시 이전 페이지 => 현재 페이지가 11~20일 경우 => 1페이지로 이동

▶ 클릭 시 다음 페이지로 넘어간다. => 현재 페이지가 11~20일 경우 => 21페이지로 이동

First는 첫 페이지로

Last는 마지막 페이지로 이동한다.

 

 

1. Pagenation 컴포넌트에 maxPage(최대 페이지), currentPage(현재 페이지), setCurrentPage, visiblePageCount(페이지 단위)를 넘겨준다.

export const HomePage = () => {
  const [pagenation, setPagenation] = useState(1);
  
  return (
        // Pagenation
          <Pagenation
            maxPage={49}
            currentPage={pagenation}
            setCurrentPage={setPagenation}
            visiblePageCount={10}
          />
  );
};​
 

 

2. 넘겨받은 인자를 통해 첫 페이지 목록부터 생성한다.

export const Pagenation: React.FC<IPagenationProps> = ({
  maxPage,
  currentPage,
  setCurrentPage,
  visiblePageCount,
}) => {
  const currentPageBase = Math.floor(currentPage / visiblePageCount) * 10;

  const pageList = useMemo(() => {
   //첫 페이지
    if (1 === currentPage || 1 > currentPage) {
      return Array.from({ length: visiblePageCount }, (_, index) => index + 1);
    }
  }, [currentPage]);

 

여기까지를 풀이하자면,

  • Array.from({객체}) : 어떤 객체를 얕은 복사하여 새로운 배열로 생성한다. =>  visiblePageCount(페이지 단위)만큼 배열을 생성한다. =>Array.map()과 같은 기능을 한다.

 

  • { length : visiblePageCount } => visiblePageCount 만큼 유사 배열 객체를 생성한다. 

=> 예를들어 { length : x } 만 정해줄 경우 이렇게 생성된다.

{ 
	0 : undefined,
    1 : undefined,
    2 : undefined,
    3 : undefined,
    4 : undefined,
}

 

  • Array.from( { length : visiblePageCount })  

=> 유사 배열 객체를 얕게 복사하여 새로운 배열을 생성한다. 결과는 다음과 같다.

[
	0 : undefined,
    1 : undefined,
    2 : undefined,
    3 : undefined,
    4 : undefined,
]

 

즉 이에 대입해 보면

const pageList = useMemo(() => {
   //첫 페이지
    if (1 === currentPage || 1 > currentPage) {
      return Array.from({ length: visiblePageCount }, (_, index) => index + 1);
    }
  }, [currentPage]);
  • Array.from({ length: visiblePageCount }, (_, index) => index + 1);

=> length가 visiblePageCount(페이지 단위:10)만큼인 첫 페이지 배열이 생성된다. (1~10까지)

(_, index) => 여기서 매개변수인 value와 index를 설정 가능한데, 배열은 0부터 시작하기 때문에 페이지 번호가 0,1,2,3 ... 이 아니라 1,2,3... 으로 설정되도록 값을 index + 1 로 설정한다.

이를 리턴하는 pageList를 맵핑하여 새로운 배열로 생성해 보면, 첫 페이지의 배열이 생성된 것을 확인할 수 있다.

<ul className={styles.flex}>
        {pageList.map((item) => {
          return (
            <li
              key={item}
              onClick={() => setCurrentPage(item)}
              className={currentPage === item ? styles.accord : styles.disaccord}>
              {item}
            </li>
          );
        })}
      </ul>

첫 페이지 생성 완료

 

 

 

3. 첫 페이지를 제외한 그 외 페이지 목록을 설정한다.

  const currentPageBase = Math.floor(currentPage / visiblePageCount) * 10;

  const pageList = useMemo(() => {
   //첫 페이지
    if (1 === currentPage || 1 > currentPage) {
      return Array.from({ length: visiblePageCount }, (_, index) => index + 1);
    }
    //그 외 페이지
   	return Array.from({ length: visiblePageCount }, (_, index) => currentPageBase + index + 1);

  }, [currentPage]);

그 외의 페이지 목록들은 기본적으로 

11 ~ 20

21 ~ 30

31 ~ 40  ... 과 같이 10단위들로 생성되어야 한다.

 

  • 먼저 페이지 목록 배열을 생성하기 위해 currentPageBase를 설정하였다.
const currentPageBase = Math.floor(currentPage / visiblePageCount) * 10;

만약 currentPage(현재 페이지)가 21이라면,

21을 visiblePageCount(페이지 단위)인 10으로 나누고 나머지 부분은 Math.floor로 내림 처리한다.

그럼 2가 반환되는데, 이 2에 10을 곱하여 currentPageBase 20을 만든다.

=> (21/10) = 2.1 => 내림처리 => 2 => 2* 10(페이지 단위) => 20

 

  • 생성한 currentPageBase에 나머지 index+1 들을 더하여 21, 22, 23 ... 등과 같은 페이지 배열을 생성한다.
return Array.from({ length: visiblePageCount }, (_, index) => currentPageBase + index + 1);

 

다음과 같이 페이지가 넘어가면 10단위씩 생성되는 것을 확인할 수 있다.

 

❗그런데 여기서 문제가 생겼다. 현재 페이지가 10페이지인데 11~20페이지 목록이 반환된다..

현재 페이지가 10페이지인데 11~20페이지 목록이 반환된다..

1~10까지 페이지 목록에서

1~9까지의 내림처리는 1/10 => 0.1 => 0으로 반환되지만

10일 경우 10/10 => 1 => 1으로 반환되어 버린다...

결과적으로 currentPage가 10,20,30 ... 이렇게 떨어질 경우 베이스가 1~9까지와 달리 한단계 높게 측정되어, 

현재 페이지가 10페이지일 경우 1~10목록이 보여져야 하는데, 11~10를 반환하는 문제가 발생하였다.

 

 

✅ 위 문제를 다음과 같이 해결해 보았다.

  • beforePageBase를 만들어 10,20,30과 같이 그전 값과 한단계 높게 측정된 값을 다시 -1하여 내려준 후 다른 1~9값들과 같은 0으로 반환하게 하여
const beforePageBase = Math.floor(currentPage / visiblePageCount - 1) * 10;

만약 현재 페이지가 10페이지여도 동일하게 1~10페이지가 보여지게 만들었다.

  const pageList = useMemo(() => {
    if (1 === currentPage || 1 > currentPage) {
      return Array.from({ length: visiblePageCount }, (_, index) => index + 1);
    }
    // 10, 20, 30 등 나머지가 남지않는 수일 경우 값을 -1하여 다른 페이지목록과 같이 보여지도록 함
    if (0 === currentPage % visiblePageCount) {
      return Array.from({ length: visiblePageCount }, (_, index) => beforePageBase + index + 1);
    }

 

✨현재 페이지가 10페이지, 20페이지, 30페이지 등 일 때에도 다른 페이지목록들과 같은 페이지목록을 생성한다.

20페이지일 때도 다음 목록을 반환하지 않는다.

 

 

 

4. First , Last 버튼 기능 넣기

  • 이제 First 버튼을 클릭하면 맨 처음 페이지(1페이지)로 이동하고, Last 버튼(49페이지)을 클릭하면 맨 마지막 페이지로 이동하는 기능을 넣어보려고 한다.

currentPage로 상태값을 변경해 주면, 너무나 쉽게 구현이 가능하다.

//First
<button className={styles.button} onClick={() => setCurrentPage(1)}>
        First
</button>

//Last
<button className={styles.button} onClick={() => setCurrentPage(maxPage)}>
        Last
</button>

 

 

5. ◀ , ▶ 버튼 기능 넣기

  • ▶ 버튼 : 이전 페이지 목록으로 이동 => 현재 21~30페이지 일 경우, 버튼 클릭 시 11페이지로 이동

=> 위에서 이미 만들어 놓음 NextPageBase를 사용하면 어렵지않게 구현 가능하다.

=> 이 때 현재 페이지가 1페이지 보다 작아질 경우 페이지가 음수로 무한히 생성 되는 것을 방지하기 위해 조건을 걸어준다.

<button
        className={styles.button}
        onClick={() => {
          setCurrentPage((prev) => {
            if (1 < prev) { //현재 페이지가 1페이지 보다 작아질 경우 페이지가 음수로 무한히 생성 되는 것을 방지
              return beforePageBase + 1;
            }
            return prev; //반드시 예외처리
          });
        }}>
        ◀
</button>

 

  • ◀ 버튼 : 이전 페이지 목록으로 이동 => 현재 21~30페이지 일 경우, 버튼 클릭 시 11페이지로 이동

=> 위에서 이미 만들어 놓음 BeforePageBase를 사용하면 어렵지않게 구현 가능하다.

=> 이 때 현재 페이지가 maxPage 보다 커질 경우 페이지가 무한히 생성 되는 것을 방지하기 위해 조건을 걸어준다.

=> 설정한 조건 외 예외처리를 반드시 해야 한다. (❗jsx에서 if문을 사용할 수 있는지에 대해 다시 한번 확인이 필요하다.)

<button
        className={styles.button}
        onClick={() =>
          setCurrentPage((prev) => {
            if (maxPage > prev) {
              return nextPageBase + 1;
            }
            return prev;
          })
        }>
        ▶
</button>

 

6. 마지막 페이지 설정

 

 

이와 같이 조금 어려웠지만... 다행히 정상적으로 목표했던 모든 기능이 작동하는 페이지네이션을 구현할 수 있어 뿌듯했다.😆