2 min read
PostgreSQL 피드 조회 인덱스 튜닝
복합 인덱스와 실행 계획 해석으로 피드 API 지연을 줄이는 쿼리 최적화 가이드

도입
피드 API 성능 이슈는 보통 트래픽 증가보다 쿼리 패턴 변화에서 먼저 나타난다. 기능이 추가되면서 정렬 조건과 필터 조건이 조금씩 늘고, 어느 순간 인덱스가 쿼리를 따라가지 못한다. 이때 단순히 인덱스를 더 만드는 방식은 오히려 쓰기 비용만 키운다.
이 글은 "어떤 인덱스를 왜 만들었는지"를 실행 계획으로 검증하는 실무 절차를 정리한다.

문제 정의
피드 조회에서 자주 터지는 병목은 아래 4가지다.
OFFSET기반 페이지네이션으로 뒷페이지 응답 시간이 급증한다.- 다중 필터(
status,published_at,author_id) 조합에서 인덱스 선택이 비효율적이다. - 커버링 인덱스가 없어 heap fetch가 과도하게 발생한다.
- 인덱스 추가 후에도
EXPLAIN ANALYZE검증 없이 배포한다.
핵심은 keyset pagination + 복합 인덱스 + 실제 계획 검증을 한 세트로 보는 것이다.
핵심 개념
| 항목 | 잘못된 접근 | 권장 접근 |
|---|---|---|
| 페이지네이션 | OFFSET n LIMIT 20 | keyset (WHERE (published_at,id) < (...)) |
| 인덱스 설계 | 조건마다 단일 인덱스 추가 | 조회 패턴 기준 복합 인덱스 |
| 검증 | 개발환경 추정치만 확인 | 프로덕션 유사 데이터로 EXPLAIN ANALYZE |
| 모니터링 | 평균 응답 시간만 확인 | p95 + rows scanned + shared hit ratio |
코드 예시 1: 피드 조회용 복합 인덱스 + keyset 쿼리
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_posts_feed
ON posts (status, published_at DESC, id DESC)
INCLUDE (title, author_id, summary);
-- cursor: (published_at, id)
SELECT id, title, author_id, summary, published_at
FROM posts
WHERE status = 'published'
AND (published_at, id) < ($1::timestamptz, $2::bigint)
ORDER BY published_at DESC, id DESC
LIMIT 20;
코드 예시 2: 실행 계획 회귀 체크
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT id, title, author_id, summary, published_at
FROM posts
WHERE status = 'published'
AND (published_at, id) < ('2026-03-01T00:00:00Z', 982341)
ORDER BY published_at DESC, id DESC
LIMIT 20;
-- 목표 예시
-- Index Scan using idx_posts_feed on posts
-- Buffers: shared hit 비율 높음
-- actual time: < 10ms (warm cache)
아키텍처 흐름
Mermaid diagram rendering...
트레이드오프
- keyset pagination은 빠르지만 임의 페이지 점프 UX가 약하다.
INCLUDE인덱스는 조회 성능에 유리하지만 저장 공간을 더 사용한다.- 인덱스가 늘수록 쓰기 비용이 증가하므로 "조회 패턴 우선순위"가 중요하다.
정리
피드 성능 최적화는 인덱스를 많이 만드는 작업이 아니다. 조회 패턴을 기준으로 인덱스를 설계하고, 실행 계획으로 효과를 검증하는 반복 루프다. 이 루프를 CI나 배포 체크리스트에 넣으면 성능 회귀를 사전에 차단할 수 있다.
이미지 출처
- Cover: source link
- License: LGPL / Author: The Oxygen Team , KDE ;
- Note: Wikimedia Commons 무료 라이선스 이미지를 다운로드 후 1600px 기준 JPG로 최적화했습니다.