ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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");

    각 인자들이 무엇인지 앞에서 설명했지만, 다시 간단히 말하면

     

    1. keyfield: 리스트 정렬 옵션 값
    2. keyword: 검색 기능 구현 시 사용자가 입력한 검색 키워드
    3. currentPage: 현재 사용자가 요청한 페이지
    4. count: DB에서 가져온 총 게시물 수
    5. rowCount: 한 페이지에서 보여줄 게시물 수
    6. pageCount: 한 페이지에서 보여줄 페이지 번호 수
    7. 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("&nbsp;<b><span style='color:red;'>");
            page.append(i);
            page.append("</span></b>");
        } else {
            page.append("&nbsp;<a href='"+pageUrl+"?pageNum=");
            page.append(i);
            page.append(sub_url+"'>");
            page.append(i);
            page.append("</a>");
        }
        page.append("&nbsp;");
    }

    [이전] 페이지 번호 [다음] 순으로 태그가 만들어져야 하므로 코드 순서도 아래와 같이 적어야 한다.

     

    [이전] 코드

     

    페이지 번호 코드

     

    [다음] 코드

     

    [결과화면]

     

    페이징 결과화면

     

    page 관련 태그 코드를 생성할 때, 부트스트랩 클래스 등을 잘 사용해서 작성하면, 아래와 같이 만들 수 있다.

     

    페이징 디자인 커스텀

     

    이상으로 리스트 페이징 처리 관련 내용을 모두 정리해봤다.

     

    그 과정에서 @RequestParam, ModelAndView, xml, EL표현식 param, 제네릭 표현식, Oracle ROWNUM, 페이징 처리 수식 유도 방법 등의 개념도 짧게나마 정리했다.

     

    PagingUtil 클래스 내부 코드는 일부 내가 이해하고 유도한 수식대로 변형한 부분이 있지만, 받아서 쓴 코드가 잘 작성되어 있어 대부분 그대로 가져왔다.

     

    이 내용을 기반으로 페이징 관련 처리를 할 때, 상황에 따라 변형해서 사용할 수 있을 것으로 기대한다.

     

    혹시 잘못된 정보나 오류가 있으면 알려주길 바라며.. 리스트 페이징 처리 정리를 마치도록 하겠다.

Designed by Tistory.