Lsiron
pagination 구현하고 분석해보기. 본문
페이지네이션을 구현해보자.
현재 '이전' 버튼과 'n페이지 이동' 버튼 그리고 '다음' 버튼을 구현해보고자 한다.
JS template engine은 ejs 를 사용하였고, URL parameter 방법을 이용하여 get 요청을 하였다.
서버 측의 app.js 와 클라이언트 측의 list.ejs 가 있다.
먼저 완성본부터 확인해보자.
<app.js>
app.get('/list/:page', async (req, res) => {
let page = parseInt(req.params.page) || 1;
const postsPerPage = 5;
const totalPosts = await db.collection('post').countDocuments();
const totalPages = Math.ceil(totalPosts / postsPerPage);
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
let result = await db.collection('post').find()
.skip((page - 1) * postsPerPage)
.limit(postsPerPage)
.toArray();
res.render('list.ejs', {
posts: result,
currentPage: page,
totalPages: totalPages,
hasPrev: page > 1,
hasNext: page < totalPages
});
});
<list.ejs>
<div class="pagination">
<% if (hasPrev) { %>
<a href="/list/<%= currentPage - 1 %>">이전</a>
<% } else { %>
<span>이전</span>
<% } %>
<% for (let i = 1; i <= totalPages; i++) { %>
<% if (i === currentPage) { %>
<span><%= i %></span>
<% } else { %>
<a href="/list/<%= i %>"><%= i %></a>
<% } %>
<% } %>
<% if (hasNext) { %>
<a href="/list/<%= currentPage + 1 %>">다음</a>
<% } else { %>
<span>다음</span>
<% } %>
</div>
먼저 app.js 부터 분석을 해보자.
<app.js>
app.get('/list/:page', async (req, res) => {
let page = parseInt(req.params.page) || 1;
const postsPerPage = 5;
const totalPosts = await db.collection('post').countDocuments();
const totalPages = Math.ceil(totalPosts / postsPerPage);
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
let result = await db.collection('post').find()
.skip((page - 1) * postsPerPage)
.limit(postsPerPage)
.toArray();
res.render('list.ejs', {
posts: result,
currentPage: page,
totalPages: totalPages,
hasPrev: page > 1,
hasNext: page < totalPages
});
});
1.라우트 설정
app.get('/list/:page', async (req, res) => {
GET 요청을 처리하며, URL 경로에서 페이지 번호를 매개변수로 받는다.
예를 들어 /list/1은 첫 번째 페이지를, /list/2는 두 번째 페이지를 의미한다. ( 저 뒤에 page 는 굳이 page가 아니여도 이름을 맘대로 지어도 된다.)
2. 페이지 번호 파싱
let page = parseInt(req.params.page) || 1;
req.params 로 하면, 유저가 URL 파라미터 자리에 입력한 글자가 담겨있다.
( 그러면 req.params.page 는 유저가 page에 무슨 숫자를 입력했는지를 나타낸다는 뜻)
req.params.page를 정수로 파싱하여 page 변수에 저장한다.
만약 req.params.page가 유효하지 않으면 기본값으로 1을 사용한다.
( 유저가 숫자 말고 쓸데없는 값을 집어넣으면 무조건 1로 봄 )
3. 페이지당 게시물 수 및 총 게시물 수 계산
const postsPerPage = 5;
const totalPosts = await db.collection('post').countDocuments();
한 페이지에 표시할 게시물 수를 postsPerPage로 설정한다.
데이터베이스의 post 컬렉션에서 전체 게시물 수를 totalPosts에 저장한다.
4. 총 페이지 수 계산
const totalPages = Math.ceil(totalPosts / postsPerPage);
총 페이지 수를 계산한다. 총 게시물 수를 페이지당 게시물 수로 나누고 올림하여 계산한다. ( 그냥 공식임 )
5. 페이지 번호 유효성 검사 및 조정
if (page < 1) page = 1;
if (page > totalPages) page = totalPages;
페이지 번호가 1보다 작으면 1로, 총 페이지 수보다 크면 총 페이지 수로 조정한다.
6. 게시물 조회
let result = await db.collection('post').find()
.skip((page - 1) * postsPerPage)
.limit(postsPerPage)
.toArray();
database의 'post' 컬렉션에서 필요한 페이지의 게시물을 find() 메서드를 사용하여 조회한다.
( cf. find({}) 로 사용하면 {} 내부의 조건에 맞는 것만 가져온다는 뜻.
findOne() 은 mongodb 데이터 하나만 가져온다는 뜻. 보통 데이터를 가져올때 아래와 같이 많이 사용한다.)
app.get('/list', async (req, res) => { // 서버를 요청하는 코드
let result = await db.collection('post').find().toArray() // 외우기, mongoDB 모든 데이터를 가져오는 방법
res.render('list.ejs',{posts : result}) // 유저에게 ejs 파일 보내는 법(render), 응답은 1번밖에 안됨
})
skip() 메서드를 사용하여 이전 페이지의 게시물 수만큼 건너뛰고,
( ex: skip(10) 뜻은 데이터를 맨 위에서 10개를 skip한다는 뜻)
limit() 메서드를 사용하여 현재 페이지의 게시물만 가져온다.
( ex: limit(10) 뜻은 데이터를 맨 위에서 10개만 가져온다는 뜻)
그 결과를 toArray()를 통해 배열로 변환한다.
7. 뷰 렌더링
res.render('list.ejs', {
posts: result,
currentPage: page,
totalPages: totalPages,
hasPrev: page > 1,
hasNext: page < totalPages
});
list.ejs 템플릿을 렌더링하며, 필요한 데이터를 전달한다.
- posts: 현재 페이지의 게시물 목록
- currentPage: 현재 페이지 번호
- totalPages: 총 페이지 수
- hasPrev: 이전 페이지가 있는지 여부 (currentPage가 1보다 큰 경우 true)
- hasNext: 다음 페이지가 있는지 여부 (currentPage가 총 페이지 수보다 작은 경우 true)
즉, 변수명과 그 값들을 담아서 list.ejs에 넘겨주는 것임.
다음은 list.ejs를 분석 해 보자.
<list.ejs>
<div class="pagination">
<% if (hasPrev) { %>
<a href="/list/<%= currentPage - 1 %>">이전</a>
<% } else { %>
<span>이전</span>
<% } %>
<% for (let i = 1; i <= totalPages; i++) { %>
<% if (i === currentPage) { %>
<span><%= i %></span>
<% } else { %>
<a href="/list/<%= i %>"><%= i %></a>
<% } %>
<% } %>
<% if (hasNext) { %>
<a href="/list/<%= currentPage + 1 %>">다음</a>
<% } else { %>
<span>다음</span>
<% } %>
</div>
!참고!
<% %> 와 같은 이상한게 들어 가 있는데 이게 무엇인지 부터 알아보자.
ejs에서 html 안에 자바스크립트 문법을 사용할 땐 이 <% %> 로 자바스크립트로 표현한 부분만을 감싸주어야 한다.
주의할 점: 정말 자바스크립트를 사용한 부분만 감싸줘야한다.
<% for (let i = 1; i <= totalPages; i++) {
if (i === currentPage) {
<span> i </span>
} else {
<a href="/list/i ">= i </a>
}
} %>
=> 위와 같이 표현하면 안 된다. 엉망진창으로 출력이 될 것이다.
<% for (let i = 1; i <= totalPages; i++) { %>
<% if (i === currentPage) { %>
<span><%= i %></span>
<% } else { %>
<a href="/list/<%= i %>"><%= i %></a>
<% } %>
<% } %>
=> 위와 같이 딱 자바스크립트를 쓰는 부분만 감싸 줘야한다.
종류는 아래와 같이 총 세가지가 있다.
<% 감싸줘 %>, <%- 감싸줘 %>, <%= 감싸줘 %>
먼저 <% 감싸줘 %> 는 for 반복문이라던지 if 조건문이라던지 생 자바스크립트 문법 쓸 때 사용한다.
다음으로 <%- 감싸줘 %> 는 include와 같이 특수한 문법을 쓸 때 사용한다.
<%- include('nav.ejs') %> <!-- nav.ejs html을 불러와서 넣어준다. -->
마지막으로 <%= 감싸줘 %> 는 <%- 감싸줘 %> 와 기능이 거의 유사하다.
허나 %- 는 안에 있는 데이터가 html 형식일 경우 실제 html처럼 인식하고, 반대로 %= 는 문자로 인식한다. 즉,
<%- <a>안녕</a> %> 은 페이지에 안녕 으로 나오고 <%= <a>안녕</a> %> 은 페이지에 <a>안녕</a> 이대로 나온다.
즉, 위 코드 예시에 있는 include 문법을 쓰면 nav.ejs에서 페이지로 표현되는 것이 그대로 페이지에 나온다.
1. 페이지네이션 컨테이너
<div class="pagination">
모든 페이지네이션 요소를 포함하는 div 요소이다. CSS 클래스를 사용하여 스타일을 지정할 수 있다.
2. 이전 페이지 링크
<% if (hasPrev) { %>
<a href="/list/<%= currentPage - 1 %>">이전</a>
<% } else { %>
<span>이전</span>
<% } %>
hasPrev가 true일 때 (currentPage가 1보다 큰 경우): 이전 페이지로 이동하는 링크를 렌더링한다.
<a href="/list/<%= currentPage - 1 %>">이전</a>
currentPage - 1은 이전 페이지 번호이다.
hasPrev가 false일 때: 이전이라는 텍스트만 렌더링하여 현재 페이지가 첫 페이지임을 표시한다.
<span>이전</span>
3. 페이지 번호 링크
<% for (let i = 1; i <= totalPages; i++) { %>
<% if (i === currentPage) { %>
<span><%= i %></span>
<% } else { %>
<a href="/list/<%= i %>"><%= i %></a>
<% } %>
<% } %>
1부터 totalPages까지 반복하면서 각 페이지 번호를 렌더링한다.
현재 페이지 번호인 경우: 링크 대신 현재 페이지 번호를 텍스트로 표시한다.
<span><%= i %></span>
현재 페이지 번호가 아닌 경우: 해당 페이지로 이동하는 링크를 렌더링 한다.
<a href="/list/<%= i %>"><%= i %></a>
4. 다음 페이지 링크
<% if (hasNext) { %>
<a href="/list/<%= currentPage + 1 %>">다음</a>
<% } else { %>
<span>다음</span>
<% } %>
hasNext가 true일 때 (currentPage가 totalPages보다 작은 경우): 다음 페이지로 이동하는 링크를 렌더링한다.
<a href="/list/<%= currentPage + 1 %>">다음</a>
currentPage + 1은 다음 페이지 번호이다.
hasNext가 false일 때: 다음이라는 텍스트만 렌더링하여 현재 페이지가 마지막 페이지임을 표시한다.
<span>다음</span>
위 사진 처럼 구현이 되었다.
1페이지 일 경우, 이전 버튼이 활성화가 되지 않으며, 마지막 페이지 일 경우 다음 버튼이 활성화 되지 않는다.
또한 각 페이지를 옮겨 다닐 수 있으며, URL상엔 페이지 번호가 입력된다.
'백엔드 > Node.js' 카테고리의 다른 글
환경변수? (0) | 2024.06.13 |
---|---|
Node.js에서 Express를 사용하여 데이터를 서버로 데이터 전송하는 방법 (0) | 2024.06.11 |
미들웨어??? (0) | 2024.06.10 |
라우트? 라우터? 라우팅? 라우터 파일에 데이터 넣기. (2) | 2024.06.06 |
Restful API? (0) | 2024.06.05 |