MMeshOps
바이브코딩을 위한 완벽한 방어선: SQL과 PostgreSQL 기초
6예상 8입문

수백만 건의 데이터를 끊어서 가져오기

AI 코드가 자주 실수하는 비효율적인 조회를 막기 위해, LIMIT와 OFFSET을 이용해 데이터베이스 단에서 데이터를 끊어오는 페이지네이션을 배웁니다.

게시판의 1페이지를 눌렀는데 글 10개가 아니라 1만 개의 글이 한꺼번에 쏟아져 내려와서 스마트폰 브라우저가 멈춰버린 적이 있나요? 바이브코딩 초보 시절에 AI가 짜준 코드를 그대로 운영 서버에 올렸을 때 가장 흔하게 발생하는 대참사입니다. 데이터가 적을 때는 완벽해 보이지만, 서비스가 흥행하여 데이터가 수십만 건으로 늘어나는 순간 서버와 앱은 데이터를 감당하지 못하고 죽어버립니다. 이번 시간에는 방대한 데이터를 한 입 크기로 예쁘게 잘라서 가져오는 필수 기술, 페이지네이션(Pagination)을 위한 `LIMIT`와 `OFFSET`을 배웁니다.

바이브코딩 AI의 끔찍한 함정

AI에게 "게시판 목록 보여주는 API 만들어줘"라고 지시하면, 열에 아홉은 무책임하게 다음과 같은 형태의 쿼리를 짭니다.

SELECT title, author, created_at 
FROM posts 
ORDER BY created_at DESC;

이 코드는 개발 중 데이터가 10개밖에 없을 때는 아무런 문제 없이 0.001초 만에 최신순 목록을 보여줍니다. 그러나 서비스가 대박이 나서 글이 100만 개가 되었다고 상상해 봅시다. 위 쿼리는 100만 개의 글을 전부 다 데이터베이스에서 퍼올린 뒤, 백엔드 서버의 메모리에 100만 개를 욱여넣고, 그제야 프론트엔드 화면으로 전송을 시도합니다. 서버의 메모리는 터져버리고(Out Of Memory), 데이터베이스는 과부하에 걸리며, 사용자는 하얀색 로딩 화면만 보게 됩니다.

10개씩만 주세요: `LIMIT`

이 참사를 막는 가장 직관적이고 강력한 무기가 바로 `LIMIT` 구문입니다. SQL 문장의 맨 꼬리에 붙어서 데이터베이스에게 "다 찾을 필요 없고, 위에서부터 딱 N개만 찾고 바로 멈춰!"라고 지시합니다.

LIMIT 구문을 추가한 안전한 쿼리

SELECT title, author, created_at 
FROM posts 
ORDER BY created_at DESC 
LIMIT 10;

이제 데이터베이스는 글이 100만 개든 1,000만 개든 상관없이, 최신순으로 정렬하다가 위에서 10개를 채우는 순간 검색을 즉시 중단하고 10개만 서버로 사뿐히 돌려보냅니다.

따라서 바이브코딩 시에는 AI에게 언제나 "목록 조회 쿼리에는 반드시 페이지네이션(LIMIT)을 기본으로 적용해서 10개나 20개씩 끊어서 내려줘"라고 강력하게 요구해야 합니다.

다음 페이지로 넘어가기: `OFFSET`

`LIMIT 10`으로 첫 번째 페이지(1~10번째 글)를 띄웠습니다. 유저가 스크롤을 내려 '2페이지' 버튼을 누르거나 무한 스크롤이 발동했습니다. 두 번째 페이지(11~20번째 글)는 어떻게 가져올까요?

이때 사용하는 것이 `OFFSET`입니다. `OFFSET`은 "앞에서부터 N개는 이미 봤으니 건너뛰고(버리고), 그다음부터 줘"라는 의미입니다.

-- 1페이지 (처음부터 10개 가져오기)
SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 0;

-- 2페이지 (앞에 10개는 버리고 11번째부터 10개 가져오기)
SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 10;

-- 3페이지 (앞에 20개는 버리고 21번째부터 10개 가져오기)
SELECT * FROM posts ORDER BY id DESC LIMIT 10 OFFSET 20;

이렇게 `LIMIT`와 `OFFSET`의 조합만으로 우리가 흔히 보는 게시판의 1페이지, 2페이지, 3페이지 이동 버튼 기능을 완벽하게 구현할 수 있습니다. 이것을 오프셋 기반 페이지네이션(Offset Pagination)이라고 부릅니다.

OFFSET의 한계와 실무 팁

오해 또는 실수

OFFSET이 커질수록 속도가 급격히 느려집니다

바로잡기

OFFSET 1000000 LIMIT 10 을 요청하면, DB는 100만 10개를 다 읽은 뒤 앞에 100만 개를 버리고 10개를 줍니다. 데이터가 수백만 개 단위로 넘어가는 대형 서비스의 뒷 페이지에서는 작동이 멈출 정도로 느려집니다.

오해 또는 실수

대형 서비스의 대안: 커서(Cursor) 기반 페이지네이션

바로잡기

인스타그램이나 유튜브처럼 무한 스크롤을 끝없이 제공하는 대형 서비스는 OFFSET 대신 '마지막으로 본 글의 ID가 50번이니, 그보다 작은 49번부터 10개를 가져와'라는 방식의 커서 기반 페이지네이션을 사용합니다. 나중에 서비스가 커지면 AI에게 커서 기반으로 바꿔달라고 요청하세요.

우선은 초기 서비스 구축 시 가장 직관적이고 구현이 쉬운 `LIMIT/OFFSET` 조합만으로도 99%의 과부하 문제를 훌륭하게 막아낼 수 있습니다.

개념 퀴즈

퀴즈 답을 맞춰야 학습 완료가 됩니다.

게시판에서 '인기순(likes DESC)'으로 정렬된 전체 게시글 중, 1페이지(1~20위)는 이미 읽었고 이제 2페이지에 해당하는 21위부터 40위까지의 20개 게시글만 정확히 끊어서 가져오려고 합니다. SQL 문장의 맨 끝에 붙여야 할 올바른 구문은 무엇인가요?

3줄 요약

  1. 1AI가 생성하는 단순 조회 코드에 LIMIT 구문이 없다면 데이터 폭증 시 서버 다운의 원인이 되므로, 목록 조회 시에는 반드시 페이지네이션을 강제해야 합니다.
  2. 2LIMIT 구문은 가져올 데이터의 최대 개수를 제한하고, OFFSET 구문은 앞쪽에서 건너뛸(버릴) 데이터의 개수를 지정하여 특정 페이지의 목록만 정확히 가져옵니다.
  3. 3OFFSET은 숫자가 커질수록(뒷 페이지로 갈수록) 성능이 저하되는 한계가 있어, 초대형 서비스에서는 커서 기반 페이지네이션 방식을 대안으로 사용합니다.