간단한 페이지네이션 구현하기
Pagination
페이지네이션은 다수의 콘텐츠를 여러 페이지로 나누고, 이전 또는 다음 페이지로 넘어가거나 특정 페이지로 이동할 수 있는 버튼을 제공하는 기술입니다. 페이지네이션은 공통된 주제로 묶은 글의 목록을 보여주는 게시판과 같은 곳에서 많이 사용되고 있습니다. 비슷한 목적의 기술로 무한 스크롤도 있는데, 이에 대한 장단점은 이전 포스트에서 다루었으니 본문에서는 페이지네이션의 장단점을 살펴보겠습니다.
페이지네이션의 장점
- 사용자 의도에 맞게 페이지를 넘길 수 있습니다.
- 특정 항목의 위치를 간편하게 확인할 수 있습니다.
페이지네이션의 단점
- 사용자 입장에서 페이지 이동이라는 추가적인 행동이 발생합니다.
- 한 페이지에 나타나는 정보량이 제한적입니다.
페이지네이션과 무한 스크롤 모두 장단점이 존재하기 때문에, 만들고자 하는 사이트에 맞게 결정하면 됩니다. 저는 한 페이지에 최대 10개의 게시물 목록을 보여주고 싶었기에 페이지네이션을 선택했습니다. 자! 그럼 구현 드루가 볼까요? 😆
예제로 준비한 HTML/CSS 코드
이번 예제는 어디서나 쉽게 볼 수 있는 간단한 게시판 형태로 디자인했습니다.
제가 공유한 코드를 그대로 쓰시면 처음에는 비어 있어 당황하실 수 있는데, 내부 콘텐츠(게시물, 버튼)를 자바스크립트를 통해 동적으로 생성해서 그렇습니다. 그러니 놀라지 마세요!! 😅
HTML
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>Pagination</title>
<!-- Styles -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<article class="article">
<ul class="contents"></ul>
<div class="buttons"></div>
</article>
<!-- Scripts -->
<script type="module" src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"></script>
<script src="app.js"></script>
</body>
</html>
스크립트를 불러오는 부분에서 [ionicons.js]라는 모듈을 추가적으로 불러왔습니다. ionicons는 여러 아이콘을 간단한 태그 형식으로 사용할 수 있는 오픈소스 라이브러리입니다. 이번 예제에서 이전(prev), 다음(next) 페이지 버튼에 아이콘을 넣기 위해 사용했습니다. 사용 방법은 크게 어렵지 않으니, 궁금하신 분들은 공식 문서를 확인해 보세요. 라이브러리를 불러오고, 사용하고자 하는 아이콘 태그를 복붙 하면 끝입니다.
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.article {
max-width: 960px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.content {
display: flex;
padding: 0.3em 0;
list-style: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.content__id,
.content__author,
.content__date {
flex-basis: 15%;
}
.content__title {
flex: 1;
text-align: left;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.buttons {
position: relative;
padding: 1rem 0;
display: inline-flex;
justify-content: center;
}
.button {
padding: 0 1rem;
font-size: 1.2rem;
color: #333;
background: transparent;
border: 0;
outline: 0;
cursor: pointer;
}
.button.active,
.button:hover {
color: #1f975d;
font-weight: 600;
text-decoration: underline;
}
.prev {
position: absolute;
left: -2.5rem;
}
.next {
position: absolute;
right: -2.5rem;
}
ion-icon {
padding-top: 0.05rem;
}
Coding Start
필요한 변수 선언
const contents = document.querySelector(".contents");
const buttons = document.querySelector(".buttons");
const numOfContent = 178;
const showContent = 10;
const showButton = 5;
const maxPage = Math.ceil(numOfContent / maxContent);
let page = 1;
각 변수의 용도는 다음과 같습니다.
- contents: 글 목록을 담기 위한 부모 리스트 요소
- buttons: 페이지 버튼을 담기 위한 부모 리스트 요소
- numOfContent: 전체 글의 개수
- maxContent: 한 페이지에 보여줄 글의 최대 개수
- maxButton: 한 화면에 보여줄 페이지 버튼의 최대 개수
- maxPage: 글을 모두 보여주기 위해 필요한 페이지의 개수
- page: 현재 페이지 (시작 페이지 = 1)
Math.ceil는 인자로 들어온 값보다 크거나 같은 숫자 중 가장 작은 정수를 반환합니다. 보유하고 있는 글의 개수가 178개이기 때문에, 이를 10개씩 보여주기 위해서는 총 18페이지가 필요합니다. 연산의 결과는 17이지만, 해당 메서드에 의해 18을 반환하여 저장합니다.
글 목록 & 버튼 생성 함수 구현
예제에서는 목록과 버튼을 동적으로 생성하기 때문에 함수로 만들어 재사용했습니다.
const makeContent = (id) => {
const content = document.createElement("li");
content.classList.add("content");
content.innerHTML = `
<span class="content__id">${id}</span>
<span class="content__title">게시물 제목</span>
<span class="content__author">작성자</span>
<span class="content__date">2022.01.01</span>
`;
return content;
};
const makeButton = (id) => {
const button = document.createElement("button");
button.classList.add("button");
button.dataset.num = id;
button.innerText = id;
button.addEventListener("click", (e) => {
Array.prototype.forEach.call(buttons.children, (button) => {
if (button.dataset.num) button.classList.remove("active");
});
e.target.classList.add("active");
renderContent(parseInt(e.target.dataset.num));
});
return button;
};
두 함수는 요소를 만들어 반환하는 단순한 기능만을 수행하는데, 버튼의 경우는 눌렀을 때 해당 페이지의 글 목록을 불러와야 하기 때문에 이벤트 리스너도 추가했습니다. 특정 버튼을 클릭하면 그 버튼은 활성화시키고, 다른 버튼은 비활성화 상태가 되어야하므로 반복문을 사용해 클래스(classlist)를 조정했습니다.
나머지 버튼의 클래스명을 제거하는 부분에서 forEach가 아닌 for문을 이용해 처리해도 됩니다.
렌더링 함수 구현
렌더링 함수는 화면에 글 목록과 버튼을 그리는 함수입니다.
const renderContent = (page) => {
// 목록 리스트 초기화
while (contents.hasChildNodes()) {
contents.removeChild(contents.lastChild);
}
// 글의 최대 개수를 넘지 않는 선에서, 화면에 최대 10개의 글 생성
for (let id = (page - 1) * maxContent + 1; id <= page * maxContent && id <= numOfContent; id++) {
contents.appendChild(makeContent(id));
}
};
const renderButton = (page) => {
// 버튼 리스트 초기화
while (buttons.hasChildNodes()) {
buttons.removeChild(buttons.lastChild);
}
// 화면에 최대 5개의 페이지 버튼 생성
for (let id = page; id < page + maxButton && id <= maxPage; id++) {
buttons.appendChild(makeButton(id));
}
// 첫 버튼 활성화(class="active")
buttons.children[0].classList.add("active");
buttons.prepend(prev);
buttons.append(next);
// 이전, 다음 페이지 버튼이 필요한지 체크
if (page - maxButton < 1) buttons.removeChild(prev);
if (page + maxButton > maxPage) buttons.removeChild(next);
};
const render = (page) => {
renderContent(page);
renderButton(page);
};
render(page);
글 목록을 페이지당 최대 10개씩, 그리고 버튼을 한 화면에 최대 5개만 띄울 수 있도록 구현했습니다. 그냥 수식만 보면 이해가 어려울 수 있어, 변수에 대입했던 값들을 토대로 조금 풀어 추가 설명합니다. 😵💫
버튼을 만드는 for문의 수식도 값을 대입하며 확인해 보면 좋습니다.
페이지 이동 함수 구현
마지막으로 이전 페이지와 다음 페이지로 이동할 수 있는 버튼과 기능을 만들어야 합니다.
const goPrevPage = () => {
page -= maxButton;
render(page);
};
const goNextPage = () => {
page += maxButton;
render(page);
};
const prev = document.createElement("button");
prev.classList.add("button", "prev");
prev.innerHTML = '<ion-icon name="chevron-back-outline"></ion-icon>';
prev.addEventListener("click", goPrevPage);
const next = document.createElement("button");
next.classList.add("button", "next");
next.innerHTML = '<ion-icon name="chevron-forward-outline"></ion-icon>';
next.addEventListener("click", goNextPage);
여기서는 크게 어려운 계산이 필요하지 않고, 현재 페이지의 값만 바꿔주면 끝입니다. 현재 페이지가 1일 때 다음 페이지 버튼을 누르면 5(maxButton)가 더해져서 6이 되도록 말이죠. 그런 식으로 처리하고 마지막에 다시 한 번 렌더링 함수를 호출하면 바로 이어지는 최종 결과를 확인할 수 있습니다.
최종 결과
8-90줄 정도의 짧은 코드로 간단한 페이지네이션 구현을 마쳤습니다.
예제에서는 미리 데이터의 개수를 정해두고 화면에 띄우는 식으로 구현했지만, 실제로는 DB에 저장된 데이터를 받아오는 식으로 구현해야 합니다. DB를 사용할 때도 지금과 전체적인 구현 방식이 비슷하기 때문에, 일단 한 번 직접 구현해 보면서 감을 익혀두면 좋을 것 같습니다.
예제에 사용된 코드는 Git 저장소에서 확인 가능합니다 ❤️
References
'🌈 기술스택 > JavaScript' 카테고리의 다른 글
Slick 같은 무한 루프 슬라이드 만들기 (1) | 2022.01.09 |
---|---|
Intersection Observer API로 무한 스크롤 구현하기 (0) | 2022.01.04 |
마우스로 터치 스크롤 구현하기 (1) | 2022.01.04 |
비동기 처리를 위한 문법 (Promise) (0) | 2021.10.08 |
자바스크립트에 동시성을 부여하는 이벤트 루프 (0) | 2021.10.07 |
댓글
이 글 공유하기
다른 글
-
Slick 같은 무한 루프 슬라이드 만들기
Slick 같은 무한 루프 슬라이드 만들기
2022.01.09 -
Intersection Observer API로 무한 스크롤 구현하기
Intersection Observer API로 무한 스크롤 구현하기
2022.01.04 -
마우스로 터치 스크롤 구현하기
마우스로 터치 스크롤 구현하기
2022.01.04 -
비동기 처리를 위한 문법 (Promise)
비동기 처리를 위한 문법 (Promise)
2021.10.08