Pagination(페이지네이션), Paging(페이징), Page(페이지)
Database(데이터베이스)에서 페이징(Paging) 없이 데이터를 가져오면 수십 건부터 수백만 건까지 데이터를 반환하여 문제(DB 부하, 서버 부하, 네트워크 트래픽 부하 등)가 발생합니다. 그래서 데이터를 가져오기 전에 한 번에 가져올 데이터의 크기를 정해서 나누어서 가져오게 해야 합니다.
이렇게 일정한 크기로 데이터를 나누는 것을 페이징(Paging)이라고 하고 일정한 크기를 가진 데이터를 페이지(Page)라고 합니다. 그리고 나누어진 페이지를 이전 페이지나 다음 페이지 또는 특정 페이지로 이동할 수 있게 처리하는 것을 페이지네이션(Pagination)이라고 합니다.
일반적인 게시판에서 페이지네이션(Pagination)의 페이지의 크기는 10을 사용합니다.
레스트 API에서 페이지네이션(Pagination)을 처리하기 위해서 Query String(쿼리 스트링)으로 URL 주소 뒤에 붙여서 사용합니다.
Query String(쿼리 스트링)은 물음표("?") 뒤에 키(key)와 값(value)을 쌍으로 구성하여 웹 서버에 전달되는 데이터들입니다. 앰퍼센드(&)로 여러 개의 키(key)와 값(value)을 추가할 수 있습니다.
?key=value&key=value
REST API Pagination(레스트 API 페이지네이션)
페이지 번호(pageno)
페이지 번호(pageno)로 URI에서 리소스 중 특정 페이지를 가져옵니다. (기본 페이지 크기는 10입니다.)
http://localhost:9000/boards -> 전체 게시판 내용
http://localhost:9000/boards?pageno=1 -> 1번째 페이지 게시판 내용
http://localhost:9000/boards?pageno=5 -> 5번째 페이지 게시판 내용
페이지 번호(pageno)가 1이면 boards(게시판)에서 1번부터 10번까지 게시물을 가져옵니다.
페이지 크기(countperpage)
페이지 크기(countperpage)로 URI에서 가져올 페이지 크기를 설정합니다.
http://localhost:9000/boards?countperpage=20&pageno=1 -> 1번째 페이지 게시판 내용
페이지 크기(countperpage)가 20이면 boards(게시판)에서 1번부터 20번까지 게시물을 가져옵니다.
http://localhost:9000/boards?countperpage=20&pageno=2 -> 2번째 페이지 게시판 내용
페이지 크기(countperpage)가 20이고 페이지 번호(pageno)가 2이면 boards(게시판)에서 21번부터 40번까지 게시물을 가져옵니다.
페이지 번호 대신 Offset과 Limit로 페이징 하여 가져올 수 있습니다.
limit으로 URI에서 가져올 페이지 크기를 설정하고 offset으로 가져올 시작 위치를 설정합니다.
http://localhost:9000/boards?limit=20&offest=10 -> 10번째 부터 20개의 게시판 내용
페이징(Paging)을 하지만 페이지 번호가 아니라 시작 위치이기 때문에 매번 시작 위치를 계산해야 합니다.
페이지 크기(countperpage)와 페이지 번호(pageno)를 이용하여 페이지네이션(Pagination)을 구현하겠습니다.
초기 데이터 추가
현재는 DB를 사용하지 않고 있어서 배열로 데이터를 관리하고 있습니다.
레스트 API에서 페이지네이션(Pagination)을 처리하기 위해서 기존 데이터에 10개 더 추가하겠습니다.
C:\workspaces\nodeserver\testrestapi\boardapi.js 파일을 오픈하여 boardList 배열에 데이터를 추가합니다.
let boardList = [
{no:1, subject:"테스트 제목1", content:"테스트 내용1", writer:"testid1", writedate:"2021-08-09 13:00:00"},
{no:2, subject:"테스트 제목2", content:"테스트 내용2", writer:"testid2", writedate:"2021-08-09 13:10:00"},
{no:3, subject:"테스트 제목3", content:"테스트 내용3", writer:"testid3", writedate:"2021-08-09 13:20:00"},
{no:4, subject:"테스트 제목4", content:"테스트 내용4", writer:"testid1", writedate:"2022-03-26 14:00:00"},
{no:5, subject:"테스트 제목5", content:"테스트 내용5", writer:"testid2", writedate:"2022-03-26 15:10:00"},
{no:6, subject:"테스트 제목6", content:"테스트 내용6", writer:"testid3", writedate:"2022-03-26 16:20:00"},
{no:7, subject:"테스트 제목7", content:"테스트 내용7", writer:"testid1", writedate:"2022-03-26 17:30:00"},
{no:8, subject:"테스트 제목8", content:"테스트 내용8", writer:"testid2", writedate:"2022-03-26 18:40:00"},
{no:9, subject:"테스트 제목9", content:"테스트 내용9", writer:"testid3", writedate:"2022-03-26 19:50:00"},
{no:10, subject:"테스트 제목10", content:"테스트 내용10", writer:"testid1", writedate:"2022-03-26 21:00:00"},
{no:11, subject:"테스트 제목11", content:"테스트 내용11", writer:"testid2", writedate:"2022-03-26 22:10:00"},
{no:12, subject:"테스트 제목12", content:"테스트 내용12", writer:"testid3", writedate:"2022-03-26 23:20:00"},
{no:13, subject:"테스트 제목13", content:"테스트 내용13", writer:"testid1", writedate:"2022-03-27 00:30:00"}
];
추가된 게시물의 제목과 내용, 시간은 의미가 없습니다. 단순히 시간은 1시간 10분 간격으로 추가였습니다.
게시판 REST API에 Pagination(페이지네이션) 추가
라우터(router)에서 Query String(쿼리 스트링)를 가져오기 위해서는 req 객체의 query를 사용하면 됩니다.
function(req, res, next) {
req.query.key -> key의 값(value)를 가져옵니다.
}
1. C:\workspaces\nodeserver\testrestapi\boardapi.js 파일을 오픈하여 게시판 배열 전체를 리턴하는 라우터(router)에 페이지 크기(countperpage)와 페이지 번호(pageno)를 추가합니다.
router.get('/', function(req, res, next) {
console.log("REST API Get Method - Read All");
// 페이지 크기
var countPerPage = req.query.countperpage;
// 페이지 번호
var pageNo = req.query.pageno;
res.json({success:true, data:boardList});
});
2. Query String(쿼리 스트링)으로 페이지 크기(countperpage)와 페이지 번호(pageno)로 전달되지 않으면 undefined으로 설정됩니다. 그래서 페이지 크기(countperpage)와 페이지 번호(pageno)를 기본값으로 설정합니다. 그리고 페이지 크기(countperpage)와 페이지 번호(pageno)가 문자열이기 때문에 정수로 변환합니다.
router.get('/', function(req, res, next) {
console.log("REST API Get Method - Read All");
// 페이지 크기
var countPerPage = req.query.countperpage;
// 페이지 번호
var pageNo = req.query.pageno;
if (countPerPage == undefined || typeof countPerPage == "undefined" || countPerPage == null) {
countPerPage = 10;
} else {
countPerPage = parseInt(countPerPage);
}
if (pageNo == undefined || typeof pageNo == "undefined" || pageNo == null) {
pageNo = 0;
} else {
pageNo = parseInt(pageNo);
}
res.json({success:true, data:boardList});
});
페이지 번호(pageno)가 0이면 전체 게시판 내용을 가져오게 하고 페이지 번호(pageno)가 0 이상면 해당 페이지의 게시물들을 가져오게 처리합니다.
3. 배열로 된 데이터에서 페이지 크기(countperpage)와 페이지 번호(pageno)를 사용하여 가져올 배열의 시작 번호와 끝 번호를 계산하고 배열에서 가져오게 처리합니다.
router.get('/', function(req, res, next) {
console.log("REST API Get Method - Read All");
// 페이지 크기
var countPerPage = req.query.countperpage;
// 페이지 번호
var pageNo = req.query.pageno;
if (countPerPage == undefined || typeof countPerPage == "undefined" || countPerPage == null) {
countPerPage = 10;
} else {
countPerPage = parseInt(countPerPage);
}
if (pageNo == undefined || typeof pageNo == "undefined" || pageNo == null) {
pageNo = 0;
} else {
pageNo = parseInt(pageNo);
}
if (pageNo > 0) {
// 전체 크기
var totalCount = boardList.length;
// 시작 번호
var startItemNo = ((pageNo - 1) * countPerPage);
// 종료 번호
var endItemNo = (pageNo * countPerPage) - 1;
// 종료 번호가 전체 크기보다 크면 전체 크기로 변경
if (endItemNo > (totalCount - 1)) {
endItemNo = totalCount - 1;
}
var boardPageList = [];
if (startItemNo < totalCount) {
for (var index = startItemNo; index <= endItemNo; index++) {
boardPageList.push(boardList[index]);
}
}
res.json({success:true, data:boardPageList});
} else {
res.json({success:true, data:boardList});
}
});
시작 번호가 배열의 크기보다 크면 빈 배열로 리턴하고 종료 번호가 배열의 전체 크기보다 크면 종료 번호를 배열의 전체 크기로 설정하여 리턴합니다.
시작 번호가 배열의 크기보다 크거나 종료 번호가 배열의 전체 크기보다 클 때 에러로 처리하셔도 됩니다.
향후 NodeJS에서 데이터베이스를 연결할 때 쿼리로 처리하는 방법을 소개하도록 하겠습니다.
4. 콘솔을 실행하고 Node.js 레스트 API 서버 프로젝트가 있는C:\workspaces\nodeserver\testrestapi 폴더로 이동합니다. 그리고 npm run 명령어를 실행합니다.
npm run start
5. Postman(포스트맨)를 실행하여 테스트합니다.
Postman(포스트맨)를 이용하여 REST API Pagination 테스트
Get Method - 페이지네이션(Pagination) 테스트
http://localhost:9000/boards?countperpage=10&pageno=1
http://localhost:9000/boards?pageno=1
리턴 값에 success가 true이고 data에는 게시판 배열에서 1번부터 10번까지 내용을 가져옵니다.
{
"success": true,
"data": [
{
"no": 1,
"subject": "테스트 제목1",
"content": "테스트 내용1",
"writer": "testid1",
"writedate": "2021-08-09 13:00:00"
},
{
"no": 2,
"subject": "테스트 제목2",
"content": "테스트 내용2",
"writer": "testid2",
"writedate": "2021-08-09 13:10:00"
},
{
"no": 3,
"subject": "테스트 제목3",
"content": "테스트 내용3",
"writer": "testid3",
"writedate": "2021-08-09 13:20:00"
},
{
"no": 4,
"subject": "테스트 제목4",
"content": "테스트 내용4",
"writer": "testid1",
"writedate": "2022-03-26 14:00:00"
},
{
"no": 5,
"subject": "테스트 제목5",
"content": "테스트 내용5",
"writer": "testid2",
"writedate": "2022-03-26 15:10:00"
},
{
"no": 6,
"subject": "테스트 제목6",
"content": "테스트 내용6",
"writer": "testid3",
"writedate": "2022-03-26 16:20:00"
},
{
"no": 7,
"subject": "테스트 제목7",
"content": "테스트 내용7",
"writer": "testid1",
"writedate": "2022-03-26 17:30:00"
},
{
"no": 8,
"subject": "테스트 제목8",
"content": "테스트 내용8",
"writer": "testid2",
"writedate": "2022-03-26 18:40:00"
},
{
"no": 9,
"subject": "테스트 제목9",
"content": "테스트 내용9",
"writer": "testid3",
"writedate": "2022-03-26 19:50:00"
},
{
"no": 10,
"subject": "테스트 제목10",
"content": "테스트 내용10",
"writer": "testid1",
"writedate": "2022-03-26 21:00:00"
}
]
}
페이지 크기(countperpage)와 페이지 번호(pageno)를 변경하여 테스트해 보시기 바랍니다.
http://localhost:9000/boards?countperpage=5&pageno=3
리턴 값에 success가 true이고 data에는 게시판 배열에서 11번부터 13번까지 내용을 가져옵니다.
{
"success": true,
"data": [
{
"no": 11,
"subject": "테스트 제목11",
"content": "테스트 내용11",
"writer": "testid2",
"writedate": "2022-03-26 22:10:00"
},
{
"no": 12,
"subject": "테스트 제목12",
"content": "테스트 내용12",
"writer": "testid3",
"writedate": "2022-03-26 23:20:00"
},
{
"no": 13,
"subject": "테스트 제목13",
"content": "테스트 내용13",
"writer": "testid1",
"writedate": "2022-03-27 00:30:00"
}
]
}