Vue.js 3 & NodeJS/NodeJS

Node.js 레스트 API 페이지네이션(구현 정보) - Node.js REST API Pagination Implementation information

carrotweb 2022. 4. 3. 18:20
728x90
반응형

레스트 API 페이징(Paging) 처리 후 Vue나 JSP, ASP, PHP, Javascript에서 게시판의 페이지네이션(Pagination)을 구현하기 위해서는 전체 크기, 전체 페이지 크기, 페이지 사이즈 등 추가 정보들이 필요합니다.

그럼 페이지네이션(Pagination) 구현에 필요한 정보들을 계산하여 라우터(router)에서 데이터와 함께 리턴하도록 처리하겠습니다.

페이지네이션(Pagination) 정보 객체 추가

1. C:\workspaces\nodeserver\testrestapi\boardapi.js 파일을 오픈하여 게시판 배열 전체를 리턴하는 라우터(router)에 추가합니다.

전체 페이지 크기(마지막 페이지 번호)

전체 페이지 크기는 전체 크기를 페이지 크기로 나누고 나머지가 있으면 1을 더합니다.

전체 페이지 크기는 마지막 페이지 번호이기 때문에 변수명을 마지막 페이지 번호로 하겠습니다.

// 마지막 페이지 번호(전체 페이지 크기)
var lastPageNo = Math.floor(totalCount / countPerPage) + (totalCount % countPerPage== 0 ? 0 : 1);

 

페이지 사이즈

페이지 사이즈는 노출되는 페이지 번호 개 수입니다. 페이지 사이즈가 3이면 페이지 번호가 3개만 노출되고 페이지 사이즈가 5이면 페이지 번호가 5개만 노출됩니다. 페이지 사이즈는 기본적으로 10입니다.

 

페이지 사이즈를 페이지 크기(countperpage)처럼 설정할 수 있게 처리합니다.

// 페이지 사이즈
var pageSize = req.query.pagesize;

if (pageSize == undefined || typeof pageSize == "undefined" || pageSize == null) {
	pageSize = 10;
} else {
	pageSize = parseInt(pageSize);
}

 

시작 페이지 번호

시작 페이지 번호는 페이지 사이즈에서 페이지 번호를 나누고 나머지가 없으면 1을 뺍니다.

// 시작 페이지 번호
var startPageNo = 1;
// 페이지 사이즈로 페이지 번호를 나눈 몫만큼 페이지 시작 번호 변경
var start = Math.floor(pageNo / pageSize);
if (start >= 1) {
	// 그렇지만 나머지가 없으면 현재 페이지 번호가 마지막 페이지 번호와 같아 감소
	if (pageNo % pageSize == 0){
		start--;
	}
	startPageNo = (start * pageSize) + 1;
}

예를 들어 페이지 번호가 10이면 start가 1이 됩니다. 그러면 시작 페이지 번호가 11이 됩니다. 그래서 페이지 사이즈의 배수(즉 나머지가 없으면)이면 start에서 1을 뺍니다. 그러면 시작 페이지 번호가 1이 됩니다. 동일하게 페이지 번호가 20이면 start가 2가 되고 나머지가 없기 때문에 1을 빼고 계산하면 시작 페이지 번호는 11이 됩니다.

다시 페이지 번호가 10보다 큰 15이면 start가 1이 됩니다. 그리고 나머지가 5 임으로 페이지 start에서 1을 빼지 않습니다. 그래서 시작 페이지 번호가 11이 됩니다.

종료 페이지 번호

종료 페이지 번호는 시작 페이지 번호에서 1을 빼고 페이지 사이즈를 더하면 됩니다.

// 종료 페이지 번호
var endPageNo = (startPageNo - 1) + pageSize;
// 그렇지만 종료 페이지 번호가 마지막 페이지 번호보다 크면 마지막 페이지 번호로 변경
if (endPageNo > lastPageNo) {
	endPageNo = lastPageNo;
}

 

페이지네이션 정보

페이지네이션 정보 객체를 생성합니다.

// 페이지네이션 정보
var paginationInfo = {};
paginationInfo.totalCount = totalCount;
paginationInfo.countPerPage = countPerPage;
paginationInfo.pageSize = pageSize;
paginationInfo.startPageNo = startPageNo;
paginationInfo.endPageNo = endPageNo;
paginationInfo.lastPageNo = lastPageNo;
paginationInfo.pageNo = 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;
	// 페이지 사이즈
	var pageSize = req.query.pagesize;
	
	if (countPerPage == undefined || typeof countPerPage == "undefined" || countPerPage == null) {
		countPerPage = 10;
	} else {
		countPerPage = parseInt(countPerPage);
	}
	if (pageSize == undefined || typeof pageSize == "undefined" || pageSize == null) {
		pageSize = 10;
	} else {
		pageSize = parseInt(pageSize);
	}
	if (pageNo == undefined || typeof pageNo == "undefined" || pageNo == null) {
		pageNo = 0;
	} else {
		pageNo = parseInt(pageNo);
	}
	
	if (pageNo > 0) {
		// 전체 크기
		var totalCount = boardList.length;
		// 마지막 페이지 번호(전체 페이지 크기)
		var lastPageNo = Math.floor(totalCount / countPerPage) + (totalCount % countPerPage== 0 ? 0 : 1);
		// 시작 페이지 번호
		var startPageNo = 1;
		// 페이지 사이즈로 페이지 번호를 나눈 몫만큼 페이지 시작 번호 변경
		var start = Math.floor(pageNo / pageSize);
		if (start >= 1) {
			// 그렇지만 나머지가 없으면 현재 페이지 번호가 마지막 페이지 번호와 같아 감소
			if (pageNo % pageSize == 0){
				start--;
			}
			startPageNo = (start * pageSize) + 1;
		}
		// 종료 페이지 번호
		var endPageNo = (startPageNo - 1) + pageSize;
		// 그렇지만 종료 페이지 번호가 마지막 페이지 번호보다 크면 마지막 페이지 번호로 변경
		if (endPageNo > lastPageNo) {
			endPageNo = lastPageNo;
		}
		// 시작 번호
		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]);
			}
		}
		// 페이지네이션 정보
		var paginationInfo = {};
		paginationInfo.totalCount = totalCount;
		paginationInfo.countPerPage = countPerPage;
		paginationInfo.pageSize = pageSize;
		paginationInfo.startPageNo = startPageNo;
		paginationInfo.endPageNo = endPageNo;
		paginationInfo.lastPageNo = lastPageNo;
		paginationInfo.pageNo = pageNo;
		res.json({success:true, data:boardPageList, pagination:paginationInfo});
	} else {
		res.json({success:true, data:boardList});
	}
});

 

2. 콘솔을 실행하고 Node.js 레스트 API 서버 프로젝트가 있는C:\workspaces\nodeserver\testrestapi 폴더로 이동합니다. 그리고 npm run 명령어를 실행합니다.

npm run start

 

3. Postman(포스트맨)를 실행하여 테스트합니다.

 

반응형

 

Postman(포스트맨)를 이용하여 REST API Pagination 테스트

Get Method - 페이지네이션(Pagination) 테스트

http://localhost:9000/boards?countperpage=2&pageno=2

{
    "success": true,
    "data": [
        {
            "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"
        }
    ],
    "pagination": {
        "totalCount": 13,
        "countPerPage": 2,
        "pageSize": 10,
        "startPageNo": 1,
        "endPageNo": 7,
        "lastPageNo": 7,
        "pageNo": 2
    }
}

 

Vue나 JSP, ASP, PHP, Javascript에서 리턴된 페이지네이션 정보 객체를 사용하면 페이지네이션을 쉽게 구현할 수 있습니다.

추가로 이전 페이지 사이즈 번호는 시작 페이지 번호에서 1을 빼면 되고 다음 페이지 사이즈 번호는 종료 페이지 번호에 1을 더하면 됩니다. 그렇지만 시작 페이지가 1이면 이전 페이지 사이즈 번호는 0이 되어 클릭 시 전체 게시물이 리턴됩니다. 그리고 다음 페이지 사이즈 번호가 마지막 페이지 번호보다 크면 빈 게시물이 리턴됩니다.

그래서 이전 페이지 사이즈 번호가 0이면 이전 페이지 사이즈 번호의 클릭을 비활성화로 해야 합니다. 그리고 다음 페이지 사이즈 번호가 마지막 페이지 번호보다 크면 다음 페이지 사이즈 번호의 클릭을 비활성화해야 합니다.

 

추가로 처리한 전체 소스입니다.

router.get('/', function(req, res, next) {
	console.log("REST API Get Method - Read All");
	// 페이지 크기
	var countPerPage = req.query.countperpage;
	// 페이지 번호
	var pageNo = req.query.pageno;
	// 페이지 사이즈
	var pageSize = req.query.pagesize;
	
	if (countPerPage == undefined || typeof countPerPage == "undefined" || countPerPage == null) {
		countPerPage = 10;
	} else {
		countPerPage = parseInt(countPerPage);
	}
	if (pageSize == undefined || typeof pageSize == "undefined" || pageSize == null) {
		pageSize = 10;
	} else {
		pageSize = parseInt(pageSize);
	}
	if (pageNo == undefined || typeof pageNo == "undefined" || pageNo == null) {
		pageNo = 0;
	} else {
		pageNo = parseInt(pageNo);
	}
	
	if (pageNo > 0) {
		// 전체 크기
		var totalCount = boardList.length;
		// 마지막 페이지 번호(전체 페이지 크기)
		var lastPageNo = Math.floor(totalCount / countPerPage) + (totalCount % countPerPage== 0 ? 0 : 1);
		// 시작 페이지 번호
		var startPageNo = 1;
		// 페이지 사이즈로 페이지 번호를 나눈 몫만큼 페이지 시작 번호 변경
		var start = Math.floor(pageNo / pageSize);
		if (start >= 1) {
			// 그렇지만 나머지가 없으면 현재 페이지 번호가 마지막 페이지 번호와 같아 감소
			if (pageNo % pageSize == 0){
				start--;
			}
			startPageNo = (start * pageSize) + 1;
		}
		// 종료 페이지 번호
		var endPageNo = (startPageNo - 1) + pageSize;
		// 그렇지만 종료 페이지 번호가 마지막 페이지 번호보다 크면 마지막 페이지 번호로 변경
		if (endPageNo > lastPageNo) {
			endPageNo = lastPageNo;
		}
		// 이전 페이지 사이즈 번호
		var prevPageSizeNo = startPageNo - 1;
		// 이전 페이지 사이즈 번호 활성화 여부
		var enablePrevPageSizeNO = true;
		if (prevPageSizeNo == 0) {
			enablePrevPageSizeNO = false;
		}
		// 다음 페이지 사이즈 번호
		var nextPageSizeNo = endPageNo + 1;
		// 다음 페이지 사이즈 번호 활성화 여부
		var enableNextPageSizeNO = true;
		if (nextPageSizeNo > lastPageNo) {
			enableNextPageSizeNO = false;
		}
		// 시작 번호
		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]);
			}
		}
		// 페이지네이션 정보
		var paginationInfo = {};
		paginationInfo.totalCount = totalCount;
		paginationInfo.countPerPage = countPerPage;
		paginationInfo.pageSize = pageSize;
		paginationInfo.startPageNo = startPageNo;
		paginationInfo.endPageNo = endPageNo;
		paginationInfo.lastPageNo = lastPageNo;
		paginationInfo.pageNo = pageNo;
		paginationInfo.prevPageSizeNo = prevPageSizeNo;
		paginationInfo.enablePrevPageSizeNO = enablePrevPageSizeNO;
		paginationInfo.nextPageSizeNo = nextPageSizeNo;
		paginationInfo.enableNextPageSizeNO = enableNextPageSizeNO;
		res.json({success:true, data:boardPageList, pagination:paginationInfo});
	} else {
		res.json({success:true, data:boardList});
	}
});

 

Postman(포스트맨)를 실행하여 테스트합니다.

http://localhost:9000/boards?pagesize=5&countperpage=2&pageno=1

{
    "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"
        }
    ],
    "pagination": {
        "totalCount": 13,
        "countPerPage": 2,
        "pageSize": 5,
        "startPageNo": 1,
        "endPageNo": 5,
        "lastPageNo": 7,
        "pageNo": 1,
        "prevPageSizeNo": 0,
        "enablePrevPageSizeNO": false,
        "nextPageSizeNo": 6,
        "enableNextPageSizeNO": true
    }
}

현재 페이지 번호가 1이기 때문에 이전 페이지 사이즈 번호의 활성화 여부가 false인 것을 확인할 수 있습니다.

현재 페이지 번호가 7이면 마지막 페이지 번호와 동일하기 때문에 다음 페이지 사이즈 번호의 활성화 여부가 false인 것을 확인할 수 있습니다.

http://localhost:9000/boards?pagesize=5&countperpage=2&pageno=7
{
    "success": true,
    "data": [
        {
            "no": 13,
            "subject": "테스트 제목13",
            "content": "테스트 내용13",
            "writer": "testid1",
            "writedate": "2022-03-27 00:30:00"
        }
    ],
    "pagination": {
        "totalCount": 13,
        "countPerPage": 2,
        "pageSize": 5,
        "startPageNo": 6,
        "endPageNo": 7,
        "lastPageNo": 7,
        "pageNo": 7,
        "prevPageSizeNo": 5,
        "enablePrevPageSizeNO": true,
        "nextPageSizeNo": 8,
        "enableNextPageSizeNO": false
    }
}

추가된 페이지네이션 정보 객체를 사용하면 별도의 계산 없이 페이지네이션을 쉽게 구현할 수 있습니다.

구현되는 페이지네이션 앞과 뒤에 이전 페이지 버튼과 다음 페이지 버튼이 있다면 추가로 버튼 활성화 여부를 추가하시면 됩니다.

router.get('/', function(req, res, next) {
	console.log("REST API Get Method - Read All");
	// 페이지 크기
	var countPerPage = req.query.countperpage;
	// 페이지 번호
	var pageNo = req.query.pageno;
	// 페이지 사이즈
	var pageSize = req.query.pagesize;
	
	if (countPerPage == undefined || typeof countPerPage == "undefined" || countPerPage == null) {
		countPerPage = 10;
	} else {
		countPerPage = parseInt(countPerPage);
	}
	if (pageSize == undefined || typeof pageSize == "undefined" || pageSize == null) {
		pageSize = 10;
	} else {
		pageSize = parseInt(pageSize);
	}
	if (pageNo == undefined || typeof pageNo == "undefined" || pageNo == null) {
		pageNo = 0;
	} else {
		pageNo = parseInt(pageNo);
	}
	
	if (pageNo > 0) {
		// 전체 크기
		var totalCount = boardList.length;
		// 마지막 페이지 번호(전체 페이지 크기)
		var lastPageNo = Math.floor(totalCount / countPerPage) + (totalCount % countPerPage== 0 ? 0 : 1);
		// 시작 페이지 번호
		var startPageNo = 1;
		// 페이지 사이즈로 페이지 번호를 나눈 몫만큼 페이지 시작 번호 변경
		var start = Math.floor(pageNo / pageSize);
		if (start >= 1) {
			// 그렇지만 나머지가 없으면 현재 페이지 번호가 마지막 페이지 번호와 같아 감소
			if (pageNo % pageSize == 0){
				start--;
			}
			startPageNo = (start * pageSize) + 1;
		}
		// 종료 페이지 번호
		var endPageNo = (startPageNo - 1) + pageSize;
		// 그렇지만 종료 페이지 번호가 마지막 페이지 번호보다 크면 마지막 페이지 번호로 변경
		if (endPageNo > lastPageNo) {
			endPageNo = lastPageNo;
		}
		// 이전 페이지 번호 활성화 여부
		var enablePrevPageNo = true;
		if ((pageNo - 1) == 0) {
			enablePrevPageNo = false;
		}
		// 다음 페이지 번호 활성화 여부
		var enableNextPageNo = true;
		if ((pageNo + 1) > lastPageNo) {
			enableNextPageNo = false;
		}
		// 이전 페이지 사이즈 번호
		var prevPageSizeNo = startPageNo - 1;
		// 이전 페이지 사이즈 번호 활성화 여부
		var enablePrevPageSizeNO = true;
		if (prevPageSizeNo == 0) {
			enablePrevPageSizeNO = false;
		}
		// 다음 페이지 사이즈 번호
		var nextPageSizeNo = endPageNo + 1;
		// 다음 페이지 사이즈 번호 활성화 여부
		var enableNextPageSizeNO = true;
		if (nextPageSizeNo > lastPageNo) {
			enableNextPageSizeNO = false;
		}
		// 시작 번호
		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]);
			}
		}
		// 페이지네이션 정보
		var paginationInfo = {};
		paginationInfo.totalCount = totalCount;
		paginationInfo.countPerPage = countPerPage;
		paginationInfo.pageSize = pageSize;
		paginationInfo.startPageNo = startPageNo;
		paginationInfo.endPageNo = endPageNo;
		paginationInfo.lastPageNo = lastPageNo;
		paginationInfo.pageNo = pageNo;
		paginationInfo.enablePrevPageNo = enablePrevPageNo;
		paginationInfo.enableNextPageNo = enableNextPageNo;
		paginationInfo.prevPageSizeNo = prevPageSizeNo;
		paginationInfo.enablePrevPageSizeNO = enablePrevPageSizeNO;
		paginationInfo.nextPageSizeNo = nextPageSizeNo;
		paginationInfo.enableNextPageSizeNO = enableNextPageSizeNO;
		res.json({success:true, data:boardPageList, pagination:paginationInfo});
	} else {
		res.json({success:true, data:boardList});
	}
});
728x90
반응형