---
term: '벡터 데이터베이스와 임베딩'
seoTitle: '벡터 데이터베이스와 임베딩이란? HNSW, ANN, 도입 기준'
headline: '벡터 데이터베이스와 임베딩이란?'
slug: vector-database-embeddings
category: ai-modern-stack
shortDefinition: '임베딩은 의미를 담은 숫자 벡터이며, 벡터 데이터베이스는 이 벡터를 저장하고 정확한 일치가 아닌 유사도로 검색하는 시스템입니다.'
relatedTerms:
  - retrieval-augmented-generation-rag
  - nosql-vs-sql
  - database-index
  - access-control-lists-acl
contrastsWith:
  - nosql-vs-sql
aboutTerms:
  - 'Embeddings'
  - 'Vector Database'
  - 'HNSW / ANN'
  - 'Cosine Similarity'
faq:
  - question: '임베딩을 쉽게 설명하면 무엇입니까?'
    answer: '모델이 텍스트, 이미지, 오디오 같은 데이터 조각에 부여하는 숫자 목록으로, 의미가 비슷한 것끼리 비슷한 숫자를 갖게 됩니다. 같은 주제를 다룬 두 문서는 고차원 공간에서 서로 가까운 점이 되며, 덕분에 컴퓨터가 "유사함"을 측정할 수 있습니다.'
  - question: '벡터 데이터베이스란 무엇입니까?'
    answer: '임베딩을 저장하고 "저장된 항목 중 이것과 가장 비슷한 것"을 빠르게 찾아 주는 시스템으로, 정확한 일치 조회 대신 근사 최근접 이웃(ANN) 인덱스를 사용합니다. 내구성, 업데이트, 메타데이터 필터링 같은 "데이터베이스" 부분이 단순한 인메모리 인덱스 라이브러리와 구별되는 지점입니다.'
  - question: '유사도 검색은 어떻게 작동합니까?'
    answer: '쿼리를 같은 모델로 임베딩한 다음, 거리 척도로 저장된 벡터와 비교합니다. 코사인(각도만), 내적(각도와 크기), 유클리드(직선 거리)가 대표적입니다. 정규화된 벡터에서는 세 척도의 순위 결과가 같으며, 텍스트 임베딩 모델은 대개 코사인에 맞춰 조정되어 있습니다.'
  - question: '정확한 검색과 근사 검색의 차이는 무엇입니까?'
    answer: '정확한 검색(kNN)은 쿼리를 모든 벡터와 비교합니다. 재현율은 완벽하지만 선형 시간이 걸리며, 약 100만 개 벡터까지는 문제없습니다. 근사 검색(ANN, 보통 HNSW)은 대부분의 비교를 건너뛰어 로그에 가까운 속도를 내며, 재현율을 몇 퍼센트 내주는 대신 쿼리가 몇 자릿수 빨라집니다. 진짜 최근접 이웃을 가끔 놓치는 것은 대개 허용할 만합니다.'
  - question: 'HNSW란 무엇입니까?'
    answer: 'Hierarchical Navigable Small World의 약자로, 프로덕션에서 가장 널리 쓰이는 ANN 인덱스입니다. 여러 층으로 된 근접 그래프를 만들어, 성긴 상위 층은 먼 거리를 건너뛰는 지름길을 제공하고, 촘촘한 최하위 층은 모든 벡터를 담으며, 검색은 거친 층에서 세밀한 층으로 탐욕적으로 내려갑니다. 조정 매개변수로 재현율과 속도, 메모리 사이의 균형을 맞추며, 그래프는 RAM에 상주합니다.'
  - question: '전용 벡터 데이터베이스가 필요합니까?'
    answer: '앱 규모에서는 대개 필요하지 않습니다. pgvector를 쓰는 Postgres나 MongoDB의 벡터 검색처럼 벡터 인덱스를 지원하는 범용 데이터베이스는 노드당 약 100만 개 벡터까지 무리 없이 처리하며, 앱 데이터와 권한 바로 옆에서 동작합니다. 전용 엔진은 수천만 개의 벡터, 매우 높은 쿼리 처리량, 또는 강한 필터링 아래에서의 높은 재현율이 필요할 때 제값을 합니다.'
  - question: '메타데이터 필터링이란 무엇이며 왜 중요합니까?'
    answer: '테넌트, 카테고리, 날짜, 권한 같은 필드로 유사도 검색을 제한하는 것입니다. 사전 필터링은 ANN 검색 전에 후보를 제한하고, 사후 필터링은 검색 후에 결과를 잘라 내므로 요청한 것보다 적은 결과를 조용히 반환할 수 있습니다. 프로덕션 쿼리에는 거의 항상 필터가 붙으며, 그 필터가 권한이라면 필터는 곧 보안 경계입니다.'
  - question: 'RAG 외에 벡터 데이터베이스는 어디에 쓰입니까?'
    answer: '시맨틱 검색, 추천, 유사 중복 탐지, 이상 탐지나 사기 탐지(이상치는 다른 모든 것에서 멀리 떨어진 벡터입니다)에 쓰이며, 이미지와 오디오 유사도 분석, 클러스터링에도 쓰입니다. 검색 증강 생성은 가장 유명한 용도일 뿐이고, 그 밑에 깔린 범용 능력은 유사도 검색입니다.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'HNSW — Malkov & Yashunin (arXiv:1603.09320)'
    url: 'https://arxiv.org/abs/1603.09320'
  - name: 'Efficient Estimation of Word Representations (word2vec, arXiv:1301.3781)'
    url: 'https://arxiv.org/abs/1301.3781'
  - name: 'pgvector — open-source vector search for Postgres'
    url: 'https://github.com/pgvector/pgvector'
  - name: 'MongoDB Vector Search — overview'
    url: 'https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-overview/'
cta:
  title: '데이터 바로 옆의 벡터'
  text: 'Back4app에서 임베딩은 객체의 배열 필드일 뿐입니다. 유사도 검색을 위해 인덱싱되고, 다른 모든 것을 지키는 바로 그 ACL로 필터링되며, 모델 키를 서버 측에 둔 채 Cloud Code에서 생성됩니다.'
  linkText: '무료로 시작하기'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-10-31'
translationKey: vector-database-embeddings
---

**임베딩은 의미를 담은 숫자 벡터이며, 벡터 데이터베이스는 이 벡터를 저장하고 정확한 일치가 아닌 유사도로 검색하는 시스템입니다.** 두 개념은 하나의 능력을 이룹니다. 모델은 텍스트(또는 이미지, 오디오)를 *의미가 비슷하면 가까이 놓이도록* 배치된 숫자 목록으로 바꾸고, 벡터 데이터베이스는 가장 가까운 점을 빠르게 찾습니다. 시맨틱 검색, 추천, [RAG](/glossary/ko/retrieval-augmented-generation-rag/)의 비결은 이것이 전부입니다. 앱 개발자에게 실용적인 핵심은, 임베딩은 자신이 설명하는 데이터 옆에 저장하는 필드 하나일 뿐이라는 점입니다.

## 핵심 요약

| 질문 | 답변 |
| --- | --- |
| 임베딩 | 의미가 비슷하면 → 가까운 점이 되는 벡터 |
| 벡터 데이터베이스 | 벡터를 저장하고 ANN 인덱스로 "이것과 가장 가까운 것"을 찾음 |
| 척도 | 코사인(텍스트에서 일반적) · 내적 · 유클리드 |
| 정확한 검색 vs. ANN | 완벽하지만 선형 vs. 재현율 ~99%, 몇 자릿수 더 빠름 |
| 전용 DB의 필요성 | 대개 벡터 ~100만 개(1M+) 이상이 되기 전까지는 불필요 – 기존 데이터베이스로 충분할 가능성이 큼 |

## 임베딩, 구체적으로

잠시 1,536차원은 잊고 3차원을 떠올려 보겠습니다. 각 단어에 *동물인 정도*, *탈것인 정도*, *작은 정도*를 점수로 매기는 장난감 모델입니다.

```text
                동물     탈것    작음
새끼 고양이     0.95     0.02    0.90
고양이          0.93     0.01    0.60
트럭            0.03     0.96    0.05
오토바이        0.04     0.94    0.55

"새끼 고양이"는 "고양이" 가까이에 있고(둘 다 동물 점수가 높음) "트럭"과는 멀리 떨어져 있습니다.
distance(새끼 고양이, 고양이) → 작음    → 유사함
distance(새끼 고양이, 트럭)   → 큼      → 무관함

실제 모델은 3개가 아니라 수백, 수천 개의 차원을 쓰며,
차원은 사람이 직접 붙인 라벨이 아니라 데이터에서 학습됩니다. 하지만 직관은 정확히 다음과 같습니다:
의미는 기하 구조가 되고, "유사함"은 "가까움"이 됩니다.
```

이 방식이 실제 구조를 담아낸다는 유명한 증거는 벡터 산술입니다. [학습된 단어 임베딩](https://arxiv.org/abs/1301.3781)에서 *king − man + woman*(왕 − 남자 + 여자)은 *queen*(여왕) 근처에 놓입니다. 의미가 좌표로 바뀌면 대수 연산도 할 수 있게 됩니다.

## 유사도 검색의 작동 방식

```mermaid
flowchart LR
  accTitle: 데이터에서 임베딩, 유사도 검색까지
  accDescr: 모델은 각 항목을 벡터로 임베딩해 벡터 인덱스에 저장합니다. 쿼리 시점에는 쿼리를 같은 모델로 임베딩하고, 인덱스는 거리 척도에 따라 가장 가까운 벡터를 반환합니다. 필요하면 권한 같은 메타데이터로 필터링하며, 결과는 유사도 순으로 정렬됩니다.
  D["항목<br/>(텍스트, 이미지, 오디오)"] -->|"임베딩"| VS["인덱스에 저장된 벡터"]
  Q["쿼리"] -->|"같은 모델"| QV["쿼리 벡터"]
  QV --> S{"최근접 탐색<br/>cosine / dot / L2"}
  VS --> S
  S -->|"메타데이터 / ACL로 필터링"| K["유사도 top-k<br/>결과"]
```

세 가지 거리 척도가 주로 쓰입니다. **코사인**은 벡터 사이의 각도를 측정하고(길이는 무시), **내적**은 크기까지 반영하며, **유클리드**는 직선 거리입니다. 혼란을 덜어 주는 핵심은 이것입니다. *정규화된* 벡터에서는 세 척도의 순위 결과가 같고, 텍스트 임베딩 모델은 보통 코사인에 맞춘 정규화된 벡터를 출력하므로, "어떤 척도를 쓸까"라는 질문의 답은 대개 이미 정해져 있습니다.

## 정확한 검색 vs. 근사 검색: ANN 트레이드오프

| | 정확한 검색(kNN) | 근사 검색(ANN) |
| --- | --- | --- |
| 비교 대상 | 모든 벡터 | 영리하게 고른 부분 집합 |
| 재현율 | 100% | ~95–99%, 조정 가능 |
| 속도 | 선형 – O(n) | 로그에 가까움 |
| 적합한 규모 | ~100만 개 벡터까지 | 수천만 개 이상 |
| 비용 | 쿼리당 CPU | 그래프를 위한 RAM |

정확한 검색은 쿼리를 저장된 모든 벡터와 비교합니다. 완벽하며, 약 100만 개 벡터까지는 전혀 문제가 없습니다. 그 이상에서는 가장 널리 쓰이는 근사 인덱스인 [**HNSW**](https://arxiv.org/abs/1603.09320)가 여러 층으로 된 근접 그래프(먼 거리를 건너뛰는 성긴 상위 층, 모든 것을 담는 촘촘한 최하위 층)를 만들고, 거친 층에서 세밀한 층으로 탐욕적으로 내려가며 대부분의 비교를 건너뜁니다. HNSW는 [B-트리의 시맨틱 사촌](/glossary/ko/database-index/)입니다. B-트리가 "같음"과 "작음"을 인덱싱한다면, ANN 인덱스는 "비슷함"을 인덱싱합니다. 경쟁 페이지들이 흐리게 넘기는 솔직한 단서도 있습니다. ANN은 진짜 최근접 이웃을 *놓칠* 수 있고, 조정 매개변수는 재현율과 속도, 메모리를 맞바꾸며, 그래프는 RAM에 상주합니다. 벡터 데이터베이스의 규모 산정이 사실상 메모리에 관한 논의인 이유입니다.

## 벡터 인덱스 vs. 벡터 데이터베이스

용어가 뒤섞이면서 흐려지는 구분입니다. 인메모리 **인덱스 라이브러리**(FAISS 등)는 최근접 이웃을 계산하지만 영속화, 업데이트, 필터링은 하지 않습니다. 재시작하면 다시 인덱싱해야 하고, `WHERE` 절도 없습니다. **벡터 데이터베이스**는 인덱스에 내구성, 증분 업데이트, 메타데이터 필터링을 더한 것입니다. 바로 이 차이 때문에 "그냥 라이브러리를 쓰면 된다"는 방식은 프로덕션에서 실패하고, *벡터 인덱스를 갖춘 기존 데이터베이스*가 정답인 경우가 많습니다. 기존 데이터베이스에는 라이브러리에 없는 내구성, 트랜잭션, 권한이 이미 있기 때문입니다.

## 전용 벡터 데이터베이스가 정말 필요할까?

벤더 페이지는 줄 수 없는 중립적인 답입니다. 벡터를 지원하는 범용 데이터베이스, 즉 [pgvector](https://github.com/pgvector/pgvector)를 쓰는 Postgres나 배열 필드 위에 [MongoDB의 HNSW 벡터 인덱스](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-overview/)를 쓰는 구성은 **노드당 약 100만 개 벡터까지** 무리 없이 처리하며, 애플리케이션 데이터와 그 [권한](/glossary/ko/access-control-lists-acl/) 바로 옆에 자리합니다. 전용 엔진이 운영 비용과 *이중 시스템 동기화 비용*(벡터 저장소를 기준 데이터베이스와 일관되게 유지하는 부담)을 감수할 가치가 생기는 것은 진정으로 큰 규모에서입니다. 수천만 개의 벡터, 매우 높은 쿼리 처리량, 또는 강한 필터링 *아래에서도* 높은 재현율이 요구되는 경우입니다. 벡터가 약 100만 개 미만이라면 특화 저장소는 성능으로 돌려주는 것보다 연결 코드에 드는 비용이 대개 더 큽니다. "벡터 데이터베이스가 필요하다"고 말하는 페이지 대부분이 구조적으로 할 수 없는 계산입니다. 그 페이지들이 바로 벡터 데이터베이스 벤더이기 때문입니다.

## 메타데이터 필터링이 곧 보안 경계

벡터 유사도는 의미로 순위를 매길 뿐 누가 무엇을 읽을 수 있는지는 *전혀* 모릅니다. 따라서 프로덕션 쿼리는 결코 "가장 가까운 이웃 10개"가 아니라 "가장 가까운 이웃 10개 중 **이 사용자가 볼 수 있는** 것"입니다. 순서를 잘못 잡으면 관련성 버그가 아니라 데이터 유출이 됩니다. **사전 필터링**은 ANN 검색 *전에* 후보를 허용된 집합으로 좁힙니다(올바른 방법). **사후 필터링**은 검색 후에 결과를 잘라 내므로, 금지된 항목의 존재를 노출하는 동시에 *k*개보다 적은 결과를 조용히 반환합니다. 필터가 [ACL](/glossary/ko/access-control-lists-acl/)이라면, 사전 필터링은 시맨틱 검색과 보안 침해를 가르는 차이입니다. [RAG](/glossary/ko/retrieval-augmented-generation-rag/)가 뼈아프게 배우는 바로 그 교훈입니다. 이미 권한을 강제하는 데이터베이스에 임베딩을 저장하면 필터는 저절로 따라옵니다.

## 실제 코드로 보는 임베딩

**JavaScript:**

```javascript
// JavaScript — Cloud Code (cloud/main.js)
// Embeddings live as a field next to the data they describe
Parse.Cloud.beforeSave('Doc', async (req) => {
  if (req.object.dirty('text')) {
    // generate server-side; embedding API key stays on the server
    req.object.set('embedding', await embed(req.object.get('text')));
    req.object.set('embedModel', 'text-embed-v3'); // version it — models differ
  }
});

// Similarity search, ACL-filtered: "nearest neighbors this user MAY see"
Parse.Cloud.define('semanticSearch', async (req) => {
  const qVec = await embed(req.params.q);
  return vectorSearch('Doc', qVec, { limit: 10, aclUser: req.user });
  // The pre-filter is the security boundary, not a relevance tweak.
});
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The client searches by meaning — the vector math is server-side
final results = await ParseCloudFunction('semanticSearch')
    .execute(parameters: {'q': 'how do refunds work?'});
for (final doc in results.result) print(doc['title']);
// "refund policy" matches "money back" though they share no keywords —
// because their embeddings are near each other. Filtered to your ACL.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The client searches by meaning — the vector math is server-side
let results: [[String: Any]] = try await Cloud.run(
    name: "semanticSearch",
    parameters: ["q": "how do refunds work?"])
for doc in results { print(doc["title"] ?? "") }
// "refund policy" matches "money back" though they share no keywords —
// because their embeddings are near each other. Filtered to your ACL.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The client searches by meaning — the vector math is server-side
val params = mapOf("q" to "how do refunds work?")
val results = ParseCloud.callFunction<List<Map<String, Any>>>(
    "semanticSearch", params)
results.forEach { println(it["title"]) }
// "refund policy" matches "money back" though they share no keywords —
// because their embeddings are near each other. Filtered to your ACL.
```

`embedModel` 필드에 주목해야 합니다. 서로 다른 모델, 또는 같은 모델의 다른 *버전*에서 나온 벡터는 서로 비교할 수 없으므로, 임베딩 모델을 바꾸면 전체 코퍼스를 다시 임베딩해야 합니다. 모델 이름을 벡터와 함께 저장하지 않으면, 조용한 업그레이드 한 번에 인덱스가 노이즈로 변합니다.

## 일반적인 사용 사례

- **시맨틱 검색**: 공통 키워드가 하나도 없어도 "돈 돌려받기"로 "환불 정책"을 찾습니다.
- [**RAG 검색**](/glossary/ko/retrieval-augmented-generation-rag/): LLM의 답변을 그라운딩하는 청크를 가져옵니다.
- **추천**: 임베딩 공간에서 사용자의 이력 근처에 있는 항목을 찾습니다.
- **중복 제거와 클러스터링**: 거의 같은 콘텐츠는 거의 같은 벡터가 됩니다.
- **이상 탐지와 사기 탐지**: 이상치는 모든 정상 사례에서 멀리 떨어진 벡터입니다.

## 벡터는 어디에 두어야 할까? 결정 매트릭스

| 상황 | 선택할 방법 |
| --- | --- |
| 앱 규모의 코퍼스(벡터 ~100만 개 미만) | 사용 중인 데이터베이스의 벡터 인덱스 – 데이터 바로 옆 |
| 벡터가 사용자 권한을 따라야 함 | [ACL](/glossary/ko/access-control-lists-acl/)이 이미 있는 곳 |
| 수천만 개의 벡터, 높은 QPS | 전용 벡터 엔진 |
| 정확한 용어*와* 의미가 모두 중요함 | 순위 융합을 적용한 하이브리드 검색 |
| 빠른 프로토타입 | pgvector 또는 임베디드 저장소 – 과잉 구축은 금물 |
| 단일 프로세스, 영속성 불필요 | 인덱스 라이브러리(FAISS) – 그 한계는 감수 |

## 한계와 트레이드오프

- **임베딩은 모델의 세계관을 담습니다.** 편향, 사각지대, 학습 데이터 기준일이 함께 따라옵니다. 기하 구조는 그것을 그린 모델만큼만 좋습니다.
- **ANN은 의도적으로 근사합니다.** 재현율은 걸려 있는 위험의 크기에 맞춰 조정해야 합니다. "대개 가장 잘 맞는 결과를 찾는다"는 것이 속도를 얻는 대가로 받아들인 조건입니다.
- **모델 버전은 스키마의 일부입니다.** 모델을 바꿀 때 대규모 코퍼스를 다시 임베딩하는 일은 설정 하나 바꾸는 일이 아니라 본격적인 마이그레이션입니다.
- **메모리가 상한선입니다.** HNSW 그래프는 RAM에 상주합니다. 노드당 벡터 한도는 이름표만 다른 메모리 한도입니다.
- **유사도는 관련성이 아닙니다.** 벡터 공간에서 가깝다는 것은 강력한 신호이지 보장이 아닙니다. 순수 벡터 검색은 정확한 용어를 놓치기 때문에 하이브리드 검색과 리랭킹이 존재합니다.

## Back4app에서의 벡터

Back4app은 관리형 데이터베이스, 자동 생성되는 REST 및 GraphQL API, 인증, 파일 스토리지, Cloud Code 서버리스 함수를 결합한 오픈소스 Backend as a Service(BaaS) 플랫폼입니다. Back4app은 MongoDB 위에서 동작하므로, 저장 방식은 이 글이 권장하는 바로 그 방식입니다. 임베딩은 **자신이 설명하는 객체의 배열 필드**이며, 문서와 그 벡터가 같은 레코드에 있고, 그 밑에서 실제로 작동하는 메커니즘은 HNSW 벡터 인덱싱입니다. 결과는 자연스럽게 이어집니다. 이미 모든 쿼리를 통제하는 [ACL](/glossary/ko/access-control-lists-acl/)이 *추가 비용 없이* 유사도 검색의 사전 필터가 되므로 검색이 권한 경계를 넘을 수 없습니다. 임베딩은 [키를 서버 측에 둔 채](/glossary/ko/api-key-security/) 임베딩 모델을 호출하는 [Cloud Code](/glossary/ko/cloud-code-serverless-functions/) `beforeSave` 트리거에서 생성됩니다. 그리고 동기화하고, 보안을 챙기고, 비용을 치러야 할 별도의 벡터 서비스가 없습니다. 대부분의 제품이 실제로 다루는 앱 규모의 코퍼스라면, "벡터 데이터베이스"는 도입해야 할 시스템이 아니라 추가하는 필드 하나입니다.
