SQL 정렬과 페이징 처리 완전 정복: 성능과 UX를 모두 고려한 설계
SQL에서 데이터를 보여주는 방식은 단순히 SELECT로 끝나지 않는다. 사용자가 데이터를 어떤 순서로 보게 될지, 페이지를 넘기며 어떻게 탐색할 수 있을지는 정렬과 페이징 처리에 달려 있다. 이 글에서는 ORDER BY, LIMIT, OFFSET 문법을 넘어서, 정렬과 페이징이 갖는 구조적 의미, 성능 고려사항, 설계 방향까지 이론 중심으로 정리한다.
1. 정렬(ORDER BY)의 본질
1-1. ORDER BY의 역할
ORDER BY는 SELECT 결과를 특정 기준으로 정렬하는 데 사용된다. 데이터는 테이블에 저장될 때 순서가 보장되지 않기 때문에, 사용자가 보기 좋은 순서를 지정해주는 작업이 필수적이다. 예를 들어, 게시판에서는 최신 글이 위에 떠야 하며, 쇼핑몰에서는 가격순, 리뷰순, 인기순 등 다양한 정렬 기준이 존재한다.
1-2. 정렬 기준의 형태
정렬은 크게 다음과 같은 유형으로 구분할 수 있다:
- 단일 컬럼 정렬 (ex. created_at DESC)
- 다중 컬럼 정렬 (ex. category ASC, created_at DESC)
- 표현식 기반 정렬 (ex. CASE, IF, LENGTH 등의 조건문 기반)
- NULL 처리 우선순위 포함 정렬
각 정렬은 결과의 가독성과 정보 탐색 효율에 큰 영향을 준다.
1-3. 정렬이 성능에 미치는 영향
정렬은 항상 비용이 든다. 특히 데이터량이 많고, 인덱스를 활용할 수 없는 컬럼일수록 정렬에 시간이 오래 걸린다.
따라서 정렬을 적용할 때는 다음을 고려해야 한다:
- 해당 컬럼에 인덱스가 존재하는가
- 정렬 대상 데이터량이 수십만 건 이상인가
- 정렬 후 LIMIT/OFFSET을 병행할 것인가
2. 페이징 처리의 필요성과 기본 구조
2-1. 페이징이 필요한 이유
모든 데이터를 한 번에 다 보여주는 것은 불가능하다. 특히 웹과 앱에서는 한 화면에 10~20건 정도만 보여주고, 페이지를 넘기거나 스크롤로 추가 로딩하는 방식이 일반적이다. 페이징 처리가 없다면 다음 문제가 발생한다:
- 데이터 과다 로딩 → 속도 저하
- 사용자가 원하는 정보를 찾기 어려움
- UI/UX 품질 저하
2-2. 기본 문법 구조
페이징 처리는 LIMIT과 OFFSET 키워드를 이용해 구현한다.
- LIMIT n: 상위 n건만 조회
- OFFSET m: m번째 이후부터 조회
조합하면 다음과 같은 구조가 된다
SELECT ... FROM ... ORDER BY ... LIMIT 10 OFFSET 20;
→ 21번째부터 10건 출력 (즉, 3페이지)
3. OFFSET 기반 페이징의 한계
3-1. OFFSET은 건너뛴 데이터를 무시하는 방식이다
OFFSET은 데이터베이스가 앞에서부터 결과를 계산한 뒤, 지정된 행 수만큼 건너뛰고 나머지를 리턴하는 방식이다.
즉, 1000건을 조회하고 OFFSET 990이라면, 처음 990건은 가져오기만 하고 버리는 연산이 발생한다. 이 구조는 다음과 같은 단점이 있다
- 페이지 수가 커질수록 속도가 급격히 느려짐
- 정렬 대상이 많을 경우 정렬 + OFFSET 비용이 매우 큼
- 페이지 이동 중 데이터가 변경되면 일관성이 깨질 수 있음
3-2. 대안: 커서 기반 페이징(Cursor-based Pagination)
커서 기반 방식은 특정 기준값을 조건으로 삼아 다음 페이지를 구하는 방식이다. 예를 들어 created_at < ? 조건을 사용하면, 이전 페이지 마지막 글의 작성일을 커서로 삼아 그 이후부터 정렬된 데이터만 가져오는 방식이다. 장점은 다음과 같다
- OFFSET 방식보다 훨씬 빠름 (특히 페이지 수가 클수록 차이 큼)
- 정렬 기준 컬럼에 인덱스가 있으면 효율적
- 변경된 데이터에 강건한 구조 (시간 기준일 경우)
단점은
- 앞 페이지로 되돌아가기가 어려움
- 정렬 기준 컬럼이 단일하고 고유해야 함
4. 정렬과 페이징의 실무 전략
4-1. 인덱스와 정렬의 관계
정렬 기준이 되는 컬럼은 가능하면 인덱스를 생성해두는 것이 좋다. 특히 ORDER BY와 WHERE가 함께 쓰일 경우, 복합 인덱스를 고려해야 한다.
예시:
- WHERE user_id = 1 AND ORDER BY created_at DESC
→ (user_id, created_at) 복합 인덱스 고려
4-2. LIMIT + 1 전략
실무에서는 LIMIT보다 1건 더 많이 조회해 다음 페이지 존재 여부를 판단하는 기법이 흔하다.
- LIMIT 11 → 실제로는 10건만 보여주고, 11번째가 있으면 "다음 페이지 있음" 표시
이 방식은 "다음 페이지 없음"일 때 불필요한 요청을 줄여준다.
4-3. 고정 정렬 기준 사용
페이징 중 데이터가 추가/삭제되면, 동일한 OFFSET에서도 다른 결과가 나올 수 있다. 이를 방지하기 위해 정렬 기준은 고정된, 불변의 값으로 설정해야 한다.
예:
- created_at DESC → 안정적
- RAND() 또는 계산식 정렬 → 불안정
5. 정렬/페이징 관련 주의사항 요약
- OFFSET은 단순하지만 대량 데이터에서는 성능 이슈가 있다
- 커서 기반 페이징은 빠르지만 구현이 어렵고 UX 제약이 존재한다
- 정렬 기준은 고유하고 불변성 있는 컬럼을 선택해야 한다
- 정렬 대상 컬럼은 인덱스를 꼭 고려해야 한다
- LIMIT/OFFSET/ORDER BY의 순서를 항상 유지해야 한다
6. 무한스크롤 vs 페이지네이션의 SQL 관점 차이
6-1. 무한 스크롤 방식의 특징
무한 스크롤 방식은 사용자가 스크롤을 내릴 때마다 다음 데이터를 자동으로 로딩하는 UI 패턴이다. 이 방식은 OFFSET 기반보다는 커서 기반 방식에 더 적합하다. 특히 모바일 환경이나 SNS 피드처럼 "끝이 없는 콘텐츠"를 구성할 때, OFFSET 기반으로는 성능이 한계에 도달한다. 따라서 다음 조건을 만족하도록 설계하는 것이 좋다:
- 정렬 기준은 항상 created_at DESC 또는 id DESC와 같은 고정 기준
- 마지막으로 본 데이터의 고유 값을 커서로 넘겨 조건에 포함
- LIMIT + 1 전략으로 다음 페이지 여부 파악
6-2. 일반 페이지네이션과의 차이
무한 스크롤은 순차 로딩에 최적화되어 있고, 페이지네이션은 사용자가 정해진 위치로 점프할 수 있는 장점이 있다.
SQL 설계 시도 동일한 기준이 적용된다:
- 페이지네이션: OFFSET = (page - 1) * size 구조
- 무한스크롤: WHERE created_at < '이전 커서값' 방식
7. 정렬 기준의 일관성과 불변성 확보
7-1. 불변성 있는 정렬 기준이 중요한 이유
페이징이나 정렬 시, 데이터가 추가/삭제되더라도 결과의 순서가 바뀌지 않아야 UX가 안정적이다. 이를 위해선 정렬 기준으로 변동 가능성이 낮은 컬럼을 선택해야 한다. 예를 들어, 다음과 같은 컬럼은 정렬 기준으로 적합하다
- created_at (등록일이 수정되지 않으면 안정적)
- id (auto_increment 컬럼, 유일하고 순차적)
- published_at (게시 승인일 등)
반면, 다음은 피해야 한다
- RAND()와 같은 랜덤 값
- name, title처럼 중복될 수 있는 컬럼
- 사용자의 정렬 클릭에 따라 조건이 달라지는 경우 → 보조키 필요
7-2. 정렬 기준이 겹칠 경우
같은 created_at 값을 가진 데이터가 다수라면, 보조 정렬 키를 반드시 추가해줘야 순서가 정확해진다.
ORDER BY created_at DESC, id DESC
이런 방식은 정렬의 불확실성을 제거하고, 커서 기반 페이징에서도 중복 없이 정확한 기준이 된다.
8. 데이터 정렬과 페이징 구조의 설계 전략
정렬과 페이징은 단순 쿼리 문제가 아니다. 전체 시스템의 UX 흐름, API 스펙, 화면 동작 방식, 데이터베이스 설계에 모두 영향을 준다.
8-1. 설계 단계에서 고려해야 할 요소
- 결과 순서를 사용자가 조작할 수 있는가?
→ 사용자가 정렬 필드를 선택할 수 있다면, 동적 정렬 처리 로직이 필요하다. - 페이지네이션인가, 무한스크롤인가?
→ 프론트 UI 구조에 따라 SQL 구조가 완전히 달라진다. - 페이지 이동 간 일관성이 중요한가?
→ 정렬 기준의 불변성과 커서 방식 여부를 결정해야 한다. - 데이터가 자주 삽입/삭제되는 테이블인가?
→ OFFSET 기반의 순서 오류를 감안하고 커서 방식도 고려해야 한다.
8-2. 종합 전략 제안
- 데이터가 적고, UX가 단순하면 OFFSET 기반 사용
- 데이터가 많고, UX 성능이 중요한 구조라면 커서 기반 페이징으로 설계
- 정렬 기준은 항상 명확하고 불변적인 컬럼 사용
- 정렬 대상 컬럼은 인덱스 구성 필수
정렬과 페이징은 “어떤 SQL 문법을 쓰는가”가 아니라, “어떤 흐름으로 데이터를 보여줄 것인가”를 먼저 설계하고, 그에 맞는 SQL을 구성하는 것이 바람직하다.