-
[Java] SpringBoot 프로젝트 : 리스트 페이징 처리(5) - PagingUtil 클래스프로젝트/기능 정리 2023. 6. 21. 15:37
목적
- 게시판 형식 웹사이트 개발 시 DB에서 글 리스트 불러오기 기능 구현
- 리스트 게시판 페이징 처리 받은 코드 리뷰
이제 마지막으로 PagingUtil 클래스 내부 코드를 보도록 하자.
이전 글에서 페이징 처리에 필요한 값들을 구하는 식을 봤으니, 이제 코드를 보면 쉽게 이해가 갈 것이다.
[데이터 흐름도]
데이터 흐름도는 다음과 같다.
게시판 리스트 데이터 흐름도 [PagingUtil 클래스]
우선 클래스 내부 코드를 보기 전 어떤 인자를 넘겨줬었는지 다시 보자.
아래는 컨트롤러에서 PagingUtil 객체를 생성하는 부분이다.
PagingUtil page = new PagingUtil(keyfield, null, currentPage, count, 5, 5, "pointList.do");
각 인자들이 무엇인지 앞에서 설명했지만, 다시 간단히 말하면
- keyfield: 리스트 정렬 옵션 값
- keyword: 검색 기능 구현 시 사용자가 입력한 검색 키워드
- currentPage: 현재 사용자가 요청한 페이지
- count: DB에서 가져온 총 게시물 수
- rowCount: 한 페이지에서 보여줄 게시물 수
- pageCount: 한 페이지에서 보여줄 페이지 번호 수
- url: 페이지 번호에 넣어줄 호출 페이지URL
이 되겠다.
이제 클래스 내부를 보자.
public class PagingUtil { private int startRow; //한 페이지에서 보여줄 글 시작 번호(start) private int endRow; //한 페이지에서 보여줄 글 끝 번호(end) private StringBuffer page; //페이지 표시 문자열 public PagingUtil(String keyfield, String keyword, int currentPage, int count, int rowCount, int pageCount,String pageUrl) { //페이징 처리 코드 } public int getStartRow() { return startRow; } public int getEndRow() { return endRow; } public StringBuffer getPage() { return page; } }
PagingUtil 객체가 리턴할 수 있는 값은 총 세 개이다.
차례로 보면, 글 시작 번호, 글 끝 번호, 페이지 문자열이다.
시작 번호와 끝 번호가 무엇인지는 이전 글에서 설명했다.
페이지 문자열은 HTML태그를 직접 작성한 뒤 버퍼에 담아 리턴하는 것으로, 하단에 페이지 버튼을 생성하는 HTML태그 모음이라고 생각하면 된다.
이제 어제 설명한 순서로 필요한 값들을 구해보자.
[전체 페이지 수]
전체 페이지 수를 구하는 수식은 아래와 같았다.
( (총 게시물 수 - 1) / 한 페이지에서 보여주고 싶은 글 개수 ) + 1
코드로 표현하면, 아래와 같다.
int totalPage = ((count - 1) / rowCount) + 1;
여기서 한 가지 조건을 추가해 줘야한다.
현재 페이지의 경우 GET방식으로 URL을 통해 전달하게 되는데 예를 들면 다음과 같다.
currentPage, keyfield 전달 방식 따라서 못된(?) 이용자가 임의로 pageNum(= currentPage) 값을 전체 페이지 수보다 크게 입력할 수 있는데, 이를 방지하기 위해 다음 조건문을 넣어주면 좋다.
if (currentPage > totalPage) { currentPage = totalPage; }
물론 반대로 가장 작은 페이지 번호는 1일테니 1보다 작은 음수값이 들어왔을 때에도 currentPage 값을 1로 고정해주는 방식으로 오류를 막을 수 있을 것이다.
[한 페이지의 글 번호 - start, end]
이제 쿼리문에 사용하기 위해 필요했던 한 페이지에 보여줄 게시물의 시작 번호와 끝 번호를 구해보자.
마지막 글 번호를 구하는 식은 다음과 같았다.
현재 페이지 번호 × 한 페이지에서 보여주고 싶은 글 개수
코드로 표현하면, 아래와 같다.
endRow = currentPage * rowCount;
다음으로 시작 글 번호를 구해보자.
식은 아래와 같다.
(현재 페이지 번호 - 1) × 한 페이지에서 보여주고 싶은 글 개수 + 1
이 식을 전개해보면, (끝 번호 - 한 페이지에서 보여주고 싶은 글 개수 + 1)이 된다.
코드로 표현하면, 아래와 같다.
startRow = (currentPage - 1) * rowCount + 1;
[페이지 번호]
마지막으로 페이지 번호의 시작값과 끝값을 구해보자.
시작 번호를 구하는 식은 아래와 같았다.
( ( 현재 페이지 번호 - 1 ) / 페이지 번호 개수 ) × 페이지 번호 개수 + 1
이를 코드로 표현하면 다음과 같다.
int startPage = (int) ((currentPage - 1) / pageCount) * pageCount + 1;
끝 번호를 구하는 식은 다음과 같았다.
( ( ( 현재 페이지 번호 - 1 ) / 페이지 번호 개수 ) × 페이지 번호 개수 + 1 ) + 페이지 번호 개수 - 1
≒ 시작 번호 + 페이지 번호 개수 - 1
코드로 보면 아래와 같다.
int endPage = startPage + pageCount - 1;
이전 글에서 마지막에 제시했듯, 페이지 끝 번호를 구하는 식을 통해 얻은 값이 전체 페이지 수보다 큰 경우가 있을 수 있다.
따라서 아래 조건을 추가해준다.
if (endPage > totalPage) { endPage = totalPage; }
[HTML태그 생성]
페이지 번호와 [다음] 또는 [이전]버튼을 생성할 때, 해당 버튼에 주소가 매핑되어야 한다.
검색 조건 또는 정렬 조건이 keyword와 keyfield에 들어 있으므로 우선 주소 뒤에 달아줄 문자열을 만들어 두자.
String sub_url = ""; if(keyword != null) { sub_url = "&keyfield="+keyfield+"&keyword="+keyword; } else if(keyword == null) { sub_url = "&keyfield="+keyfield; }
이제 위에서 구한 값들을 이용해 조건에 따라 태그를 생성하고, 페이지 번호에 링크를 달아줘야 한다.
이 부분은 태그에 원하는 클래스나 인라인 스타일 등을 부여해서 커스텀할 수 있으니, 태그 자체의 내용보다는 조건을 유의해서 보면 될 것이다.
이해를 위해 개념을 조금 잡고 가겠다.
예를 들어, 33개의 게시물이 있고 5개씩 나눠서 글을 보여줄 것인데, 5개의 페이지씩 분할하고 싶다면,
위 식의 계산들에 따라 총 7개의 페이지가 나오고 1, 2, 3, 4, 5 [다음]이 한 묶음, [이전] 6, 7 이 한 묶음이 될 것이다.
먼저 [이전]버튼을 보자.
[이전]버튼은 간단하다. 현재 페이지가 한 페이지에서 보여주기로한 버튼의 개수보다 크면, 이미 한 묶음이 앞에 있다는 뜻이다.
다시 말하면 현재 페이지가 6번일 때, 5개씩 보여주기로 했으니까, 6 > 5로 앞에 5개의 한 묶음이 있다는 뜻이다. 이럴 때 [이전]버튼을 표시하면 된다.
page = new StringBuffer(); if (currentPage > pageCount) { page.append("<a href="+pageUrl+"?pageNum="+ (startPage - 1) + sub_url +">"); page.append("[이전]"); page.append("</a>"); }
전달하는 주소는 호출할 컨트롤러 주소 뒤에 pageNum(= currentPage)값을 (현재 보고 있는 페이지 번호의 첫 번째 값 - 1)로 넣어주어 앞 묶음의 가장 마지막 번호 페이지를 매핑하겠다는 의미로 보면 된다.
[다음] 버튼은 언제 생성되어야 할까..?
만약 총 페이지가 5개일 경우 [다음] 버튼은 없을 것이다.
하지만 6개 이상이라면 [다음] 버튼은 있을 것이다.
만약 현재 6페이지에 위치했는데 총 페이지가 11개 이상이라면, [다음] 버튼은 있을 것이다.
좀 더 정리해서 말하자면 5개씩 보여주기로 했는데,
총 페이지가 현재 화면에 보이는 (시작 페이지 번호 + 보여주기로 한 페이지 개수) 이상이면 그 뒤에 페이지가 더 있다는 뜻이다.
예를 들어 아래와 같을 때(현재 페이지 번호는 시작 페이지 번호 구할 때 사용함),
총 페이지 수 현재 페이지 번호 시작 페이지 번호 페이지 번호 개수 시작 + 번호 개수 6 3 1 5 1 + 5 = 6 이니까 [다음] 버튼이 보여야 한다.
총 페이지 수 현재 페이지 번호 시작 페이지 번호 페이지 번호 개수 시작 + 번호 개수 5 2 1 5 1 + 5 = 6 반대로 위와 같은 경우 (시작 번호 + 번호 개수)가 6으로 총 페이지 수보다 적다. 따라서 [다음]버튼은 없어야 한다.
코드를 보자.
if (totalPage >= startPage + pageCount) { page.append("<a href="+pageUrl+"?pageNum="+ (endPage + 1) + sub_url +">"); page.append("[다음]"); page.append("</a>"); }
여기서도 전달하는 주소는 pageNum에 현재 화면에 보이는 (페이지 번호 중 마지막 값 + 1)을 할당해, 다음 페이지의 시작 번호를 매핑하겠다는 의미다.
마지막으로 번호 자체를 생성하는 코드를 보자.
번호는 페이지 시작 번호 ~ 끝 번호까지 루프를 돌려서 생성하면 된다.
for (int i = startPage; i <= endPage; i++) { if (i > totalPage) { break; } if (i == currentPage) { page.append(" <b><span style='color:red;'>"); page.append(i); page.append("</span></b>"); } else { page.append(" <a href='"+pageUrl+"?pageNum="); page.append(i); page.append(sub_url+"'>"); page.append(i); page.append("</a>"); } page.append(" "); }
[이전] 페이지 번호 [다음] 순으로 태그가 만들어져야 하므로 코드 순서도 아래와 같이 적어야 한다.
[이전] 코드
페이지 번호 코드
[다음] 코드
[결과화면]
페이징 결과화면 page 관련 태그 코드를 생성할 때, 부트스트랩 클래스 등을 잘 사용해서 작성하면, 아래와 같이 만들 수 있다.
페이징 디자인 커스텀 이상으로 리스트 페이징 처리 관련 내용을 모두 정리해봤다.
그 과정에서 @RequestParam, ModelAndView, xml, EL표현식 param, 제네릭 표현식, Oracle ROWNUM, 페이징 처리 수식 유도 방법 등의 개념도 짧게나마 정리했다.
PagingUtil 클래스 내부 코드는 일부 내가 이해하고 유도한 수식대로 변형한 부분이 있지만, 받아서 쓴 코드가 잘 작성되어 있어 대부분 그대로 가져왔다.
이 내용을 기반으로 페이징 관련 처리를 할 때, 상황에 따라 변형해서 사용할 수 있을 것으로 기대한다.
혹시 잘못된 정보나 오류가 있으면 알려주길 바라며.. 리스트 페이징 처리 정리를 마치도록 하겠다.
'프로젝트 > 기능 정리' 카테고리의 다른 글