API
API pagination을 처음 설계할 때부터 고려하면 좋은 이유
처음부터 pagination을 고려해서 API를 만들면, 나중에 대량 데이터를 처리할 때 서버 부하를 줄일 수 있다.
API를 만들 때 많은 개발자가 pagination을 나중에 생각한다. 먼저 기본 기능을 만들고, 데이터가 많아지면 그때 pagination을 추가하자는 식이다. 하지만 처음부터 고려하는 게 훨씬 낫다.
Pagination이 필요한 이유
# pagination 없이 모든 데이터를 가져오면
GET /api/items
# 응답이 10만 개의 아이템을 모두 포함한다
# 메모리: 너무 많은 데이터를 클라이언트에 보냄
# 성능: 네트워크가 느려진다
# 사용자 경험: 브라우저가 먹통이 된다
기본 pagination 구현
// API 엔드포인트
GET /api/items?page=1&limit=20
// 응답 형식
{
data: [...20개의 아이템...],
page: 1,
limit: 20,
total: 10000,
hasNextPage: true
}
요청 파라미터 설계
Pagination 파라미터는 명확하고 일관성 있어야 한다.
# 페이지 기반 (가장 일반적)
GET /api/items?page=2&limit=20
# Offset/Limit 기반
GET /api/items?offset=20&limit=20
# Cursor 기반 (대량 데이터에 더 적합)
GET /api/items?cursor=abc123&limit=20
데이터베이스에서 효율적으로 가져오기
-- ❌ 비효율적
SELECT * FROM items LIMIT 10000 OFFSET 20;
-- 문제: 10020개를 모두 읽고 마지막 20개만 버린다
-- ✅ 효율적 (Cursor 기반)
SELECT * FROM items WHERE id > last_cursor ORDER BY id LIMIT 20;
응답 형식 설계
type PaginatedResponse<T> = {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
hasNextPage: boolean;
hasPrevPage: boolean;
};
};
클라이언트가 다음 페이지가 있는지 여부를 쉽게 판단할 수 있도록 hasNextPage 같은 필드를 포함하는 게 좋다.
정렬과 필터링
Pagination과 함께 정렬과 필터링도 고려해야 한다.
# 정렬
GET /api/items?page=1&limit=20&sort=createdAt&order=desc
# 필터링
GET /api/items?page=1&limit=20&status=active&category=tech
클라이언트에서 테스트하기
# 1페이지 확인
curl -i 'https://example.com/api/items?page=1&limit=5'
# 2페이지 확인
curl -i 'https://example.com/api/items?page=2&limit=5'
# 전체 데이터 수와 일치하는지 확인
# total = 10이면 2페이지만 있어야 한다
흔한 실수들
- limit의 상한선 없음: 클라이언트가 limit=999999를 요청할 수 있으면, 서버가 뻗을 수 있다
- offset의 효율성 무시: 대량 데이터에는 offset 대신 cursor를 써야 한다
- 정렬 기준 불명확: 같은 값을 가진 여러 행이 있을 때 순서가 불안정해진다
- 캐시 무효화: 페이지 기반 pagination에서 새 데이터가 추가되면 페이지 번호가 바뀔 수 있다
Pagination은 단순해 보이지만, 제대로 설계하면 서버 부하를 크게 줄이고 사용자 경험을 개선할 수 있다. 처음부터 고려하는 것이 나중의 리팩토링을 막는다.