ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] SpringBoot 프로젝트 : 리스트 페이징 처리(4) - 페이징 처리 수식 유도
    프로젝트/기능 정리 2023. 6. 20. 22:45

    목적

    • 게시판 형식 웹사이트 개발 시 DB에서 글 리스트 불러오기 기능 구현
    • 리스트 게시판 페이징 처리 받은 코드 리뷰

     

    우선 PagingUtil 클래스의 코드를 보기 전에 어떤식으로 동작하는지 알아야 한다.

     

    이를 위해 이번 글에서는 페이징 처리에 필요한 수식을 유도해보자.

     

    [데이터 흐름도]

    데이터 흐름도는 다음과 같다.

     

    게시판 리스트 데이터 흐름도

     

    [페이징 처리]

    우선 페이징 처리를 위해 어떤 값들이 필요한지 보자.

     

    아래 계산들을 위해서 다음 값들은 필수적으로 가지고 있어야 한다.

     

    • 현재 페이지
    • 전체 게시물 개수
    • 한 화면에 보여줄 게시물 수
    • 한 화면에 보여줄 페이지 번호 수

     

    [전체 페이지 수]

    먼저 전체 게시물의 개수를 알고 있고, 한 페이지에 몇 개의 게시물씩 보여줄지 정했다면 전체 페이지 수를 구할 수 있다.

     

    예를 들어, 전체 게시물 개수가 33개이고, 한 페이지에 5개씩 보여주기로 정했다면, 전체 페이지 수는

     

    33 / 5 = 6...3

     

    으로 몫이 6, 나머지가 3이 나올 것이다. 즉, 6개의 페이지로 나누고 3개의 게시물이 남는다는 의미가 되겠다.

     

    3개의 게시물이 남았으니 한 페이지를 추가해서 총 7개의 페이지가 필요하다는 건데.. 그럼 나온 몫에 그냥 1을 더해주면 되지 않을까..?

     

    한번 보도록 하자.

     

    34 / 5  + 1 =  6(...4) + 1 = 7

    35 / 5  + 1 =  7 + 1 = 8

     

    위와 같이 나누어 떨어지지 않는 경우에는 맞게 계산이 되지만, 나누어 떨어지는 경우(35/5=7) 불필요한 페이지가 하나 추가된다.

     

    5개씩 나누고 싶으니까 총 게시물이 1개, 2개, 3개, 4개, 5개일 때 1페이지만 있어야 하고, 6개 7개 8개 9개 10개일 때 2페이지가 존재해야 한다.

     

    근데 그냥 1을 더하면 총 게시물 수가 5이거나 10인 경우, 즉 나누어 떨어지는 경우 불필요한 페이지가 하나 추가되어,

     

    게시물이 1 or 2 or 3 or 4개일 때 1페이지, 5 or 6 or 7 or 8 or 9개일 때 2페이지, 10 or 11 or 12 or 13 or 14개일 때 3페이지가 존재하게 된다.

     

    몫의 입장에서 보면 각 총 게시물 수를 5로 나눴을 때 1, 2, 3, 4, 5는 0, 0, 0, 0, 1이되고, 6, 7, 8, 9, 10은 1, 1, 1, 1, 2가 된다.

     

    이를 1~5 / 6~10 / 11~15와 같이 다섯 개씩 묶기 위해 총 게시물 수에서 1을 빼주면 0, 1, 2, 3, 4에 몫이 0, 0, 0, 0, 0이 되고, 5, 6, 7, 8, 9는 1, 1, 1, 1, 1이 된다.

     

    거기에 1을 더해줄 것이니 총 글이 1~5개일 때 1, 1, 1, 1, 1이 되고, 6~10개일 때 2, 2, 2, 2, 2가 되어 원하는 페이징 결과가 나온다(총 글 개수가 1~5개이면 1페이지, 6~10개이면 2페이지가 되는 것).

     

    정리하면,

     

    ( (총 게시물 수 - 1) / 한 페이지에서 보여주고 싶은 글 개수 ) + 1

     

    를 해주면 전체 필요한 페이지의 수를 구할 수 있다.

     

    [한 페이지의 글 번호 - start, end]

    이전 글에서 Mapper에 적은 쿼리문을 설명하면서 rownum에 대해 설명했다(여기).

     

    다시 큰 틀만 보자면 다음과 같다.

    SELECT * FROM (SUBQUERY1) WHERE rnum >= #{start} AND rnum <= #{end}

    원하는 정렬대로 추출한 데이터에 rownum을 부여해서 원하는 rownum 범위만큼 추출할 수 있도록 했다고 생각하면 된다.

     

    그럼 쿼리문에 전달할 start와 end는 어떻게 구하는지 알아보자.

     

    start와 end는 문자 그대로 현재 페이지에 불러올 글의 시작 번호(정확히는 rownum 번호)와 끝 번호이다.

     

    예를 들어 10개의 글이 있고, 한 페이지에 5개씩 보여주기로 했다면 위에서 찾은 식에 따라,

     

    (10 - 1) / 5 + 1 = 1(...4) + 1 = 2

     

    로 2개의 페이지가 나올 것이고, 1페이지에 1번~5번 게시물, 2페이지에 6번~10번 게시물이 위치할 것이다.

     

    start와 end는 현재 페이지에 불러올 글의 시작 번호와 끝 번호라고 했으니까, 여기서 1페이지의 start는 1, end는 5가 된다.

     

    마찬가지로 2페이지의 start는 6, end는 10이다.

     

    이걸 어떻게 구할 수 있을까..?

     

    먼저 end는 구하기 쉬워 보인다.

     

    한 페이지에 보여주기로한 개수에 현재 페이지 번호를 곱하면 마지막 번호가 될 것이기 때문이다.

     

    예를 들어, 13개의 글이 있고 한 페이지에 5개씩 보여주기로 했다면,

     

    첫 번째 페이지의 마지막 글 번호인 end는

     

    1 × 5 = 5

     

    가 될 것이다.

     

    13번 글은 어떻게 되는건가 싶을 수 있는데, 여기서 start와 end는 특정 글 번호를 지정한다기보다 범위를 설정해 주는 것이므로, 13번 글이 위치한 3번 페이지의 데이터를 불러올 땐 end가 다음과 같을 것이다.

     

    3 × 5 = 15

     

    그러므로 어떤 식에 의해 유도된 3페이지의 start값인 11과 위 식으로 유도된 end값 15가 범위로 주어지고, 13은 그 범위에 해당하니까 3페이지 호출 시 불러올 수 있게 된 것이다.

     

    정리하면, 

     

    현재 페이지 번호 × 한 페이지에서 보여주고 싶은 글 개수

     

    를 통해 한 페이지 내에서 마지막 글 번호를 알아낼 수 있다.

     

    그럼 이제 start를 구해보자.

     

    느낌상 마지막 번호를 구한 다음 그 값에서 다시 한 페이지에서 보여주고 싶은 게시물의 개수만큼 빼면 될 것 같다.

     

    만약 총 11개의 게시물이 있고 한 페이지에서 5개씩 보여주고자 한다면, 2페이지 마지막 글 번호는 위 식에 따라 

     

    2 × 5 = 10

     

    이 될 것이다.

     

    여기서 다시 한 페이지에서 보여주고자 했던 글 개수인 5를 빼주면, 5가 나온다.

     

    근데 5번은 1페이지에 위치한 글의 마지막 번호가 된다.

     

    교정을 위해 1을 더해주자.

     

    이 방식을 3페이지에 적용해보면,

     

    (3 × 5) - 5 + 1= 15 - 5 + 1 = 11

     

    로 3페이지의 첫 글 번호인 11을 구할 수 있게 된다.

     

    이를 좀 더 그럴듯 하게 묶어서 정리하면,

     

    (현재 페이지 번호 - 1) × 한 페이지에서 보여주고 싶은 글 개수 + 1

     

    이 된다. 한 페이지에서 보여주고 싶은 글 개수를 5, 현재 페이지 번호를 3으로 두고 곱셈을 분배해보면 위와 같다는 것을 알 수 있을 것이다.

     

    이미 페이지에 글 번호 순서로 나와 있는데 역으로 글 번호를 구하는 것 같아 이 과정이 와닿지 않을 수 있는데, 이건 식을 유도하려는 과정일 뿐이다.

     

    데이터 흐름을 잘 생각해보면, 이 start와 end값을 1~5 / 6~10 이렇게 정해서 DB에 전달했기 때문에 번호순으로 리스트를 뽑아내는 것이 가능하다는 것을 다시 생각해보면 도움이 될 것 같다.

     

    암튼 이렇게 한 페이지에 보이는 첫 글의 번호와 마지막 글의 번호를 구했다.

     

    [페이지 번호]

    이제 마지막으로 페이지 번호를 구해보자.

     

    보통 페이징 처리된 게시판을 보면 아래 페이지 번호가 5개나 10개 혹은 20개까지 나와있는 경우가 있는데, 이를 5개나 10개씩 끊어서 보여주고 다음 또는 이전 버튼 처리등을 하기 위해서는 페이지 번호의 시작값과 끝값이 필요하다.

     

    예를 들면, 33개의 게시물을 5개씩 보여줄 것이라고 하면 위에서 도출한 식에 따라 7개의 페이지가 필요하다.

     

    이를 5개씩 끊어서 보여준다면, 첫 화면에 1, 2, 3, 4, 5 [다음] 과 같이 페이지 번호가 보일 것이고, 다음을 누르면 [이전] 6, 7이 보일 것이다.

     

    이 때 1, 2, 3, 4, 5가 한 묶음이 되고, 6, 7, (8), (9), (10)이 한 묶음이 된다.

     

    앞 묶음에서 시작 번호는 1, 끝 번호는 5가 될 것이고 뒷 묶음에서 시작 번호는 6, 끝 번호는 7이 될 것이다.

     

    처음에 봤던 전체 페이지 구하는 것과 유사한 느낌이 든다.

     

    몫의 입장에서 보면 각 총 게시물 수를 5로 나눴을 때 1, 2, 3, 4, 5는 0, 0, 0, 0, 1이되고, 6, 7, 8, 9, 10은 1, 1, 1, 1, 2가 된다.

     

    라고 했던 문장이 기억 난다면, 뭔가 이를 페이지에서도 사용할 수 있겠다는 생각이 들 것이다.

     

    1, 2, 3, 4, 5를, 한 화면에 보여주고 싶은 페이지 수인 5로 나눴을 때 몫은 0, 0, 0, 0, 1이 된다.

     

    위에서 했던 것처럼, 각 번호에 1을 빼주고 보여주고 싶은 페이지 수를 나누면, 그 수까지는 몫이 같아질 것이다.

     

    페이지 번호 1, 2, 3, 4 | 5 1, 2, 3, 4, 5 | 6 1, 2, 3, 4, 5, 6 | 7
    페이지 번호 - 1 0, 1, 2, 3 | 4 0, 1, 2, 3, 4 | 5 0, 1, 2, 3, 4, 5 | 6
    나눌 수(보여줄 페이지 개수) 4 5 6
    0, 0, 0, 0 | 1 0, 0, 0, 0, 0 | 1 0, 0, 0, 0, 0, 0 | 1

    그렇다면 (현재 페이지 번호 - 1)을 보여주고 싶은 페이지 수로 나누고, 거기에 1을 더하면 화면에 보이는 페이지 번호의 첫 수를 알 수 있을 것 같다.

     

    위 표의 첫 번째 경우에서 현재 페이지가 2라고 해보면,

     

    (2 - 1) / 4 + 1 = 1 / 4 + 1 = 0(...4) + 1 = 1

     

    로 시작 페이지 번호인 1이 나온다. 그럼 이제 현재 페이지가 5라고 가정해보자.

     

    (5 - 1) / 4 + 1 = 4 / 4 + 1 = 1 + 1 = 2

     

    만약 페이지 번호를 4개씩 보여주는 위 경우라면 5는 [다음]을 눌렀을 때 나올 번호일 것이고, 그 리스트의 첫 번호는 5니까 5가 나왔어야 한다.

     

    어떻게 구할 수 있을까. 위 표를 다시 보자.

     

    나눌 수(보여줄 페이지 개수) 4 5 6
    0, 0, 0, 0 | 1 0, 0, 0, 0, 0 | 1 0, 0, 0, 0, 0, 0 | 1

    각각 4개, 5개, 6개씩 페이지 번호를 보여주고 싶은데, 그렇게 했을 때 [다음] 이후 리스트의 첫 번호는 각각 5,  6, 7일 것이다.

     

    방금은 그냥 몫에 1을 더했다.

     

    왠지 표에 나온 나눌 수와 몫을 봤을 때, 두 수를 곱하고 1을 더하면 [다음] 이후 리스트의 첫 번호가 정확하게 들어 맞을 것 같다.

     

    다시 위에서 했던 예를 가져와 4개씩 나누는데 현재 페이지가 5인 경우를 보면,

     

    ((5 - 1) / 4) × 4 + 1 = (4 / 4) × 4 + 1 = 1 × 4 + 1 = 5

     

    로 리스트의 첫 번호인 5가 잘 나온다. 혹시 모르니 현재 페이지가 2인 경우도 다시 계산해 보면,

     

    ((2 - 1) / 4) × 4 + 1 = (1 / 4) × 4 + 1 = 0(...4) × 4 + 1 = 1

     

    로 정확하게 나온다.

     

    정리하면 페이지의 시작 번호는,

     

    ( ( 현재 페이지 번호 - 1 ) / 페이지 번호 개수 ) × 페이지 번호 개수 + 1

     

    이 된다(페이지 번호 개수 = 한 페이지에서 보여주고 싶은 페이지 번호 개수, 길어서 줄여썼다).

     

    끝 번호를 구해보자.

     

    첫 페이지 번호를 구했으니 끝 번호는 또 간단해 보인다.

     

    첫 페이지 번호에 한 페이지에서 보여줄 페이지 번호 개수를 더하면 될 것 같다.

     

    예를 들어, 5개씩 페이지 번호를 나눠서 보여주고 싶은데 현재 구한 페이지 번호의 첫 숫자가 1이라면,

     

    1 + 5 = 6

     

    하나가 더 많으니까 다시 1을 빼주자.

     

    1 + 5 - 1 = 5

     

    만약, 4개씩 페이지 번호를 나눠서 보여주는데 현재 페이지가 7페이지라면, 

     

    위에서 구해둔 시작 페이지 식에 따라

     

    ((7 - 1) / 4) × 4 + 1 = (6 / 4) × 4 + 1 = 1(...2) × 4 + 1 = 5

     

    로 시작 페이지 번호는 5가 나올 것이고, 마지막 페이지 번호는

     

    5 + 4 - 1 = 8

     

    로 8이 될 것이다.

     

    정리하면 페이지의 끝 번호는,

     

    ( ( ( 현재 페이지 번호 - 1 ) / 페이지 번호 개수 ) × 페이지 번호 개수 + 1 ) + 페이지 번호 개수 - 1

    ≒ 시작 번호 + 페이지 번호 개수 - 1

     

    라고 할  수 있겠다.

     

    한 가지가 더 있는데, 처음 작성했던 예를 다시 보자.

     

    예를 들면, 33개의 게시물을 5개씩 보여줄 것이라고 하면 위에서 도출한 식에 따라 7개의 페이지가 필요하다.

    이를 5개씩 끊어서 보여준다면, 첫 화면에 1, 2, 3, 4, 5 [다음] 과 같이 페이지 번호가 보일 것이고, 다음을 누르면 [이전] 6, 7이 보일 것이다.

    이 때 1, 2, 3, 4, 5가 한 묶음이 되고, 6, 7, (8), (9), (10)이 한 묶음이 된다.

    앞 묶음에서 시작 번호는 1, 끝 번호는 5가 될 것이고 뒷 묶음에서 시작 번호는 6, 끝 번호는 7이 될 것이다.

     

    위 예시에서 현재 페이지가 6이라면 페이지 시작 번호는,

     

    ((6 - 1) / 5) × 5 + 1 = (5 / 5) × 5 + 1 = 1 × 5 + 1 = 6

     

    로 6이 나오고, 끝 번호는

     

    6 + 5 - 1 = 10

     

    로 10이 나온다.

     

    근데 총 페이지가 7페이지까지니까 사실상 끝 번호는 7이 되어야 한다.

     

    이렇기에 조건이 필요한데 만약 끝 번호가 총 페이지 수보다 크다면 끝 번호는 총 페이지 수가 되어야 한다.

     

    즉, 결과가 10으로 나왔지만 끝 번호가 총 페이지 수보다 크게 나왔기 때문에 이 경우의 끝 번호는 7이 된다.

     

    IF 페이지 끝 번호 > 총 페이지 수 → 페이지 끝 번호 = 총 페이지 수

     

     

     

    이렇게 해서 페이징 처리를 하기 위해 계산해야 할 식들을 유도해 봤다.

     

    코드를 보고 구글링을 하며 식을 유도해 봤는데, 정확하게 분석한 것인지 모르겠다.

     

    일단 결론적으로 세운 식들이 받은 코드와 유사하거나 구글링을 통해 찾아본 식과 같긴 하니까.. 맞게 생각을 정리한 것 같다.

     

    그냥 아무 생각 없이 받아서 쓴 코드에 생각할 내용이 많아 시간이 걸렸지만.. 아무튼 정리 완료

     

    다음 글에서는 최종적으로 코드를 볼텐데 오늘 유도한 식을 통해 일부 변형해서 작성해보도록 하겠다.

Designed by Tistory.