백엔드 데이터 인프라 77편 — Redis Vector Database (HNSW · 코사인 유사도 · RAG)

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 77편. Redis Vector Database — AI 임베딩 유사도 검색. VectorField·HNSW·FLAT 인덱스, COSINE·L2·IP 거리 메트릭, RAG(검색 보강 생성)·시맨틱 검색 패턴, Pinecone·Weaviate·pgvector 와의 비교까지 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 77편 — Redis Vector Database (HNSW · 코사인 유사도 · RAG)

이 글은 백엔드 데이터 인프라 시리즈 130편 중 77편이에요. 76편 까지 Time Series 를 풀었다면, 이번 77편은 가장 최근에 핫해진 영역 — Vector Database. RediSearch(Redis 검색·인덱스 모듈) 의 VectorFieldAI 임베딩 기반 유사도 검색. RAG (검색 보강 생성) 의 핵심 인프라. Part 4-7 모듈의 마지막 글.

Vector Database가 어렵게 느껴지는 이유

"벡터·임베딩·HNSW·코사인 유사도" 같은 ML 용어 가 한꺼번에 등장. 백엔드 개발자에게는 처음 보는 어휘.

첫째, 임베딩이 뭔지 가 안 잡힙니다. "단어·문장·이미지를 숫자 배열로 변환" — 그게 왜 검색에 유용한지 가 직관적이지 않음.

둘째, HNSW·FLAT 알고리즘 이 새 영역. "FLAT 은 brute force, HNSW 는 그래프 기반" — 추상 개념을 알아도 실무 선택 기준 이 명확하지 않음.

셋째, 유사도 메트릭 3가지 (COSINE·L2·IP) 가 언제 어느 걸 쓸지 헷갈림.

이 글에서 임베딩 기초·VectorField 인덱스·HNSW/FLAT 선택·메트릭 3가지·RAG 패턴·전문 Vector DB 와의 비교까지.

임베딩 기초 — 한 줄

임베딩 (embedding) = 텍스트·이미지·오디오를 N차원 숫자 배열로 변환한 것.

예제:

"고양이" → [0.12, 0.34, -0.56, ..., 0.78]    # 768차원
"강아지" → [0.15, 0.31, -0.52, ..., 0.71]    # 비슷한 위치
"자동차" → [-0.42, 0.91, 0.05, ..., -0.23]   # 다른 위치

핵심 — 의미가 비슷한 것은 벡터 공간에서 가까운 위치. 유사도 = 벡터 거리.

생성 모델 예시:

  • 텍스트 — OpenAI text-embedding-3-small (1536차원)
  • 한국어 — KoBERT, KoSimCSE 등
  • 이미지 — CLIP
  • 다국어 — Multilingual-E5

VectorField — Vector 인덱스 생성

from redis.commands.search.field import VectorField, TextField, NumericField
from redis.commands.search.index_definition import IndexDefinition, IndexType

schema = (
    TextField("$.title", as_name="title"),
    NumericField("$.price", as_name="price"),
    VectorField(
        "$.embedding",
        "HNSW",                       # 알고리즘
        {
            "TYPE": "FLOAT32",
            "DIM": 768,                # 차원
            "DISTANCE_METRIC": "COSINE",
            "M": 16,                   # HNSW 파라미터
            "EF_CONSTRUCTION": 200,
        },
        as_name="vector",
    ),
)
definition = IndexDefinition(prefix=["doc:"], index_type=IndexType.JSON)
client.ft("idx:docs").create_index(fields=schema, definition=definition)

핵심 옵션:

  • HNSW vs FLAT — 알고리즘
  • DIM — 임베딩 차원 (모델에 따라)
  • DISTANCE_METRIC — 유사도 메트릭

HNSW vs FLAT — 선택

FLAT — Brute Force

모든 벡터와 exhaustively 거리 계산. 정확하지만 느림.

  • 장점 — 100% 정확한 결과
  • 단점 — O(N) — 100만 벡터에 수 초~수십 초
  • 적합 — 수천~수만 벡터, 정확도 결정적

HNSW (Hierarchical Navigable Small World) — Approximate

그래프 기반 근사 알고리즘. 매우 빠르지만 약간 부정확.

  • 장점 — O(log N) ~ — 수백만 벡터에 수 ms
  • 단점 — 약간 부정확 (recall(검색 적중률) 90~99%)
  • 적합 — 수십만~수억 벡터, 속도 결정적

파라미터:

  • M = 그래프 연결 수 (기본 16) — 클수록 정확도 ↑ + 메모리 ↑
  • EF_CONSTRUCTION = 인덱스 빌드 시 탐색 폭 (기본 200) — 클수록 정확 + 빌드 시간 ↑
  • EF_RUNTIME = 쿼리 시 탐색 폭 — 클수록 정확 + 응답 시간 ↑

대부분 환경 = HNSW + 기본 파라미터.

유사도 메트릭 3가지

COSINE (코사인 유사도)

벡터 사이 각도 기준. 방향만 본다 (크기 무시).

similarity = (A · B) / (||A|| × ||B||)
  • 범위 = -1 ~ 1 (RediSearch 는 거리 로 0~2 변환)
  • 자주 쓰는 자리 = 텍스트 임베딩 (대부분 정규화되어 있음)

L2 (Euclidean Distance)

직선 거리.

distance = sqrt(sum((A_i - B_i)^2))
  • 자주 쓰는 자리 = 이미지 픽셀·정규화 안 된 벡터

IP (Inner Product)

내적 (dot product).

  • 자주 쓰는 자리 = 추천 시스템·임베딩이 정규화 됐을 때만 COSINE 과 동등

선택

99% 환경에서 COSINE. OpenAI·대부분 텍스트 모델이 정규화된 임베딩 반환 → 코사인이 표준.

검색 — KNN 쿼리

KNN(K-Nearest Neighbors, K개 최근접 이웃) 으로 가장 가까운 k 개 벡터를 뽑는다.

from redis.commands.search.query import Query

query_vector = np.array([0.12, 0.34, ...]).astype(np.float32).tobytes()

q = Query("*=>[KNN 5 @vector $vec AS score]") \
    .return_fields("title", "score") \
    .sort_by("score") \
    .dialect(2)

results = client.ft("idx:docs").search(q, query_params={"vec": query_vector})
for doc in results.docs:
    print(doc.title, doc.score)

핵심 syntax — *=>[KNN k @vector $param AS alias]:

  • * = 사전 필터 (모두) — 다른 필드 필터 가능
  • KNN k = top k 유사
  • @vector = vector 필드 이름
  • $param = 바인드된 query 벡터
  • AS alias = 점수 alias

사전 필터 (Hybrid Search)

@price:[100 500] @category:{electronics} =>[KNN 5 @vector $vec AS score]

벡터 검색 전에 일반 필터 적용. 가격대 안에서 유사한 상품 같은 hybrid search.

RAG (Retrieval Augmented Generation) 패턴

LLM 활용의 가장 흔한 패턴.

흐름

1. 문서를 chunk 로 분할 (수백 token 단위)
2. 각 chunk → 임베딩 모델 → 벡터
3. Redis 에 (chunk text + vector) 저장
4. 사용자 질문 → 임베딩 → 벡터
5. Redis 에서 KNN 으로 가장 유사한 chunk 5~10 개 검색
6. 검색된 chunks + 질문 → LLM 프롬프트
7. LLM 이 chunks 기반으로 답변 생성

여기서 chunk 는 문서를 잘게 자른 짧은 텍스트 조각 을 가리킨다.

코드 스케치

# 색인 단계
for chunk in document_chunks:
    embedding = openai.embeddings.create(model="text-embedding-3-small", input=chunk)
    r.json().set(f"doc:{chunk_id}", "$", {
        "text": chunk,
        "embedding": embedding.data[0].embedding,
    })

# 질의 단계
question = "Redis Vector 가 뭔가요?"
q_embed = openai.embeddings.create(model="text-embedding-3-small", input=question)
q_bytes = np.array(q_embed.data[0].embedding, dtype=np.float32).tobytes()

results = client.ft("idx:docs").search(
    Query("*=>[KNN 5 @vector $vec AS score]").dialect(2),
    {"vec": q_bytes}
)

context = "\n\n".join([d.text for d in results.docs])
answer = openai.chat.completions.create(
    model="gpt-4",
    messages=[
        {"role": "system", "content": f"다음 문서들 기반으로 답: {context}"},
        {"role": "user", "content": question},
    ]
)

Redis Vector vs 전문 Vector DB

항목 Redis Pinecone Weaviate pgvector Milvus
호스팅 모델 self-host SaaS only self-host / SaaS self-host self-host
지연 시간 μs ms ms ms ms
메모리 모델 인메모리 관리형 디스크 디스크 (PG) 디스크
데이터 크기 메모리 한계 수십억 벡터 수십억 수백만 수십억
Hybrid Search ◯ (RediSearch 통합) ◯ (SQL)
메타데이터 + 필터 ◯ (SQL JOIN)
운영 복잡도 낮음 매우 낮음 (SaaS) 중간 낮음 (PG 있으면) 높음
비용 (대규모) 메모리 비용 큼 사용량 과금 자체 호스팅 PG 비용 자체 호스팅

선택 가이드

  • 이미 Redis 있음 + 수십만~수백만 벡터 + μs 응답Redis Vector
  • 수억 벡터 + 관리형 SaaSPinecone
  • 이미 PG 있음 + 수백만 벡터 + SQL 결합 필요pgvector
  • 수십억 벡터 + 자체 호스팅Milvus / Weaviate

대부분의 중소 RAG 응용Redis 또는 pgvector 로 충분. 진짜 거대 벡터 인덱스 만 전문 Vector DB.

Spring 통합

Spring AI 의 Vector Store 추상화에 Redis 포함:

@Configuration
public class VectorConfig {
    @Bean
    public VectorStore vectorStore(JedisPooled jedisPooled, EmbeddingModel embeddingModel) {
        return RedisVectorStore.builder(jedisPooled, embeddingModel)
            .indexName("idx:docs")
            .prefix("doc:")
            .build();
    }
}
@Autowired
private VectorStore vectorStore;

vectorStore.add(List.of(new Document("Redis 는 ...")));
List<Document> results = vectorStore.similaritySearch("Redis 뭔가요?");

한계·실무 함정

1. 메모리 비용

차원 768 × float32 4byte × 100만 벡터 = 3GB. 거대 벡터 인덱스는 메모리 비용 큼. Pinecone·Weaviate 가 자주 선택되는 이유.

2. 임베딩 모델 비용

OpenAI text-embedding-3-small = $0.02 / 1M tokens. 100만 문서 × 평균 500 tokens = $10. 거대 색인은 비용 고려.

3. recall 트레이드오프

HNSW = 약간 부정확. 정확도 결정적 인 자리는 FLAT 또는 EF_RUNTIME 크게.

4. 임베딩 모델 일관성

색인 시점과 쿼리 시점 같은 모델 사용 필수. 다른 모델 = 완전 다른 벡터 공간 = 무의미한 결과.

5. 사전 필터 vs 사후 필터

*=>[KNN k @vector $vec] 패턴은 사전 필터. 메타데이터 필터를 KNN 결과 후 적용하는 사후 필터 도 가능하지만 결과 수가 줄어듦 — 일반적으로 사전 필터 권장.

시험 직전 한 번 더 — Vector Database 함정 압축 노트

  • 임베딩 = 텍스트·이미지·오디오 → N차원 숫자 배열
  • 의미 비슷 = 벡터 공간 가까운 위치
  • VectorField — RediSearch 안에서 정의 (HNSW·FLAT 알고리즘)
  • HNSW vs FLAT — HNSW = O(log N) 빠른 근사 / FLAT = O(N) 정확
  • HNSW 파라미터 = M (그래프 연결 수)·EF_CONSTRUCTION·EF_RUNTIME
  • 대부분 환경 = HNSW + 기본 파라미터
  • 3가지 메트릭 = COSINE (텍스트, 99%) · L2 (이미지) · IP (정규화 시 COSINE 과 동등)
  • 99% 텍스트 환경 = COSINE
  • 검색 syntax = *=>[KNN k @vector $param AS score]
  • Hybrid Search = @price:[100 500] =>[KNN 5 @vector $vec] 사전 필터
  • RAG 패턴 = chunk 분할 → 임베딩 → 색인 → 질의 임베딩 → KNN → LLM 프롬프트
  • vs 전문 Vector DB — Pinecone(SaaS), Weaviate, pgvector, Milvus
  • Redis Vector = 작은~중간 + μs 응답 + 이미 Redis 있음
  • Pinecone = 수억 벡터 SaaS
  • pgvector = PG 있음 + SQL 결합
  • Milvus/Weaviate = 자체 호스팅 거대
  • Spring AI = VectorStore 추상화 + Redis 통합
  • 함정 — 메모리 비용 크다 (768차원 × float32 × 100만 = 3GB)
  • 함정 — 임베딩 모델 비용
  • 함정 — 색인과 쿼리 같은 모델 필수
  • 함정 — HNSW recall 트레이드오프
  • 함정 — 사전 필터 vs 사후 필터 선택

공식 문서: Redis Vector Search 에서 자세한 사양과 예제를 확인할 수 있어요.

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!