13장. Extension 생태계 — 같은 엔진, 무한한 변신
PostgreSQL의 진짜 강점은 엔진이 아니라 생태계입니다. 같은 바이너리가 extension에 따라 시계열 DB도, 벡터 검색 엔진도, 지리정보 시스템도 됩니다 — 플러그인이 아니라 "코어에 직접 연결되는 일급 시민"이기 때문입니다.
① Extension이 훅(Hook)으로 코어에 연결되는 구조 ② shared_preload_libraries와 CREATE EXTENSION의 차이 ③ 반드시 켜야 할 pg_stat_statements 활성화 흐름 ④ 목적별 대표 extension 지도
13.1 Extension은 어떻게 코어에 "끼어드는가"
PostgreSQL 코어에는 쿼리 처리 경로 곳곳에 훅(Hook)이라는 함수 포인터가 심어져 있습니다. 평소에는 비어 있지만, extension이 자기 함수를 등록하면 모든 쿼리가 그 함수를 통과하게 됩니다. 콘센트(훅)에 플러그(extension)를 꽂는 셈이죠. 이 밖에도 SPI(내부 SQL 실행), FDW(외부 데이터 소스), Index AM(새 인덱스 타입 — pgvector의 HNSW가 이것) 같은 공식 API가 열려 있습니다.
코어의 쿼리 파이프라인(Parser → Planner → Executor) 곳곳에 훅이 있습니다. 평소엔 비어 있는 함수 포인터 — 콘센트만 있고 아무것도 안 꽂힌 상태입니다.
서버 시작 시 shared_preload_libraries에 지정된 pg_stat_statements.so가 로드되며 자기 함수를 훅에 등록합니다. 플러그가 꽂혔습니다.
이후 모든 쿼리가 실행을 마칠 때마다 훅에 꽂힌 extension 함수가 호출됩니다. pg_stat_statements는 이 순간 실행 시간과 호출 횟수를 공유 메모리에 누적합니다.
사용자는 pg_stat_statements 뷰로 누적 통계를 조회합니다. 코어 소스는 한 줄도 수정하지 않고 엔진의 동작을 확장했습니다 — 이것이 훅 아키텍처의 힘입니다.
13.2 두 개의 스위치 — preload와 CREATE EXTENSION
Extension 활성화에는 종류에 따라 두 단계의 스위치가 있습니다. 이걸 혼동하면 "설치했는데 왜 안 되지?"의 늪에 빠집니다.
| 스위치 | 시점 | 하는 일 | 대상 |
|---|---|---|---|
shared_preload_libraries | 서버 시작 시 (재시작 필요) | .so를 프로세스에 로드, 훅 등록, 공유 메모리 확보 | pg_stat_statements, auto_explain, pgaudit, pg_cron 등 |
CREATE EXTENSION | 언제든 (DB 단위) | SQL 객체(뷰·함수·타입·연산자) 생성 | 모든 extension — 단 DB마다 따로 실행 |
# ① postgresql.conf — 프로세스 레벨 (재시작 필요)
shared_preload_libraries = 'pg_stat_statements,auto_explain'
pg_stat_statements.max = 10000
pg_stat_statements.track = all
-- ② 재시작 후, 사용할 DB에서 — SQL 객체 생성
CREATE EXTENSION pg_stat_statements;
-- 확인: 설치 가능/설치된 extension
SELECT name, default_version, installed_version
FROM pg_available_extensions ORDER BY name;
① postgresql.conf의 shared_preload_libraries에 extension을 추가합니다. 이 설정만으로는 아직 아무 일도 일어나지 않습니다.
② 서버를 재시작하면 .so가 모든 프로세스에 로드되고 훅이 등록됩니다. 이때부터 통계 수집은 시작되지만, 조회할 SQL 객체가 아직 없습니다.
③ 대상 DB에서 CREATE EXTENSION pg_stat_statements;를 실행하면 뷰와 함수가 생성됩니다. extension은 DB 단위라 사용할 DB마다 실행해야 합니다.
④ 이제 pg_stat_statements 뷰에서 "가장 무거운 쿼리 TOP 20"을 한 번에 뽑을 수 있습니다. 운영 DB라면 설치하지 않을 이유가 없는 필수 extension입니다.
preload가 필요한 extension(pg_stat_statements, auto_explain, pgaudit, pg_cron)을 shared_preload_libraries에 넣지 않았거나 재시작을 빠뜨린 경우입니다. 반대로 pg_trgm, postgis, pgvector 등은 preload 없이 CREATE EXTENSION만으로 동작합니다.
13.3 대표 Extension 지도 — 목적별 카드
📊 pg_stat_statements
쿼리를 정규화해 실행 횟수·시간·버퍼 히트를 누적. 운영 환경 1순위 설치. preload 필요.
🔍 auto_explain
임계값(예: 1초) 이상 걸린 쿼리의 실행 계획을 로그에 자동 기록. 재현 안 되는 장애의 결정적 증거. preload 필요.
🛡️ pgaudit
누가 언제 어떤 SQL을 실행했는지 감사 로그. 금융·공공·의료 규제 대응 필수. preload 필요.
🔤 pg_trgm
LIKE '%foo%'와 오타 허용 검색을 GIN 인덱스로 가속. similarity() 유사도 계산.
🌍 PostGIS
지리정보 표준. 좌표계 변환, 공간 조인, 반경 검색(ST_DWithin), KNN까지.
🧠 pgvector
LLM 임베딩 유사도 검색. v0.5+의 HNSW 인덱스로 실전 수준 속도. <=> 코사인 거리 연산자.
⏰ pg_cron
DB 안에서 cron 문법으로 SQL 스케줄링. 파티션 생성·VACUUM 등 유지보수 자동화. preload 필요.
📅 pg_partman
12장의 파티션 생성/삭제 로테이션을 자동화. pg_cron과 짝으로 쓰는 것이 표준.
📈 TimescaleDB
시계열 특화 — hypertable 자동 파티셔닝, 압축, 연속 집계. 라이선스(Apache 2.0/TSL) 확인 필수.
🌐 Citus
테이블을 여러 워커 노드로 분산(샤딩)해 스케일 아웃. 멀티테넌트 SaaS에 적합.
🚪 pgBouncer
(extension은 아니지만) 커넥션 풀링의 사실상 표준. transaction 모드가 일반적 선택. 14장 참조.
🔧 pg_repack
VACUUM FULL 없이 무중단으로 테이블 재구성 — Bloat 제거의 운영 표준. 8장 참조.
실무에서 자주 쓰는 조합을 외워두면 아키텍처 결정이 빨라집니다.
| 목적 | 추천 조합 |
|---|---|
| 기본 모니터링 | pg_stat_statements + auto_explain |
| 대량 데이터 관리 | pg_partman + pg_cron |
| 전문/유사 검색 | pg_trgm + btree_gin |
| AI/임베딩 | pgvector + pg_trgm (하이브리드 검색) |
| 규제 대응 | pgaudit + pgcrypto |
13.4 손으로 익히기 — pg_trgm과 pgvector 맛보기
-- pg_trgm: LIKE '%…%'를 인덱스 스캔으로
CREATE EXTENSION pg_trgm;
CREATE INDEX ON users USING GIN (email gin_trgm_ops);
SELECT similarity('postgres', 'postgrez'); -- 0.625 (오타 유사도)
SELECT * FROM users WHERE email LIKE '%@danble.co%'; -- 이제 인덱스 사용!
-- pgvector: 임베딩 유사도 top-5
CREATE EXTENSION vector;
CREATE TABLE docs (id bigserial, content text, embedding vector(1536));
CREATE INDEX ON docs USING HNSW (embedding vector_cosine_ops);
SELECT id, 1 - (embedding <=> :q) AS similarity
FROM docs ORDER BY embedding <=> :q LIMIT 5;
pg_upgrade로 PostgreSQL을 올릴 때는 source에 설치된 모든 extension의 동일 버전이 target에도 준비되어 있어야 합니다. PostGIS·TimescaleDB는 자체 업그레이드 절차가 복잡하니 반드시 해당 프로젝트 가이드를 따르세요. shared_preload_libraries도 새 서버 conf에 별도로 옮겨야 합니다.
✍️ 이해도 체크
CREATE EXTENSION pg_stat_statements;를 실행했는데 뷰에 통계가 쌓이지 않습니다. 가장 유력한 원인은?이 장의 원문 문서: chapters/ch13_extensions.md — pgaudit 상세 설정, PostGIS geography vs geometry, pgvector 인덱스 파라미터, Citus 분산 테이블, pgBouncer 모드별 함정이 담겨 있습니다.