게시판 페이징에 관련해 수업을 한 적이 있다.
그때도 로직만 보고 시작했어서
설명하면서 정리한 자료가 내게는 없다.
기억을 더듬어 조금 더 생각을 정리하며 적어보고자 한다.
사실 텍스트만으로는 조금 전달이 복잡할 수 있을것 같지만 노력해보겠다.
처음 페이징로직을 접하면 아마 많이들 뭐지? 하는 생각을 하지 않을까 한다.
적어도 나는 그랬다.
페이징은 구조를 이해하고나면 간단함을 알 수 있다.
아주 동일하게 주기적으로 반복되는 로직이기 때문이다.
( 전체 규칙을 파악하고자 하는 것이 목표이므로, 글번호는 1번부터 시작하고, 중간에 삭제되거나 번호가
점프된 글은 없다고 가정한다. 이거야 어차피 정렬하면 되니까 굳이 지금 상황에 예외를 확인할 필요는 없다. )
각 페이지의 마지막 게시글의 번호를 구하는 법을 알아보자.
한 페이지에는 10개의 게시글을 나타낼 것이다.
1페이지에는 1 ~ 10 의 게시글의 나타날 것이다. 그렇다면 1페이지에는 10번의 글이 마지막에 올 것이다.
2페이지에는 11 ~ 20 의 게시글이 나타날 것이다. 그러면 2페이지에는 20번의 글이 마지막에 올 것이다.
그렇다면
( 규칙을 정말 바로 찾아내고 생각할 수 있는 사람들도 있겠지만, 사실 이렇게 예시를 찾아가며
샘플링을 하는 것의 효율이 엄청 좋다. 뭐.. 천재들은 다르겠지만 )
n페이지에는 n * 10 의 게시글이 마지막에 나타날 것이다.
너무 간단했나?
10처럼 딱 떨어지는 숫자 말고 한 페이지에 8개의 게시글이 오는 상황도 보자.
1 페이지 : 1 ~ 8
2 페이지 : 9 ~ 16
3 페이지 : 17 ~ 24
이게 딱. 딱. 그려지지 않을 수 있다. 검증을 해보자.
( 다양한 셈과 확인의 방법이 있고, 확인하는 과정과 방법을 따라해보고 알아보자. 다른 방법이더라도 좋다
어떤 방법이던 본인의 방법을 찾고 해보고 하는 훈련이 중요하다. )
각 페이지에는 8개씩의 게시글이 들어가야한다.
우선, 1부터 2까지의 갯수를 세어보자. 2개이다.
2 - 1 = 1 이고, 여기에 1을 더하면 2개가 된다.
( 갯수세는 데에 왜 뺀다음 1을 더해야 갯수가 되는지는 수직선등을 그려보면 쉽게 알 수 있다. )
8 - 1 = 7 이고, 여기에 1을 더하면 8개가 된다.
16 - 9 = 7 이고, 여기에 1을 더하면 8개가 된다.
24 - 17 = 7 이고, 여기에 1을 더하면 8개가 된다.
각 페이지에 8개가 들어감을 알 수 있고,
각 페이지의 마지막 글번호는 ( 한 페이지의 글 갯수 ) * 페이지 수
가 됨을 알 수 있다.
이번에는 페이지의 첫번째 게시글 번호를 구하는 법을 알아보자.
혹시 이렇게 생각해보면 어떨까?
현재 페이지의 첫번째 게시글 번호는 전 페이지의 마지막 글번호 + 1 이다.
1페이지에 1 ~ 10 이 있었으면
2페이지에는 10번 다음 번호인 11번이 오는 것이다.
글 번호는 1이 최소단위 ( 로 일반적으로 사용되기 때문에, 0.5번은 없지않은가 ) 이기 때문에
10번이 마지막이였으면 11번은 그 다음이 될 것이다.
그리고 10번은 마지막 글 번호였기때문에 다음페이지로 넘어갈 것이고, 11번이 다음 페이지의 첫번째 글이 된다.
즉, ( 전 페이지의 마지막 번호 ) + 1 이 현재 페이지의 첫번째 글 번호이다.
그렇다면, 첫 페이지는 어떨까?
0페이지의 마지막 번호는 몇번일까? 0페이지는 없기도하고, 1번 이전의 글도 없기도 하니 그냥 0으로 생각하자,
뭐 프로그래밍을 공부하는 분들이 보실테니 null?을 떠올리실 수도 있겠지만
이건 셈으로 접근하는 것이지 프로그래밍은 아니니 0 으로 생각해주시라.
그렇다면 식으로 세워보면 어떨까?
한 페이지당 p개의 게시글이 있고, n페이지의 경우
( n - 1 ) 페이지가 전 페이지가 될 거고
( n - 1 ) * p 가 전 페이지의 마지막 글 번호가 될테니
( n - 1 ) * p + 1 이 n페이지의 첫번째 게시글 번호가 될 것이다.
이 부분이 혹시 너무 낯설게 느껴진다면 찬찬히 순서대로 읽어보시라.
각 구문이 무엇을 의미하는지를 알게되면 전체를 바라볼 수 있다.
단계단계별로 생각하는 것이 이해에 상당한 도움이 된다.
( 사실 수업 당시에는 즉흥적으로 떠올린 수식이라 그래프, 빵 묶음등등 다양한 방법을 떠올려 동원했으나
텍스트상 담기 어려운 부분이 있어 여러번 읽어보시라. 혹시 너무 그래도 각 구문이 의미하는 바가
이해되지 않는다면, 직접 빵 묶음, 페이징을 그려보시거나.. 연락해보시라)
( 전 페이지라는 개념을 잡고, 그 이후 라는 것. 현재 페이지는 전 페이지의 다음 이라는 것 을 유념하자 )
그럼 우리는 지금
첫 페이지와 마지막 페이지의
글 번호를 구하는 법을 알았다.
이번에는 전체 페이지를 구하는 법을 알아보자.
여태까지는 특정 페이지의 맨 앞, 맨 뒤 등으로 하나의 게시글을 목표로 달려보았다.
이제는 글을 묶음으로 바라봐야할 때이다.
1 2 3 4 5 6 7 8 9 10 번의 글이 있다.
혹시 10개 주기로 돌아가는 수가 무엇인지 떠오르는가?
10의 배수는 10개 단위로 수가 나타난다.
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
보면 10의 배수는 1씩 증가하는 자연수에서 10번째 수마다 나타나는 수이다.
사실 당연하다. 배수 라는게 그 곱이고
그 곱이라는게 그 갯수만큼의 묶음의 결과고
그 묶음에는 그 갯수만큼 들어있고
즉, 2의 배수는 2개 단위로
3의 배수는 3개 단위로 나타나는 수이다.
우리는 고정된 한페이지의 노출되는 값을 갖고있고, ( 현재 내가 가진 것 )
마지막 글 번호가 몇 번째 페이지에 속하는지 ( 알아야 하는 것 )
를 구할 것이다.
바로 p개의 글을 한 페이지에 표현할때는 각 페이지에는 p개의 글이 들어가고
p개의 주기로 페이지가 반복된다는 점을 이용해서 말이다.
p의 배수가 ( 혹시 잊을까봐 다시 언급하면, p는 한 페이지의 게시글 갯수로 하자. )
p번째 숫자마다 반복된다는 점은.
사실 나누기를 했을때 p개의 숫자가 나머지로 반복된다는 말이다.
다시 봐보자.
1 2 3 4 5 6 7 8 9 10
의 각 수를 10으로 나누어보자.
1 2 3 4 5 6 7 8 9 0
이 된다.
11 12 13 14 15 16 17 18 19 20
은 어떤가
1 2 3 4 5 6 7 8 9 0
이 된다
21 22 23 24 25 26 27 28 29 30
은 어떤가
1 2 3 4 5 6 7 8 9 0
이 된다
보면... 뭔가 뭔가라고 하기도 뭐하게
똑같지 않은가
이처럼 주기가 반복되는 것이고
p로 나누게 되면 이렇게 된다.
1 2 3 ... (p-1) 0
이 된다. 왜냐하면 마지막은 p의 배수로 p*k ( k는 임의의 자연수 ) 가 되고
p로 나누면 몫이 k가 되고 나머지가 없기때문이며,
1 2 3 이렇게 나오는 수들은
p*(k-1) + 1, p*(k-1) + 2, p*(k-1) + 3 ... p*(k-1) + (p-1), p*(k-1) + p
이렇게 표현되고, p로 나누면 나머지가 뒤에 더하기들만 생겨서
1 2 3 ... k-1, 0
이 되는 것이다.
이 부분도 좀 직접 눈 앞에서 써가며, 그려가며 설명한다면 훨씬 좋을텐데 아쉽다.
최대한 단계를 나누어 표현해보았으니 참고가 되면 좋겠다.
자, 그럼 여기까지 왔으면 많이 왔다!
한번 중간정리를 해보면,
한 페이지에 p개의 게시글이 나온다면,
그 페이지의 각 게시글 번호를 p로 나누면
1 2 3 4 ... p-1 0
의 나머지를 갖게된다.
참 예쁜모양인데 페이지 번호로 쓰기에는 조금 아쉽다. 심지어 몫을보면
k-1 k-1 k-1 k-1 ... k-1 k
로 몫도 차이가 난다.
이건 갑작스레 등장한 것 같을테니 ( 사실 모양만 다르게 위에 있지만 )
이것도 우리 같이 샘플링을 해보자 ( 이해에 진짜 도움되는게 샘플링이다. )
1 2 3 4 5 6 7 8 9 10
을 10으로 나누어 보자
0 0 0 0 0 0 0 0 0 1
이 된다. 마지막만 10번째 숫자니까 1개가 올라간 것이 보이는가
11 12 13 14 15 16 17 18 19 20
도 10으로 나누어 보자
1 1 1 1 1 1 1 1 1 2
이 된다. 마지막만 10번째 숫자로 하나가 역시 증가했다. 10개들이 묶음이 하나가 더 들어난 것이다.
21 22 23 24 25 26 27 28 29 30
역시 10으로 나누어보면
2 2 2 2 2 2 2 2 2 3
으로 동일하다.
이 처럼 지금 보면 10으로 각 페이지의 게시글 번호를 나누었을때
나머지는 1부터시작해 0까지
몫은 마지막 숫자만 하나가 크다.
그러니 우리 좀 더 예쁜모양으로 각 페이지에 담기위해서
-1 을 각 수에 한다면 어떨까?
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
이
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
이렇게 되는 것이다.
( 사실 이 말은 0번 글부터 시작하면 이 것과 동일하겠지만,
1번글부터 시작하는 것이 사람들의 인지에 더 일치하니 그렇게 잘 하지도 않거니와
지금은 계산을 위해 임의로 각 게시글의 번호에서 1을 뺀 값을 나타낸 것이다. )
이번에 10으로 나눠보게 되면
나머지는
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
이 될 것이고
몫은
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
로
아주아주 예쁜 구조가 나타나게 된다. :)
첫번째 1 ~ 10은 1페이지 였고,
이후부터 2,3 페이지였으니
각 몫의 + 1 을 한 값이 그 게시글의 페이지 번호임을 알 수 있다.
그러면 우리는 지금까지의 과정으로
1. 글 번호 - 1
2. 페이지 당 갯수로 나누기
3. 몫 + 1 은 페이지, 나머지 + 1은 그 페이지에서의 번호
임을 알 수 있고,
사실 몇 페이지 몇 번째까지인지는 수학문제에서나 구할 법 한 거기때문에 차치하고
몫 + 1이 페이지 번호임을 구할 수 있다.
지금 모든 글 번호에 대해 해당 과정을 적용시킨 것으로
우리는 특정 글 번호가 주어졌을때
이게 마지막 글 번호라면 마지막 페이지, 즉 총 페이지 번호를 알 수 있고,
특정 글 번호라면, 해당 글의 페이지 번호를 알아낼 수 있을 것이다.
이처럼 페이지에 대한 내용은 사실 길게 쓰긴했지만
간단하게? 구할 수 있는 것이다 ( 맞죠? )
이 과정을 이제 컴퓨터한테 계산하게 만든다면
페이징 로직을 구현할 수 있게 되는 것이다.
이 구현하는 과정은 직접 해본적은 있으나 다른 분께서 작업하시는 건 본 적이 없어
어떤 구현의 어려운 부분이 있는지 알지못해 팁을 작성하기는 어렵다.
언젠가 누군가 작업하는 것을 보게된다면 팁을 작성해볼 수 있도록 하겠다.
하지만 그 분도 쉽게한다면.. 팁은 없을것같다.
하나하나 가진것과 해야할 것, 어떤 방법을 할지 고민해보시며
즐기실 수 있길 바란다.
아 작성하고 나니 생각났는데,
로직을 사용할땐, 그 로직의 예외와 오류를 예상해보는 것은 중요하다.
이 걸 사실 사람이 하는데 한계가 있기때문에 우선, 코드를 구상해보고 실제 테스트를 다양하게 커쳐보는 것이다.
당시 피수업자의 학원에서 잘못된 로직이 사용되고 있고
해당 로직의 논리적인 오류를 설명했던 점이 있다.
아마 간단하게 소수의 데이터를 다루는 환경에서 구현하는데 목표를 두었거나,
오류를 수강생들이 스스로 찾아 수정하는 것을 기대했을지는 모르겠다.
무튼 해당 로직과 오류를 같이 살펴보는 것도 좋겠다.
이 부분 이였다.
총 페이지의 갯수 = (int)((double)(( 마지막 글번호 ) / ( 페이지당 글 갯수 ) + 0.95))
이런 방식이다. ( 몇개월전 기억에 의존하므로 형변환의 방식이 약간 다를 수 있다. )
아직 수리적인 감이 적다면
어떤 생각이 들지는 모르겠지만,
보자마자 어? 뭐지 싶다면
그 부분을 찾아보자.
우선 이 로직을 이해해보자.
가장 쉬운 방법은 샘플링을 이용하는 것이다.
마지막 글번호 11
페이지당 갯수 10
으로 예시를 대입해보자.
11 / 10 + 0.95 이고
1.1 + 0.95 로 2.05가 된다.
해당 코드는 중간에는 double 타입으로 소숫점까지 구하고
마지막에 int로 형변환을 하기때문에 2.05는 2가 된다.
이 방식은 페이지당 글 갯수 묶음으로 글을 묶어 페이징을 하고 남는 값이 있으면 0.95를 더해 다음 묶음수로
강제로 만들고,
만약 페이지가 글 갯수 묶음으로 나누어 떨어지는 경우 0.95를 더해 다음수가 되지는 않게 하는 방식이다.
글 갯수의 최소 단위는 1개이다. 0.5개짜리 글은 존재하지 않는다. 즉,
페이지당 글 갯수로 나누었을때 나머지는 1이 가장 작다.
그러므로
1 / 페이지당 글 갯수 < 0.05 라면
묶음으로 묶은 후 게시글이 1개가 남는데, 0.95를 더해도 다음 페이지가 나오지 않으니
총 페이지 갯수가 필요한 것보다 적게 된다.
이 부분을 좀 더 풀어 설명하면
총 게시글이 1개일때
1 / 페이지당 글 갯수 + 0.95
를 해도 1페이지가 되지 않는 경우가 될 수 있다는 이야기이고,
이를 부등호로 변환한 것이다.
0.05는 분수로 1/20 이고
페이지당 글 갯수가 20개일때 게시글이 한개라면 1페이지를 만들 수 있다.
1/21은 0.47...
1/22은 0.45...
1/100은 0.01...
로 분모가 커질수록 나누는 조각수가 커지기 때문에 한 조각의 크기는 점점 작아지고
즉, 1/20 이 0.05이기 때문에 0.95로 다음 숫자로 넘어갈 수 있는 최소치가 되고
한 페이지당 글 갯수가 20이 넘어가면 0.95로는 1페이지조차 구할 수 없게된다.
결국 해당 페이징 로직은 페이지 숫자가 커지면 페이징을 할 수 없다는 이야기가 된다.
전하고 싶은 내용은
무언가보면 이해하고, 검증하는 과정이 필요하다. 검증에서 찾지못하는 경우도 많다.
그렇기 때문에 테스트를 하면 좋다.
테스트를 하는 방법은 직접 찍어보는 것이 제일 좋고,
다양한 방법들이 있겠지만
하나씩 자신만의 방법을 찾고 익혀가고 늘려가는 것이 좋다.
나같은 경우는 지금 같은것은 검증하려면
반복문으로 페이지수를 변수로 조절해가며 해당 식이 성립하지 않는 경우를 찾는 법을 할 것 같다.
'알고리즘' 카테고리의 다른 글
3차원 배열, 1차원 배열 같은 데이터라면 속도차이 (0) | 2023.11.06 |
---|---|
퀵정렬 설명과 함께 작성한 코드 (1) | 2023.06.06 |
분수의 덧셈 ( 프로그래머스 문제, 코드 없음 ) (1) | 2023.04.24 |
BOJ3052 - 서로 다른 나머지의 개수 (0) | 2022.01.13 |
StringBuilder, StringBuffer (0) | 2022.01.11 |