보뇨 다이어리

벡터 DB 기초 정리 (백엔드 개발자 관점) 본문

컴퓨터 관련/개발지식 정보

벡터 DB 기초 정리 (백엔드 개발자 관점)

보뇨 2026. 5. 18. 11:36
반응형

일반 RDB는 잘 알지만 벡터 DB는 처음 접하는 백엔드 개발자를 위한 정리


1. 벡터 DB란 무엇인가

1.1 저장되는 데이터

핵심: 벡터(숫자 배열) + 원본 데이터(payload)

{
  "id": "doc_1",
  "vector": [0.024, -0.183, 0.471, ..., 0.092],  // 차원수만큼 (예: 1024개)
  "payload": {
    "title": "K8s 네트워킹",
    "content": "CNI는 컨테이너 네트워크를 ...",
    "category": "infra"
  }
}
  • vector: 임베딩 모델이 텍스트를 받아 만들어낸 숫자 배열
  • payload: 원본 데이터 및 메타데이터 (필터링, 결과 표시용)

1.2 임베딩의 핵심 원리

의미가 비슷한 텍스트는 비슷한 벡터로 변환됨.

"K8s 네트워킹"     → [0.02, -0.18, 0.47, ...]
"쿠버네티스 통신"  → [0.03, -0.19, 0.45, ...]  ← 거의 비슷한 벡터
"오늘 점심 뭐 먹지" → [0.81, 0.32, -0.15, ...]  ← 완전 다른 벡터

1.3 RDB와의 비교

항목 일반 RDB 벡터 DB

데이터 형태 정형 (row/column) 벡터 + payload
쿼리 방식 정확한 매칭 (=, LIKE, JOIN) 유사도 검색 (ANN)
인덱스 B-tree, Hash HNSW, IVF 같은 ANN 인덱스
결과 조건을 만족하는 전체 Top-K (보통 5~20개)
강점 트랜잭션, JOIN, 집계 의미 검색, 유사 항목 찾기
약점 의미 기반 검색 불가 트랜잭션/JOIN 거의 없음

2. 검색 동작 방식

2.1 검색 흐름

1. 사용자 쿼리 입력
   "쿠버네티스 네트워크 어떻게 동작해?"

2. 같은 임베딩 모델로 벡터화
   → [0.025, -0.182, ...]

3. 이 벡터와 거리가 가까운 Top-K개 검색

4. 결과 반환
   [
     { "id": "doc_1", "score": 0.92, "payload": {...} },
     { "id": "doc_7", "score": 0.87, "payload": {...} },
     { "id": "doc_3", "score": 0.81, "payload": {...} }
   ]

2.2 Top-K의 의미

K = 가져올 개수. SQL의 LIMIT과 유사한 개념.

-- RDB 비유
SELECT * FROM documents
ORDER BY similarity DESC
LIMIT 5;  -- Top-5

벡터 검색에서는 거의 항상 Top-K 방식.

  • 모든 문서가 어느 정도 점수를 가짐 (정확 일치 개념이 없음)
  • "조건 만족하는 전체"가 아니라 "가장 가까운 N개"가 자연스러움

참고: Recall@7 같은 지표의 @7은 Top-7으로 검색했을 때 정답 문서가 포함된 비율을 의미.

2.3 거리 계산 방법

각 항목값을 ±1씩 시도하는 게 아니라, 수식 한 방으로 계산.

코사인 유사도 (가장 흔함)

두 벡터가 이루는 각도를 측정.

쿼리 벡터 Q = [0.2, 0.5, 0.1]
문서 벡터 A = [0.3, 0.4, 0.2]
문서 벡터 B = [-0.8, 0.1, 0.6]

코사인 유사도 = (Q · A) / (|Q| × |A|)
             = 내적 / (각 벡터의 길이 곱)

Q vs A → 0.95 (방향이 비슷함, 가까움)
Q vs B → 0.12 (방향이 다름, 멈)

주요 거리 측정 방식

방식 설명 언제 씀

Cosine 각도 기반 텍스트 임베딩 (가장 흔함)
Dot Product 내적 정규화된 벡터 (빠름)
Euclidean (L2) 직선 거리 이미지 임베딩 등

정규화된 벡터(bge, OpenAI 등)에서는 Cosine = Dot Product라 Dot가 더 빠름.

2.4 Top-K를 빠르게 뽑는 방법: ANN

Brute Force (전수 검색)

모든 벡터와 거리 계산 → 정확하지만 느림 (1억 개면 수 초).

ANN (Approximate Nearest Neighbor)

정확도를 약간 포기하고 속도를 얻음. 실제 운영에서 사용하는 방식.

HNSW (Hierarchical Navigable Small World) - 가장 보편적

Layer 2 (성김):     A ─────────── E
                    │             │
Layer 1 (중간):     A ─── C ───── E ─── G
                    │     │       │     │
Layer 0 (촘촘):   A-B-C-D-E-F-G-H-I-J-K-L  ← 모든 벡터

비유:

"서울역에서 가장 가까운 카페 5개" 찾기
1. "서울 어디?" (대륙 단위)
2. "중구 근처?" (도시 단위)
3. "서울역 근처?" (동네 단위)
4. 그 동네에서만 정밀 비교

→ 1억 개에서도 수 ms 안에 검색 가능.

주요 인덱스 알고리즘

알고리즘 특징 지원 DB

HNSW 그래프 기반, 빠르고 정확, 메모리 많이 씀 Qdrant, Milvus, pgvector, Weaviate
IVF 클러스터링 기반, 메모리 효율 Milvus, FAISS, pgvector
IVF-PQ IVF + 양자화 (벡터 압축) Milvus, FAISS
DiskANN 디스크 기반, 초대용량 Milvus, Azure

대부분 HNSW가 기본값. 수억 건 이상부터 IVF-PQ 등 압축 방식 고려.


3. 임베딩 모델과 벡터 DB의 분리

[임베딩 모델]              [벡터 DB]
텍스트 → 벡터 생성    →    벡터 저장 + 검색
(bge-m3, OpenAI 등)        (Qdrant, Milvus 등)
  • 임베딩 모델: 모델마다 많이 다름
  • 벡터 DB 검색 방식: 대체로 표준화되어 있음

3.1 임베딩 모델의 차이점

차원 수 차이

모델 차원

OpenAI text-embedding-3-small 1536
OpenAI text-embedding-3-large 3072
bge-m3 1024
bge-small-en 384
Cohere embed-v3 1024

차원이 다르면 DB 스키마 자체가 달라짐. 모델 바꾸면 컬렉션 재생성 필요.

의미 공간이 다름 (중요)

같은 문장이라도 모델마다 완전히 다른 벡터가 나옴.

"K8s 네트워킹"

OpenAI:  [0.024, -0.183, 0.471, ..., 0.092]   (1536차원)
bge-m3:  [-0.512, 0.087, -0.231, ..., 0.405]  (1024차원)

임베딩 모델을 섞어 쓰면 안 됨:

❌ 문서는 OpenAI로 임베딩 → 검색 쿼리는 bge-m3
   → 의미 공간이 달라서 거리 계산이 무의미

✅ 문서도 OpenAI, 쿼리도 OpenAI
✅ 문서도 bge-m3, 쿼리도 bge-m3

정규화 여부

  • 정규화된 벡터: 길이가 1 → 내적이 곧 코사인 유사도라 빠름
  • 정규화 안 된 벡터: 코사인 쓰려면 길이 계산 추가 필요

bge 계열, OpenAI는 정규화 출력. 일부 오픈소스 모델은 직접 정규화 필요.

특수 임베딩 형태 (bge-m3 예시)

타입 형태 특징

Dense [0.02, -0.18, ...] 1024개 일반 의미 임베딩
Sparse {30522: 0.5, 1024: 0.3, ...} BM25처럼 키워드 기반
ColBERT (multi-vector) 토큰마다 벡터 (N × 1024) 정밀 매칭, 무거움

3.2 벡터 DB의 표준화

거리 계산 방식과 인덱스 알고리즘은 DB가 달라도 대체로 동일.

주요 벡터 DB 비교

DB 특징

Qdrant Rust, 단순한 API, payload 필터링 강함
Milvus 대규모/분산, K8s 친화적
pgvector PostgreSQL 확장, 기존 RDB와 함께
Weaviate GraphQL, 모듈식
Elasticsearch 검색엔진 + 벡터, BM25 통합 쉬움

DB 선택은 인덱스 알고리즘보다 **운영 환경(K8s, 규모, 기존 스택)**에 좌우됨.


4. 언제 벡터 DB를 쓰는가

4.1 벡터 DB가 적합한 경우

  • RAG (Retrieval-Augmented Generation): LLM에 컨텍스트로 넣을 문서 검색
  • 추천 시스템: "이 상품과 비슷한 거"
  • 이미지/음성 검색: 의미적으로 유사한 멀티미디어
  • 중복 탐지, 표절 검사: 표현이 달라도 유사한 내용 찾기
  • 이상 탐지: 정상 패턴 벡터와 멀리 떨어진 데이터

4.2 일반 DB를 써야 하는 경우

  • 사용자, 주문, 결제 같은 트랜잭션 데이터
  • 정확한 조건 검색이 필요한 경우
  • 집계, 통계, 리포팅

4.3 실무 아키텍처: 둘 다 같이 사용

[원본 문서]
    ├→ RDB / Object Storage: 원본 보관, 메타데이터, 권한
    └→ Vector DB: 청크별 임베딩 + 검색용 payload

검색 시:
    1. 사용자 쿼리 → 벡터화
    2. Vector DB에서 Top-K 청크 검색
    3. (필요하면) RDB에서 원본/권한 확인
    4. LLM에 컨텍스트로 전달

하이브리드 검색: BM25(키워드 매칭) + 벡터 검색(의미 매칭)을 RRF 등으로 결합해 정확도 향상.


5. 실무 의사결정 포인트

  1. 임베딩 모델 선택이 가장 중요 — 검색 품질의 대부분이 여기서 결정됨
  2. 벡터 DB는 운영 환경에 맞춰 선택 — K8s 환경, 데이터 규모, 기존 스택 고려
  3. 모델 변경 시 전체 재임베딩 필수 — 운영 비용이 크므로 신중히 선택
  4. 하이브리드 검색 고려 — 의미 검색만으로 부족한 경우 BM25 등과 결합

6. 추가 학습 키워드

  • HNSW 파라미터 튜닝: M, ef_construction, ef_search
  • 양자화 (Quantization): 메모리 절약 기법 (Scalar, Product Quantization)
  • Reranker: 1차 검색 후 정밀 재정렬 (Cross-encoder 기반)
  • 청킹 전략: 문서를 어떻게 분할할지 (fixed-size, semantic, recursive)
  • 임베딩 모델 선택 기준: 도메인/언어/문서 길이/비용
  • MTEB 벤치마크: 임베딩 모델 성능 비교 지표
반응형