13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[AI] pgvector vs Weaviate: RAG를 위한 벡터 DB 선택 가이드

pgvector vs Weaviate: RAG를 위한 벡터 DB 선택 가이드

RAG 애플리케이션을 만들다 보면 결국 부딪히는 질문이 하나 있습니다. pgvector Weaviate 비교를 해보면 도대체 뭐가 더 맞는 선택이냐는 거죠. 저도 홈랩에서 이것저것 붙여 보면서, 처음엔 그냥 PostgreSQL에 확장만 올리면 끝 아닌가 싶었는데요. 실제로 써보니까 운영 방식, 검색 품질 튜닝 포인트, 개발 편의성이 꽤 다르더라고요. 특히 벡터 데이터베이스를 RAG 아키텍처에 넣는 순간, 단순히 저장소 하나 고르는 문제가 아니라 검색 파이프라인 전체 성격이 바뀝니다.

이번 글에서는 실무 관점에서 pgvector와 Weaviate를 비교해보겠습니다. 제품 소개만 나열하는 글 말고, 제가 직접 구성할 때 어떤 기준으로 판단하는지, 어디서 삽질했는지, 그리고 어떤 팀에 어떤 선택이 더 현실적인지까지 풀어보겠습니다. 혹시 지금 LLM 임베딩(벡터 표현) 저장소를 정해야 하는 상황이라면, 이 글이 꽤 빠르게 방향을 잡는 데 도움이 될 겁니다.

pgvector Weaviate 비교가 포함된 RAG 아키텍처 개요 다이어그램

RAG 아키텍처에서 임베딩 생성, 벡터 저장, 검색, 재정렬, LLM 응답 생성까지의 흐름을 한눈에 보여주는 개요 이미지입니다.

1. 왜 벡터 DB 선택이 RAG 성능을 좌우할까

많은 분들이 처음에는 모델부터 고르십니다. 어떤 임베딩 모델을 쓸지, 어떤 LLM을 붙일지부터 보게 되거든요. 근데 실제로는 검색 품질과 운영 복잡도가 먼저 발목을 잡는 경우가 많습니다. 이유는 간단합니다.

  • 문서 chunking(분할) 방식이 검색 결과에 직접 영향을 줍니다.
  • 메타데이터 필터링이 약하면 엉뚱한 문서가 섞입니다.
  • 색인 방식이 맞지 않으면 검색 지연 시간이 튑니다.
  • 운영팀이 익숙하지 않은 저장소를 고르면 장애 대응이 느려집니다.

제가 직접 해보니, RAG는 모델이 다 해주는 구조가 아니더라고요. 오히려 검색기(Retriever)를 얼마나 안정적으로 운영하느냐가 체감 품질을 크게 좌우했습니다. 여기서 pgvector는 기존 PostgreSQL 생태계를 활용한다는 강점이 있고, Weaviate는 아예 벡터 검색 중심으로 설계된 제품이라는 차이가 있습니다.

2. 핵심 개념 정리: pgvector와 Weaviate를 쉽게 말해보면

쉽게 말해 보겠습니다.

pgvector는 PostgreSQL 안에 벡터 기능을 넣는 방식입니다. 이미 PostgreSQL을 쓰고 있다면, 익숙한 테이블과 SQL 위에 벡터 검색을 얹는 느낌입니다. 즉, 관계형 데이터와 임베딩 데이터를 한곳에서 다루기 좋습니다.

Weaviate는 처음부터 벡터 검색을 중심으로 설계된 오픈소스 벡터 DB입니다. 문서 객체, 벡터, 메타데이터, 검색 API가 비교적 자연스럽게 묶여 있습니다. 그래서 애플리케이션 입장에서는 “벡터 검색용 서비스”처럼 접근하기 편하죠.

항목 pgvector Weaviate
기본 성격 PostgreSQL 확장 전용 벡터 데이터베이스
쿼리 방식 SQL 중심 API 중심
메타데이터 활용 관계형 모델과 결합이 쉬움 객체 기반 검색과 필터링이 편함
운영 난이도 DBA 친화적 벡터 검색 기능은 풍부하지만 별도 운영 필요
적합한 상황 기존 PostgreSQL 스택 유지 벡터 검색 중심 서비스 구축

여기서 중요한 포인트가 하나 있습니다. pgvector가 단순하고, Weaviate가 고급형이다 이렇게 이분법으로 보면 틀립니다. 실제로는 데이터 모델과 조직 역량에 따라 유불리가 갈립니다. SQL로 조인 많이 하는 환경에서는 pgvector가 훨씬 편할 수 있고, 하이브리드 검색(키워드+벡터 결합 검색)을 적극적으로 쓰려면 Weaviate 쪽이 더 자연스러운 경우가 있습니다.

3. 어떤 기준으로 골라야 하나: 실무형 비교 체크리스트

제가 프로젝트 초기에 꼭 보는 기준은 아래 정도입니다.

  1. 기존 데이터가 어디에 있나
    이미 PostgreSQL에 사용자, 문서, 권한, 조직 구조가 들어 있다면 pgvector가 꽤 매력적입니다.
  2. 검색 API를 애플리케이션에서 어떻게 다룰 건가
    SQL로 끝내고 싶으면 pgvector가 편하고, 벡터 검색 서비스 자체를 별도 계층으로 두려면 Weaviate가 어울립니다.
  3. 필터링과 스키마 변경이 얼마나 잦은가
    RAG는 생각보다 메타데이터 조건이 자주 바뀝니다. 문서 타입, 팀, 권한, 작성일 같은 조건이 붙거든요.
  4. 팀이 무엇에 익숙한가
    이거 진짜 큽니다. 낯선 저장소 하나 더 늘어나는 순간 관제, 백업, 장애 대응도 같이 늘어납니다.

정리하면 이렇습니다.

  • pgvector는 “기존 데이터베이스와 붙어 살기 좋은 선택”입니다.
  • Weaviate는 “벡터 검색을 중심으로 더 빠르게 기능을 확장하기 좋은 선택”입니다.

4. 실전 구현 1: pgvector로 최소 RAG 저장소 만들기

이제 손에 잡히게 구성해보겠습니다. 먼저 pgvector입니다. 저는 로컬 테스트할 때 보통 Docker Compose로 PostgreSQL을 띄우고 확장을 활성화합니다. 복잡하게 시작하면 금방 지치거든요.

4-1. PostgreSQL + pgvector 실행

services:
  postgres:
    image: pgvector/pgvector:pg16
    container_name: rag-postgres
    environment:
      POSTGRES_USER: rag
      POSTGRES_PASSWORD: ragpass
      POSTGRES_DB: ragdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

실행은 간단합니다.

docker compose up -d

4-2. 확장과 테이블 생성

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE documents (
  id BIGSERIAL PRIMARY KEY,
  source TEXT NOT NULL,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  metadata JSONB DEFAULT '{}'::jsonb,
  embedding VECTOR(1536)
);

CREATE INDEX idx_documents_metadata ON documents USING GIN (metadata);

여기서 <code>VECTOR(1536) 같은 차원 수는 쓰는 임베딩 모델 차원에 맞춰야 합니다. 저도 처음엔 모델 바꾸고 차원 안 맞아서 에러를 꽤 봤습니다. 이 부분은 진짜 자주 실수합니다.

4-3. 유사도 검색 쿼리

SELECT id, title, source, content
FROM documents
WHERE metadata ->> 'team' = 'platform'
ORDER BY embedding <-> '[0.01, 0.02, 0.03]'::vector
LIMIT 5;

<-> 연산자는 거리 계산에 사용합니다. 실제 서비스에서는 애플리케이션에서 쿼리 임베딩을 만든 뒤 바인딩해서 넣는 방식이 일반적입니다.

4-4. Python으로 적재하기

import json
import psycopg

rows = [
    {
        "source": "runbook-001",
        "title": "PostgreSQL vacuum note",
        "content": "Autovacuum tuning and maintenance checklist",
        "metadata": {"team": "platform", "type": "runbook"},
        "embedding": [0.01, 0.02, 0.03]
    }
]

conn = psycopg.connect("postgresql://rag:ragpass@localhost:5432/ragdb")
with conn, conn.cursor() as cur:
    for row in rows:
        cur.execute(
            """
            INSERT INTO documents (source, title, content, metadata, embedding)
            VALUES (%s, %s, %s, %s, %s)
            """,
            (
                row["source"],
                row["title"],
                row["content"],
                json.dumps(row["metadata"]),
                row["embedding"],
            ),
        )

pgvector 쪽의 장점은 여기서 바로 드러납니다. 기존 서비스가 PostgreSQL에 이미 기대고 있으면, 별도 저장소를 추가하지 않고도 RAG 아키텍처의 검색 계층을 꽤 자연스럽게 넣을 수 있습니다.

pgvector 기반 벡터 데이터베이스 구조와 SQL 검색 흐름 이미지

문서 테이블, 메타데이터 JSONB, 벡터 컬럼, 인덱스, 유사도 검색 SQL 흐름을 보여주는 구성 이미지입니다.

5. 실전 구현 2: Weaviate로 벡터 검색 서비스 구성하기

이번엔 Weaviate입니다. 실제로 써보니까 “아, 이건 벡터 검색을 서비스처럼 다루는 느낌이구나” 싶었습니다. 특히 스키마와 객체 단위 관리가 비교적 직관적이더라고요.

5-1. Weaviate 실행

services:
  weaviate:
    image: semitechnologies/weaviate:latest
    container_name: rag-weaviate
    ports:
      - "8080:8080"
    environment:
      QUERY_DEFAULTS_LIMIT: 10
      AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
      PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
      DEFAULT_VECTORIZER_MODULE: 'none'
      CLUSTER_HOSTNAME: 'node1'
    volumes:
      - weaviate_data:/var/lib/weaviate

volumes:
  weaviate_data:

여기서는 외부 임베딩 모델을 쓴다고 가정하고 DEFAULT_VECTORIZER_MODULE을 none으로 뒀습니다. 직접 임베딩을 넣는 패턴이 RAG에서는 꽤 흔하거든요.

5-2. 컬렉션 성격의 스키마 생성

import weaviate
from weaviate.classes.config import Configure, Property, DataType

client = weaviate.connect_to_local()

client.collections.create(
    name="Document",
    vectorizer_config=Configure.Vectorizer.none(),
    properties=[
        Property(name="source", data_type=DataType.TEXT),
        Property(name="title", data_type=DataType.TEXT),
        Property(name="content", data_type=DataType.TEXT),
        Property(name="team", data_type=DataType.TEXT),
        Property(name="doc_type", data_type=DataType.TEXT),
    ],
)

client.close()

5-3. 데이터 적재와 검색

import weaviate
from weaviate.classes.query import Filter

client = weaviate.connect_to_local()
collection = client.collections.get("Document")

collection.data.insert(
    properties={
        "source": "runbook-001",
        "title": "PostgreSQL vacuum note",
        "content": "Autovacuum tuning and maintenance checklist",
        "team": "platform",
        "doc_type": "runbook",
    },
    vector=[0.01, 0.02, 0.03],
)

response = collection.query.near_vector(
    near_vector=[0.01, 0.02, 0.03],
    limit=5,
    filters=Filter.by_property("team").equal("platform")
)

for obj in response.objects:
    print(obj.properties)

client.close()

Weaviate는 이런 식으로 API와 객체 중심으로 흐름이 잘 잡혀 있습니다. SQL 없이도 검색 로직을 애플리케이션 계층에서 비교적 읽기 좋게 표현할 수 있다는 점이 장점입니다. 그리고 하이브리드 검색이나 스키마 중심 운영을 선호하는 팀이라면 꽤 편합니다.

6. 주의사항과 트러블슈팅: 여기서 많이 막힙니다

이 섹션은 좀 현실적으로 가보겠습니다. 문서만 보면 다 쉬워 보이는데, 실제로는 여기서 시간 많이 씁니다.

6-1. 임베딩 차원 불일치

⚠️ 가장 흔한 실수입니다. 임베딩 모델을 바꿨는데 스키마 차원은 그대로 두는 경우죠. pgvector에서는 아예 삽입 단계에서 막히고, Weaviate에서도 입력 벡터 형식 검증에서 문제가 날 수 있습니다.

  • 해결법: 차원 수를 코드와 스키마에서 한 번만 정의하고 공통 상수로 관리합니다.
  • 팁: 적재 파이프라인 시작 전에 첫 벡터 길이를 검사하세요.

6-2. 필터링 없는 유사도 검색

RAG는 “비슷한 문서”만 찾으면 끝이 아닙니다. 권한, 조직, 문서 타입, 최신성 같은 조건이 붙습니다. 저는 처음에 메타데이터 설계를 대충 했다가 검색은 잘 되는데 엉뚱한 부서 문서가 섞여서 다시 뜯어고쳤습니다.

  • pgvector: JSONB와 일반 컬럼을 같이 써서 필터링 구조를 미리 잡는 게 좋습니다.
  • Weaviate: 속성 설계를 초기에 어느 정도 정리해두면 나중이 편합니다.

6-3. 인덱스 튜닝을 너무 늦게 시작함

작은 데이터셋에서는 다 빨라 보입니다. 근데 문서 수가 늘어나면 얘기가 달라집니다. pgvector는 인덱스 전략을 고민해야 하고, Weaviate도 검색 품질과 응답 시간을 같이 봐야 합니다. 저는 테스트 데이터 500건일 때는 차이를 못 느꼈는데, 규모가 커질수록 접근 방식 차이가 훨씬 선명해졌습니다.

6-4. 운영 관점의 백업/복구

이건 개발 단계에선 잘 안 보입니다. 하지만 서비스에 들어가면 꼭 봐야 합니다.

  • pgvector: PostgreSQL 백업 체계에 그대로 편승하기 좋습니다.
  • Weaviate: 별도 서비스로 다루는 만큼 백업, 모니터링, 복구 절차를 분리해서 생각해야 합니다.

혹시 이런 경험 있으신가요? 검색은 잘 되는데 운영 문서가 없어서 배포를 못 하는 상황이요. 저는 이거 몇 번 겪고 나서부터는 기능보다 운영 체크리스트를 먼저 씁니다.

Weaviate 오픈소스 벡터 DB 구성과 벡터 검색 API 흐름 이미지

Weaviate의 컬렉션 구조와 필터 기반 near vector 검색 흐름을 설명하는 시각 자료입니다.

7. 검증과 결과: 어떤 상황에서 무엇이 더 잘 맞았나

이제 가장 궁금한 부분이죠. 그래서 뭘 고르면 되느냐. 제가 실제로 써보니까 기준은 꽤 분명했습니다.

상황 더 잘 맞는 선택 이유
기존 서비스가 PostgreSQL 중심 pgvector 운영 도구와 데이터 모델을 재사용하기 좋음
문서 검색 서비스를 별도 계층으로 운영 Weaviate 벡터 검색 API 중심 구성이 자연스러움
권한/조인/트랜잭션이 중요 pgvector 관계형 쿼리와 함께 다루기 편함
하이브리드 검색과 검색 기능 확장 우선 Weaviate 벡터 검색 중심 기능 사용성이 좋음
운영팀이 PostgreSQL에 매우 익숙함 pgvector 학습 비용이 낮음
벡터 검색 전용 제품을 명확히 분리하고 싶음 Weaviate 시스템 역할 구분이 선명함

검증 포인트도 같이 보셔야 합니다.

  1. 질문 20~30개를 수동으로 만들어 검색 결과를 비교합니다.
  2. 정답 문서가 상위 몇 개 안에 들어오는지 확인합니다.
  3. 메타데이터 필터가 예상대로 적용되는지 봅니다.
  4. 색인 후 반영 시간과 운영 절차를 기록합니다.

이 과정을 해보면 벡터 DB 선택이 단순 성능 비교가 아니라는 걸 바로 느끼실 겁니다. RAG 아키텍처에서는 검색 정확도, 메타데이터 모델링, 운영 편의성, 팀 역량이 같이 움직입니다.

pgvector Weaviate 비교 결과와 운영 체크리스트 요약 이미지

검색 정확도, 필터링, 운영 복잡도, 확장성 같은 평가 항목을 한 화면에 정리한 결과 검증 이미지입니다.

8. 제 결론: 둘 중 하나가 무조건 정답은 아닙니다

결론은 좀 싱겁게 들릴 수도 있는데요. pgvector Weaviate 비교에서 절대적인 승자는 없습니다. 대신 선택 기준은 분명합니다.

  • pgvector를 추천하는 경우
    이미 PostgreSQL이 핵심 데이터 저장소이고, 애플리케이션 로직도 SQL 중심이며, 운영 복잡도를 최소화하고 싶을 때입니다.
  • Weaviate를 추천하는 경우
    벡터 검색을 서비스 단위로 분리하고 싶고, 검색 기능 자체를 적극적으로 확장할 계획이 있을 때입니다.

제가 직접 해보니, 작은 팀이나 내부 업무용 검색은 pgvector가 정말 현실적이었습니다. 반대로 검색 기능이 제품 핵심이고, 문서 탐색 경험을 계속 개선해야 하는 구조라면 Weaviate가 더 손에 잘 맞더라고요. 결국 중요한 건 “뭘 더 잘하느냐”보다 “우리 팀이 어디서 덜 고생하느냐”입니다. 이거 무시하면 나중에 꼭 삽질합니다.

데이터 구조, 운영 역량, 검색 기능 요구사항에 따라 어떤 선택이 적합한지 요약한 비교 인포그래픽입니다.

9. 정리와 FAQ: 시작은 어떻게 하는 게 좋을까

마무리로 아주 실무적으로 정리해보겠습니다.

9-1. 한 줄 정리

  • 기존 PostgreSQL을 살리고 싶다: pgvector부터 보시면 됩니다.
  • 오픈소스 벡터 DB를 별도 검색 계층으로 쓰고 싶다: Weaviate가 더 자연스러울 수 있습니다.
  • RAG 품질이 안 나온다: DB보다 청킹, 메타데이터, 평가셋부터 점검하세요.

9-2. 자주 묻는 질문

Q. 둘 다 오픈소스 벡터 DB 범주로 봐도 되나요?
엄밀히 보면 pgvector는 PostgreSQL 확장이고, Weaviate는 전용 벡터 데이터베이스에 가깝습니다. 다만 실무에선 둘 다 벡터 검색 저장소 후보로 같이 검토합니다.

Q. 처음 시작하는데 무엇이 더 쉬운가요?
PostgreSQL에 익숙하면 pgvector가 쉽습니다. 검색 전용 API 개념으로 접근하고 싶으면 Weaviate가 더 직관적으로 느껴질 수 있습니다.

Q. 성능은 누가 더 좋나요?
데이터 크기, 인덱스, 필터 조건, 질의 패턴에 따라 달라집니다. 그래서 벤치마크 숫자 한 줄보다, 내 데이터셋으로 직접 평가셋을 돌려보는 게 훨씬 중요합니다.

다음 글에서는 RAG 평가셋 만드는 방법과, 검색 결과를 사람이 검수하기 쉽게 정리하는 방법도 다뤄보려고 합니다. 이전 글에서 청킹 전략을 정리했다면 같이 보시면 흐름이 더 잘 잡히실 겁니다. 여기까지 구성해보시면, 단순한 제품 비교를 넘어서 실제 서비스에 맞는 벡터 데이터베이스 선택 기준이 훨씬 선명해질 겁니다. 드디어 됐다 싶을 때가 오거든요. 그 순간부터 RAG가 좀 재밌어집니다. 🎉