PostgreSQL 5분 소요

큐도 캐시도 검색도 벡터DB도 전부 Postgres 하나로? '그냥 Postgres 써' 논쟁이 다시 뜨거워진 이유

개발자 커뮤니티에서 몇 년째 주기적으로 다시 불붙는 논쟁이 하나 있습니다. “그냥 Postgres 써(Just use Postgres)“라는 한 줄짜리 주장인데요. 큐도, 캐시도, 전문 검색도, 심지어 AI 시대의 필수품이 된 벡터 검색까지 전부 Postgres 하나로 처리하자는 이야기입니다. 요즘 이 논쟁이 다시 달아오른 데는 클라우드 비용 압박과 AI 스택이 빠르게 불어난 사정이 겹쳐 있습니다.

먼저 솔직히 말씀드리면, 이번 글은 최근 30일 내의 신규 커뮤니티 스레드 데이터가 거의 잡히지 않았습니다. 그래서 특정 스레드의 추천수나 댓글수를 인용하는 대신 이 논쟁의 구조와 양쪽 진영의 핵심 논거를 정리하는 쪽으로 씁니다. 어차피 특정 시점의 이벤트라기보다 수년째 이어지는 상시 논쟁이라 구조를 알아두는 편이 더 쓸모 있기도 합니다.

왜 하필 Postgres가 ‘만능 후보’가 됐을까

출발점은 Postgres의 확장 구조입니다. Postgres는 처음부터 확장 가능한 데이터베이스로 설계됐습니다. 사용자 정의 타입, 사용자 정의 인덱스 접근 방식, 확장(extension) 시스템을 코어가 공식적으로 지원합니다. 쉽게 말해 새로운 데이터 종류나 새로운 검색 방식이 등장해도 DB를 갈아엎지 않고 플러그인처럼 얹으면 된다는 뜻입니다.

실제 사례를 보면 이해가 빠릅니다. 벡터 검색이 필요해졌을 때 Postgres 진영은 새 데이터베이스를 만들지 않았습니다. pgvector라는 확장을 붙였습니다. 그걸로 끝이었습니다. 지리 정보가 필요하면 PostGIS, 시계열 데이터가 필요하면 TimescaleDB, 전문 검색은 코어에 내장된 tsvector와 GIN 인덱스가 처리합니다. 큐는 SELECT ... FOR UPDATE SKIP LOCKED 한 줄이면 동시성 안전한 작업 큐가 됩니다. 이 SKIP LOCKED 구문은 여러 워커가 같은 작업을 중복해서 집어가는 문제를 DB 레벨에서 막아줍니다. 큐 전용 미들웨어 없이도 꽤 견고한 큐를 짤 수 있게 된 게 이 한 줄 덕분입니다.

여기에 라이선스 문제도 한몫했습니다. Redis와 Elasticsearch가 각각 라이선스를 바꾸면서 커뮤니티가 홍역을 치렀던 걸 기억하실 겁니다. Postgres는 PostgreSQL 라이선스라는 아주 관대한 오픈소스 라이선스를 쓰고 특정 회사가 소유하지도 않습니다. 인프라를 고르는 입장에서 라이선스 리스크가 없다는 건 생각보다 크게 작용합니다.

‘그냥 Postgres’ 진영의 진짜 논거는 성능이 아니다

이 주장을 오해한 사람들이 흔히 내놓는 반박이 있습니다. “Postgres 큐가 Kafka보다 빠를 리가 없잖아요.” 맞습니다. 안 빠릅니다. 그런데 이 진영이 내세우는 건 애초에 성능이 아닙니다.

핵심은 운영 복잡도입니다. 서비스 하나를 굴리는데 Postgres, Redis, Kafka, Elasticsearch, Pinecone를 다 쓴다고 해봅시다. 각각 별도의 백업 전략, 별도의 모니터링, 별도의 장애 대응 플레이북, 별도의 버전 업그레이드 주기가 필요합니다. 접속 정보도 다섯 벌이고, 보안 패치 추적도 다섯 갈래입니다. 팀이 3명인데 인프라 컴포넌트가 5개면 사실상 아무도 전체를 제대로 모르는 상태가 됩니다.

트랜잭션 일관성도 빼놓을 수 없습니다. 주문을 저장하면서 동시에 “결제 처리” 작업을 큐에 넣어야 한다고 해봅시다. DB는 Postgres, 큐는 별도 시스템이면 이 둘 사이에 원자성이 없습니다. 주문은 저장됐는데 큐 삽입이 실패하거나, 반대로 큐에는 들어갔는데 주문 커밋이 롤백되는 상황이 생깁니다. 이걸 해결하려고 아웃박스 패턴이니 사가 패턴이니 하는 걸 도입하는데, 큐가 같은 Postgres 안의 테이블이라면 그냥 같은 트랜잭션에 넣으면 끝입니다. 문제 자체가 사라집니다.

돈 문제도 있습니다. 관리형 서비스를 컴포넌트마다 하나씩 붙이면 월 청구서가 곱셈으로 늘어납니다. 트래픽이 작은 초기 서비스일수록 이 고정비가 아픕니다.

반대 진영: “그건 스케일을 만나본 적 없는 사람의 말”

반대편 논거도 만만치 않고 대체로 실전 경험에서 나옵니다.

가장 자주 나오는 지적은 워크로드 간섭입니다. Postgres 하나에 OLTP 트래픽, 큐 폴링, 전문 검색, 벡터 유사도 계산을 다 몰아넣으면 서로가 서로를 방해합니다. 벡터 검색은 CPU를 많이 먹고 큐 폴링은 쉬지 않고 락과 I/O를 만들어냅니다. 그 와중에 사용자 로그인 쿼리가 느려지면 서비스 전체 체감 속도가 떨어집니다. 전용 도구를 쓰는 가장 큰 이유는 성능 그 자체보다 격리인 경우가 많습니다.

Postgres 특유의 구조적 약점도 있습니다. Postgres는 MVCC 방식으로 동작하는데, 행을 업데이트하면 기존 행을 지우는 게 아니라 새 버전을 쓰고 옛 버전을 죽은 튜플로 남깁니다. 큐 테이블처럼 삽입과 삭제가 초당 수천 번씩 일어나는 워크로드는 죽은 튜플이 순식간에 쌓입니다. 오토배큠이 못 따라잡으면 테이블이 부풀고 인덱스가 비대해지고 성능이 서서히 무너집니다. 큐 전용 도구는 애초에 이런 패턴을 전제로 설계돼서 이 문제가 없습니다.

기능의 깊이도 다릅니다. Postgres의 전문 검색은 쓸 만하지만 Elasticsearch가 제공하는 형태소 분석기 생태계, 하이라이팅, 패싯 검색, 랭킹 튜닝의 정교함에는 미치지 못합니다. 특히 한국어처럼 형태소 분석이 까다로운 언어에서는 이 격차가 더 벌어집니다. pgvector도 마찬가지입니다. 수억 건 규모의 벡터를 다뤄야 하는 상황이라면 전용 벡터 DB의 인덱스 알고리즘과 샤딩 전략을 이기기 어렵습니다.

실제로 갈리는 지점은 ‘규모’가 아니라 ‘어느 축이 먼저 터지는가’

이 논쟁을 흔히 “작은 서비스는 Postgres, 큰 서비스는 전용 도구"로 정리하는데, 저는 이 프레임이 조금 게으르다고 봅니다. 실제로 따져야 할 건 규모가 아니라 어느 축이 먼저 한계에 부딪히느냐입니다.

큐를 예로 들면, 초당 수백 건 수준의 작업이라면 Postgres 큐로 충분합니다. 그런데 초당 수만 건의 이벤트를 스트리밍하고 여러 컨슈머 그룹이 같은 스트림을 각자 다른 오프셋으로 읽어야 한다면, 그건 Kafka가 풀도록 설계된 문제입니다. Postgres로 흉내 낼 수는 있지만 흉내에 드는 비용이 Kafka 운영 비용을 넘어섭니다.

캐시도 비슷합니다. Postgres의 공유 버퍼와 OS 페이지 캐시는 이미 훌륭한 캐시입니다. 애플리케이션 레벨 캐시를 붙이기 전에 인덱스부터 제대로 잡으면 해결되는 경우가 꽤 많습니다. 하지만 세션 저장소처럼 마이크로초 단위 지연이 중요하고 데이터가 휘발돼도 상관없고 초당 수십만 번 읽히는 워크로드라면 Redis가 맞습니다. 이걸 DB에 넣으면 DB의 커넥션과 CPU를 무의미하게 태우는 셈입니다.

판단 기준을 하나 제안드리면 이렇습니다. “이 컴포넌트를 빼면 다른 게 느려지는가, 아니면 그냥 안 되는가”를 물어보세요. “그냥 안 되는” 경우에만 전용 도구를 도입하는 겁니다. “조금 느려지는” 수준이면 Postgres로 버티는 쪽이 대체로 총비용에서 유리합니다.

그럼 어디서부터 시작해야 할까

현실적인 순서는 이렇습니다. 처음에는 Postgres 하나로 시작합니다. 큐는 SKIP LOCKED 테이블, 검색은 GIN 인덱스, 벡터는 pgvector로 붙입니다. 그리고 각 기능의 인터페이스를 애플리케이션 코드에서 추상화 계층 뒤에 숨겨둡니다. 큐 라이브러리를 직접 호출하지 말고 JobQueue라는 인터페이스를 하나 만들어두라는 이야기입니다.

그러면 나중에 큐가 진짜 병목이 됐을 때 구현체만 갈아끼우면 됩니다. 이 추상화 비용은 처음엔 몇 시간이면 되지만 없으면 나중에 몇 주가 됩니다. “Postgres로 시작하되 탈출구를 미리 뚫어둔다"가 이 논쟁의 가장 실용적인 결론이라고 봅니다.

또 하나 중요한 건 계측입니다. 언제 갈아타야 할지를 감으로 판단하면 늦거나 이릅니다. 큐 대기 시간, 죽은 튜플 비율, 검색 쿼리의 p99 지연 같은 지표를 처음부터 대시보드에 올려두면 “지금이 그 순간이다"를 숫자로 알 수 있습니다.

마무리

“그냥 Postgres 써"는 만능 해법이라기보다 검증되지 않은 복잡도를 미리 사지 말라는 경고에 가깝습니다. 전용 도구는 전용 문제를 만났을 때 값어치를 하고, 문제를 만나기도 전에 들이면 운영 부담만 남습니다. 지금 여러분 스택에서 돌아가는 컴포넌트는 저마다 “그게 없으면 안 되는” 이유를 댈 수 있나요? 대답이 막히는 게 하나라도 있다면 거기서부터 한번 들여다볼 만합니다.

PostgreSQL 데이터베이스 아키텍처 개발자 인프라

댓글

    댓글을 불러오는 중...