개발/Javascript

[자바스크립트] 페이지네이션(Pagination) 구현

duknock 2023. 3. 12. 21:32
반응형

하 이건 진짜 하기 싫었는데 정리하긴 해야지...

 

 

 

게시판 같은거 보면 아래에 페이지 버튼이 있다.

 

 

 

네이버 메일의 페이지네이션 버튼

 

얘는 페이지네이션 (Pagination)이라고 하는데

 

 

콘텐츠를 여러 페이지로 나누어 다음 또는 이전 페이지로 이동하거나 특정 페이지로 이동할 수 있는 요소이다.

 

 

오늘은 얘를 만들어볼건데

 

 

이게 보기엔 별거아닌거 같은데 은근히 만들기 귀찮은 녀석이다. 이유는 아래에 보면 나온다.

 

 

이번엔 자바스크립트의 Class 문법을 사용하여 만들어보겠다.

 

 

 

 

 

설계

 

1. 페이지 버튼을 누르면 해당 페이지가 URL 파라미터에 추가되며 페이지를 갱신한다.

   - (2 페이지를 클릭했을 경우 : duknock.tistory.com -> duknock.tistory.com?page=2)

 

 

2. URL에 ?page=3 이런식으로 파라미터가 들어가있으면 버튼을 누르지 않아도 해당 페이지로 이동한다.

    (1번과 같은 얘기)

 

 

3. 한 페이지에 보여줄 데이터의 양은 셀렉트 박스로 조절할 수 있다. 얘를 변경했을 때에도 페이지가 갱신된다.

  - (10개씩보기, 20개씩보기 이런거)

 

 

4. 데이터를 한번에 모두 불러왔을때만 사용할 수 있게 설계했다.

  - (보여줄 데이터의 총량을 알아야 페이지네이션 구현이 가능하다.)

물론 데이터 자체를 다 가져오지 않아도 데이터의 총 갯수만 따로 받을 수 있게 API를 구성하면 페이지네이션을 구현할 수 있다.
그렇게 하려면 API를 요청할때 프론트단에서 page 값과 page에서 보여줄 데이터의 갯수 등의 정보를 파라미터에 담아서 보내면 백엔드단에서 요청에 맞게 데이터를 잘라서 보내주어야 한다.

 

 

5. 페이지네이션 버튼은 한줄에 10개가 존재한다. (<<, >> 제외)

  - (<<, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, >>)

 

 

6. 현재 페이지가 항상 중간에 위치한다. 예를 들어 총 페이지수가 20페이지이고 현재페이지가 12일 경우

  - (<<, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, >>) 이런식

    물론 현재 페이지 앞이나 뒤에 남은 페이지 수가 4장보다 적을 경우엔 그쪽으로 치우치게 된다.

    예시로 총 페이지수가 20페이지고 현재페이지가 18일 경우

  - (<<, 12, 13, 14, 15, 16, 17, 18, 19, 20, >>) 이런식

 

 

 

Pagination.js

 

export default class Pagination {
	constructor(data, itemsPerPage) {
		this.data = data;
		this.itemsPerPage = itemsPerPage;
		this.totalPages = Math.ceil(this.data.length / this.itemsPerPage);
		this.currentPage = 1;
		this.queryString = new URLSearchParams(window.location.search);
		if (this.queryString.has('page')) {
			this.currentPage = parseInt(this.queryString.get('page'));
		}
	}
}

 

일단 constructor 메서드로 클래스에 값을 할당해준다.

 

data

- 보여줄 데이터를 의미한다. 배열로 되어있으며, 게시판을 예시로 들자면 게시판의 글에 해당한다.

 

itemsPerPage

- 한 페이지에서 보여줄 데이터의 양을 의미한다. 아까 위에서 말한 셀렉트 박스의 value를 가져올것이다. (~개씩 보기)

 

totalPages

- 총 페이지 수를 의미한다. 데이터의 수 / 페이지에 보여줄 데이터의 수 로 계산해서 올림한다. 예시로 데이터의 수가 52개이고 한 페이지에 10개씩 보여준다면 Math.ceil(52 / 10) 이 되어 총 6페이지가 나온다.

Math.ceil() 을 하는 이유는 데이터가 1개라도 있으면 페이지가 추가되기 때문

 

currentPage

- 현재 페이지를 의미한다. 디폴트로 1페이지가 설정되어있다.

 

queryString

- URL 파라미터를 가져온다. 만약 URL 파라미터에 page가 있을 경우 currentPage를 해당 page값으로 교체한다.

 

 

createPagination() {
		const paginationContainer = document.createElement('div');
		paginationContainer.classList.add('pagination');
		let startPage = 1;
		if (this.currentPage > 5) {
			startPage = this.currentPage - 5;
		}
		let endPage = startPage + 9;
		if (endPage > this.totalPages) {
			endPage = this.totalPages;
			startPage = this.totalPages - 9;
		}
		if (startPage < 1) {
			startPage = 1;
		}

 

 

그럼 이제 페이지네이션을 생성하는 메서드를 만들어보자! (은근 머리통 아프니 천천히 보자)

 

 

일단 페이지네이션 요소들을 담을 div를 생성하고

 

 

startPage(첫번째 페이지 버튼)를 1로 설정한다.

단, 현재 페이지가 5보다 클 경우 startPage는 현재페이지 -5 로 변경한다.

(현재 페이지가 9일 경우 맨 첫번째버튼은 4가 된다.)

 

 

endPage(마지막 페이지 버튼)는 startPage + 9 다.

(그렇다면 아까 위에 말한것과 합쳐서 현재 페이지가 9일 경우 [4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 이렇게 된다)

단, endPage(startPage +9) 가 총 페이지 수보다 많을 경우! endPage는 총 페이지 수로 변경한다.

그리고 startPage는 endPage -9 로 변경한다.

(현재페이지가 9이고 총 페이지 수가 11일 경우 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 이렇게 된다.)

 

 

그리고 startPage가 1보다 작아지면 startPage는 1로 설정한다.

(현재페이지가 3이고 전체 페이지 수가 5일 경우 [1, 2, 3, 4, 5] 이렇게 된다.)

 

 

여기까지가 가장 어려운 부분이었고 이제부터는 쉬우니 이어서 계속 해보자

 

 

 

		const goFirstButton = document.createElement('button');
		goFirstButton.innerText = '<<';
		goFirstButton.classList.add('page-btn');
		goFirstButton.disabled = this.currentPage === 1;
		goFirstButton.addEventListener('click', () => {
			let url = new URL(window.location.href);
			url.searchParams.set('page', 1);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goFirstButton);

 

맨 처음으로 가는 버튼을 만들어준다. [<<]

 

 

단, 이미 현재 페이지가 1이라면 disabled 처리된다.

 

 

이 버튼을 클릭하면 URL의 page 파라미터 값을 1로 설정하고 페이지를 갱신한다.

 

 

 

for (let i = startPage; i <= endPage; i++) {
			const paginationButton = document.createElement('button');
			paginationButton.innerText = i;
			paginationButton.classList.add('page-btn');
			paginationButton.addEventListener('click', () => {
				let url = new URL(window.location.href);
				let pageValue = i;
				let pageParam = 'page';
				if (!url.searchParams.has(pageParam)) {
					url.searchParams.append(pageParam, pageValue);
				} else {
					url.searchParams.set(pageParam, pageValue);
				}
				window.location.href = url.toString();
			});
			if (i === this.currentPage) {
				paginationButton.disabled = true;
			}
			paginationContainer.appendChild(paginationButton);
		}

 

 

아까 구한 startPage부터 endPage 까지 반복문을 돌린다.

 

 

그 중간에 들어가는 페이지 버튼들을 만드는 코드이다.

 

 

당연히 해당 페이지 버튼들을 누르면 URL의 page 파라미터를 해당 페이지 값으로 바꾸고 페이지 갱신...

만약 URL에 page 라는 파라미터 자체가 없을 경우 page 파라미터를 추가한다.

 

 

그리고 currentPage가 해당 버튼의 값과 일치 할 경우, disabled 처리한다.

 

 

 

const goLastButton = document.createElement('button');
		goLastButton.innerText = '>>';
		goLastButton.classList.add('page-btn');
		goLastButton.disabled = this.currentPage === this.totalPages;
		goLastButton.addEventListener('click', () => {
			let url = new URL(window.location.href);
			url.searchParams.set('page', this.totalPages);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goLastButton);

		return paginationContainer;

 

아까 처음으로 가는 버튼과 똑같다. 얘는 끝으로 가는 버튼이다.

 

 

현재 페이지가 이미 총 페이지 수(즉, 마지막 페이지)와 같을 경우 disabled 처리한다.

 

 

그리고 버튼을 누를 경우 총 페이지 수의 값으로 이동한다. 즉 마지막 페이지로 이동한다는 소리

 

 

여기까지 생성한 요소들이 전부 paginationContainer 에 append 되었으니

 

 

마지막으로 paginationContainer를 return 하면 끝!

 

 

 

한번에 보여주자면


export default class Pagination {
	constructor(data, itemsPerPage) {
		this.data = data;
		this.itemsPerPage = itemsPerPage;
		this.totalPages = Math.ceil(this.data.length / this.itemsPerPage);
		this.currentPage = 1;
		this.queryString = new URLSearchParams(window.location.search);
		if (this.queryString.has('page')) {
			this.currentPage = parseInt(this.queryString.get('page'));
		}
	}

	createPagination() {
		const paginationContainer = document.createElement('div');
		paginationContainer.classList.add('pagination');
		let startPage = 1;
		if (this.currentPage > 5) {
			startPage = this.currentPage - 5;
		}
		let endPage = startPage + 9;
		if (endPage > this.totalPages) {
			endPage = this.totalPages;
			startPage = this.totalPages - 9;
		}
		if (startPage < 1) {
			startPage = 1;
		}

		const goFirstButton = document.createElement('button');
		goFirstButton.innerText = '<<';
		goFirstButton.classList.add('page-btn');
		goFirstButton.disabled = this.currentPage === 1;
		goFirstButton.addEventListener('click', () => {
			let url = new URL(window.location.href);
			url.searchParams.set('page', 1);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goFirstButton);

		for (let i = startPage; i <= endPage; i++) {
			const paginationButton = document.createElement('button');
			paginationButton.innerText = i;
			paginationButton.classList.add('page-btn');
			paginationButton.addEventListener('click', () => {
				let url = new URL(window.location.href);
				let pageValue = i;
				let pageParam = 'page';
				if (!url.searchParams.has(pageParam)) {
					url.searchParams.append(pageParam, pageValue);
				} else {
					url.searchParams.set(pageParam, pageValue);
				}
				window.location.href = url.toString();
			});
			if (i === this.currentPage) {
				paginationButton.disabled = true;
			}
			paginationContainer.appendChild(paginationButton);
		}

		const goLastButton = document.createElement('button');
		goLastButton.innerText = '>>';
		goLastButton.classList.add('page-btn');
		goLastButton.disabled = this.currentPage === this.totalPages;
		goLastButton.addEventListener('click', () => {
			let url = new URL(window.location.href);
			url.searchParams.set('page', this.totalPages);
			window.location.href = url.toString();
		});
		paginationContainer.appendChild(goLastButton);

		return paginationContainer;
	}
}

 

이렇게 된다~👍

 

그럼 얘를 어떻게 쓰냐!

 

사용법은 아주 간단하다.

import Pagination from '/pagination.js';

let dataArray = [a,b,c,d,e,f,g,h....]; //대충 데이터 몇백개
let itemsPerPage = 10;
let paging = new Pagination(dataArray, itemsPerPage);
let pagination = paging.createPagination();
document.querySelector('.box').append(pagination);

 

import 로 아까 만들어둔 페이지네이션 모듈을 가져와서

 

 

new (생성자 메서드)로 Pagination을 생성해주고

.createPagination 메서드로 페이지네이션 요소를 생성해주면 끝!

 

이렇게 잘 만들어졌습니다~
5 페이지를 클릭했을 때 URL에도 page=5 로 잘 표시된다

 

 


마무리

 

페이지네이션은 사용할 곳이 많으니 한번 제대로 만들어두고 계속 재활용하자~

 

아, 페이지네이션 버튼 만드는 함수는 알려줬으니 해당 페이지에 데이터 보여주는건 알아서 하시길...😀

반응형