페이징 처리는 웹서비스를 만들다보면 직면하는 가장 흔한 개발요건이다. 웹화면에서는 한 페이지에 보여줄 데이터는 한정적이고, 페이지를 나누어 효율적인 방법으로 최적의 조회성능을 내줘야 한다. 오늘 볼 예제는 대량의 데이터는 아니며, 간단히 10건 미만의 데이터를 기준으로 limit 를 이용한 페이징처리 쿼리정도만 정리해본다.
데이터 준비하기
아래는 아스키코드를 문자로 변경하는 함수 chr
를 사용한 예제이다. 단순히 A~H 까지 8개 row 를 가진 샘플 데이터를 만들기 위한 작업이다.
select chr(65);
chr
---
A
A~H까지의 샘플 데이터를 만들기 위해서는 65~72까지의 숫자를 가진 테이블데이터가 필요하다. generate_series
함수를 이용해서 손쉽게 아래 데이터를 만들어준다. 아래 generate_series
함수 사용법 부분은 여기를 눌러 참고한다.
select aa, chr(aa)
from generate_series(65, 72) T(aa)
aa|chr
--+---
65|A
66|B
67|C
68|D
69|E
70|F
71|G
72|H
우선 데이터 row 별로 몇번째 줄인지 index 정보를 추가해야 한다. row_number()
함수를 사용하면 order by
기준에 의해 순서정보가 추가된다. 여기까지하면 어느정도 페이징처리를 위한 정보는 정리된 것이다.
select chr(aa) letter
, row_number() over (order by aa)
from generate_series(65, 72) T(aa)
letter|row_number
------+----------
A | 1
B | 2
C | 3
D | 4
E | 5
F | 6
G | 7
H | 8
페이징 쿼리 만들기
만약에 8개의 컨텐츠가 있고 한 페이지에 3건씩 표시한다고 하면, 원하는 그림은 아래처럼 3페이지로 나누어 표시되어야 할 것이다.
1페이지
-----------------
letter|row_number
------+----------
A | 1
B | 2
C | 3
2페이지
-----------------
letter|row_number
------+----------
D | 4
E | 5
F | 6
3페이지
-----------------
letter|row_number
------+----------
G | 7
H | 8
각 row 마다 몇 페이지에 표시될지 계산해서 page_num
컬럼으로 계산해놓는다. 이 컬럼이 limit
절에 사용되는 건 아니고, 단순 검증용으로 보면 될거 같다.
select chr(aa) letter
, row_number() over (order by aa)
, (row_number() over (order by aa) - 1) / 3 + 1 page_num
from generate_series(65, 72) T(aa)
letter|row_number|page_num
------+----------+--------
A | 1| 1
B | 2| 1
C | 3| 1
D | 4| 2
E | 5| 2
F | 6| 2
G | 7| 3
H | 8| 3
limit
절은 뒤에 offset
이 중요한데, 어느 위치부터 몇건을 제한하여 조회할지를 결정한다. limit 3 offset 0
이면, 맨 첫줄부터 3건까지를 의미한다. offset
은 0 부터 시작하니 유념하자. 이걸 나중에 입력될 변수까지 고려하면 아래 쿼리로 나타낼 수 있다.
-- 1페이지 대상건 조회
select chr(aa) letter
, row_number() over (order by aa)
, (row_number() over (order by aa) - 1) / 3 + 1 page_num
from generate_series(65, 72) T(aa)
-- limit 3 offset 0 과 동일
limit 3 offset (1-1) * 3
-- 출력결과
letter|row_number|page_num
------+----------+--------
A | 1| 1
B | 2| 1
C | 3| 1
2페이지의 데이터는 limit 3 offset 3
로 지정하면 되고, offset
은 0부터 이니, offset 3
은 4번째 row 위치를 의미한다.
-- 2페이지 대상건 조회
select chr(aa) letter
, row_number() over (order by aa)
, (row_number() over (order by aa) - 1) / 3 + 1 page_num
from generate_series(65, 72) T(aa)
-- limit 3 offset 3 과 동일
limit 3 offset (2-1) * 3
-- 출력결과
letter|row_number|page_num
------+----------+--------
D | 4| 2
E | 5| 2
F | 6| 2
3페이지 역시 위의 조건들 감안하면 아래 처럼 쉽게 조회할 수 있다.
-- 3페이지 대상건 조회
select chr(aa) letter
, row_number() over (order by aa)
, (row_number() over (order by aa) - 1) / 3 + 1 page_num
from generate_series(65, 72) T(aa)
-- limit 3 offset 6 과 동일
limit 3 offset (3-1) * 3
-- 출력결과
letter|row_number|page_num
------+----------+--------
G | 7| 3
H | 8| 3
실제건수보다 넘어서는 offset 설정시, sql 에러가 날까? 에러가 발생하지 않으며, 다만 대상건이 없으니 아래와 같이 조회된다.
-- 실제건수보다 넘어서는 offset 설정시
select chr(aa) letter
, row_number() over (order by aa)
, (row_number() over (order by aa) - 1) / 3 + 1 page_num
from generate_series(65, 72) T(aa)
limit 3 offset 10000
-- 출력결과
letter|row_number|page_num
------+----------+--------
여기서 조회되는 쿼리는 postgreSQL
DB 라면, 별도 테이블을 만들지 않고도 바로바로 실행할 수 있는 쿼리이니 본인이 조금씩 조정하면서 페이징쿼리를 차근차근 만들어보면 쉬울 것 같다. 다음에는 대량의 데이터를 기준으로 어느정도 수행시간으로 조회가 되는지, 그것을 보완할 방법 등은 있는지 문서를 찾아보고 포스팅을 해볼까 한다.