본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 25. 11:27

브라우저만으로 동작하는 간이 RAG(검색 증강 생성)를 만들어 보았다: IndexedDB + 벡터 검색

요약

외부 서버나 DB 없이 브라우저의 IndexedDB와 JavaScript만으로 동작하는 초간이 RAG 시스템 구현 방법을 소개합니다. 글자 빈도 기반의 벡터화와 코사인 유사도 계산을 통해 RAG의 핵심 구조를 이해할 수 있도록 돕습니다.

핵심 포인트

  • IndexedDB를 활용한 브라우저 내 데이터 영속화 및 저장
  • 문장을 수치 집합으로 변환하는 벡터화 개념 이해
  • 코사인 유사도를 이용한 문장 간 의미적 유사도 측정
  • Node.js와 외부 DB가 필요 없는 서버리스 구조 구현

최근 몇 년 사이, 생성 AI와 관련된 문맥에서 **RAG(검색 증강 생성, Retrieval-Augmented Generation)**나 **벡터 검색 (Vector Search)**이라는 단어를 자주 접하게 되었습니다.

실제로 접해본 분들도 많으시겠지만, 막상 사용해 보면 "결국 이게 어떤 구조로 작동하는 거지?" 라고 느끼는 경우도 많지 않을까요?

이번에는 아주 심플하게

  • Node.js 불필요
  • 외부 DB 불필요 (브라우저 내장 IndexedDB로 완결)
  • 서버 불필요
  • 브라우저만 사용

하여 **"간이 벡터 검색 같은 것"**을 만들어 보았습니다.

완성 이미지

예를 들어,

벡터 검색의 구조를 알고 싶다

라고 검색하면,

RAG는 벡터 검색으로 관련 문서를 취득한 후 LLM에 전달하는 구조다 (유사도: 0.81)
Embedding 모델은 문장을 고차원 벡터로 변환한다 (유사도: 0.74)
코사인 유사도는 벡터의 방향이 얼마나 가까운지 측정하는 지표다 (유사도: 0.71)

와 같이, 의미적으로 가까운 문장이 상위에 반환되는 이미지입니다.

이번에는 본격적인 Embedding 모델(텍스트의 의미·문맥·의도를 고정밀도로 벡터화할 수 있는 모델)은 사용하지 않고,

  • 글자의 출현 빈도
  • 코사인 유사도 (Cosine Similarity)

를 이용한 초간이 버전으로 구현합니다.

실제 모델과는 정밀도가 다르지만,

"문장을 벡터로 변환하여 유사도로 순위를 매긴다"

라는 RAG 검색 부분의 구조를 이해하는 데에는 충분하다고 생각합니다.

IndexedDB를 사용하는 이유

이번 스토리지에는 IndexedDB를 사용합니다.

IndexedDB는 브라우저 내장 Key-Value 스토어형 데이터베이스로,

  • LocalStorage보다 대용량 (상한은 디바이스의 디스크 여유 공간에 의존)
  • 페이지를 닫아도 데이터가 영속화됨
  • 인덱스를 생성하여 프로퍼티 값으로 검색 가능
  • 트랜잭션 (Transaction)을 지원함
  • 브라우저만으로 완결됨

이라는 특징이 있습니다.

구현

이번에 사용하는 것은 HTML과 JavaScript뿐입니다.

우선 HTML

<!DOCTYPE html>
<html lang="ja">
<head>
...

매우 심플합니다.

검색어 입력란과 검색 버튼뿐입니다.

IndexedDB 초기화

다음으로 app.js를 만듭니다. 먼저 DB를 열고 문서를 등록합니다.

const documents = [
// RAG・벡터 검색의 기초 개념
"RAG는 벡터 검색으로 관련 문서를 취득한 후 LLM에 전달하는 구조다",
...

여기서 포인트가 되는 부분은 vector: createVector(text) 부분입니다.

문장을 등록하는 시점에 벡터로 변환하여 함께 저장하고 있습니다.

실제 RAG에서도 "문서를 사전에 의미 벡터화(Embedding)하여 저장해 두는 것"이 기본적인 흐름입니다.

초간이 벡터화

이번에는 글자의 출현 횟수로 벡터를 만듭니다.

function createVector(text) {
const vector = {};
for (const char of text) {
...

예를 들어 "RAG검색"이라면 { R:1, A:1, G:1, 검:1, 색:1 }이라는 딕셔너리가 됩니다.

실제 Embedding (Word2Vec, text-embedding-3-small 등)과는 정밀도가 완전히 다르지만,

"문장을 수치의 집합으로 변환하여 비교한다"라는 사고방식은 같습니다.

코사인 유사도

두 벡터가 얼마나 닮았는지를 계산합니다.

function cosineSimilarity(vec1, vec2) {
const keys = new Set([
...Object.keys(vec1),
...

식으로 쓰면 다음과 같습니다.

"벡터의 내적 ÷ 각각의 노름(Norm)의 곱"이며, 일반적으로 -1~1 사이의 값을 얻습니다.

다만, 이번에는 글자의 출현 횟수(0 이상)로 벡터를 만들고 있기 때문에, 이 구현에서는 실질적으로 0~1 범위의 값만 가질 수 있습니다.

1에 가까울수록 방향이 비슷하며, 의미적으로 가까운 문장이라는 뜻이 됩니다.

이 장의 코사인 유사도 계산식은 AI에게 생성해 달라고 했습니다.

동작 확인은 했습니다만, 수학적인 부분의 상세한 설명은 제 자신의 언어로 설명하기 어렵기 때문에,

여기서는 "방향이 일치할수록 값이 1에 가까워진다"는 직관적인 이해 정도로만 남겨두겠습니다.

검색 처리

쿼리 문자열을 벡터화하고, DB 내의 모든 문서와 점수를 비교하여 내림차순으로 반환합니다.

// IndexedDB의 콜백 기반 API를 Promise로 래핑하는 헬퍼
function getAllDocs() {
return new Promise((resolve, reject) => {
...

실제로 해보기

먼저 HTML 실행입니다.

HTML 파일을 직접 실행하기만 하면 작동합니다.

IndexedDB가 생성되고 레코드가 추가되어 있습니다.

레코드의 상세 내용을 열면 벡터화(Vectorization) 상세 내용을 참조할 수 있습니다.

**"벡터 검색의 원리를 알고 싶다"**로 검색하면,

이와 같이 유사도 순으로

RAG는 벡터 검색으로 관련 문서를 가져온 뒤 LLM에 전달하는 구조다 유사도: 0.44
코사인 유사도는 벡터의 방향 근접성을 측정하는 지표다 유사도: 0.35
벡터 DB는 Annoy나 Faiss 같은 근사 최근접 이웃 탐색을 사용하는 경우가 많다 유사도: 0.32
...

라고 출력됩니다.

문자의 출현 횟수 벡터이므로, 쿼리와 문서에서 동일한 문자가 얼마나 겹치는지만을 보고 있습니다.

따라서 이번 검증의 한계로서 유사도는 낮게 나타납니다.

문자 레벨의 일치로 동작하기 때문에 정밀도는 거칠지만,

쿼리와 관련된 토픽의 문서가 상위에 올라온다는 점은 체감할 수 있을 것입니다.

신경 쓴 포인트

이번 구현에서 특히 신경 쓴 점은, 브라우저만으로 완결된다는 점입니다.

  • 외부 DB 불필요 (브라우저 내장 IndexedDB로 완결)
  • 서버 불필요
  • API 불필요

그럼에도 "쿼리와 의미적으로 가까운 문장을 찾아 반환한다"는 RAG의 근간이 되는 동작은 재현되어 있습니다.

요약

RAG(검색 증강 생성)라고 하면 "어려울 것 같다"고 느끼는 분들이 많을 것입니다.

이번 구현을 되돌아보면, 하고 있는 일은 매우 단순합니다.

문장을 벡터(수치 집합)로 변환하여 저장해 둔다
쿼리도 같은 방법으로 벡터로 변환한다
저장된 벡터와 비교하여 유사한 것을 순서대로 반환한다

진짜 RAG도 이 흐름은 변하지 않습니다.

다른 점은 "얼마나 정밀도 높은 벡터로 변환할 수 있는가"라는 부분뿐입니다.

이번에는 문자의 출현 횟수라는 초간이 방식을 사용했지만,

실제 프로덕트에서는 text-embedding-3-small과 같은 전용 모델을 사용함으로써,

문장의 의미·문맥·뉘앙스까지 포함한 고정밀 벡터화가 가능해집니다.

또한, 스토리지로 IndexedDB를 선택함으로써 Node.js도 서버도 필요 없는 브라우저 전용 구현을 실현할 수 있었습니다.

IndexedDB는 LocalStorage와 달리 구조화된 데이터와 인덱스를 다룰 수 있기 때문에, 이번과 같은 벡터의 저장 및 취득에는 궁합이 좋은 선택입니다.

"원리를 이해한다"는 목적에 있어서는, 복잡한 인프라나 외부 서비스를 일절 사용하지 않고 RAG의 검색 부분을 체험할 수 있다는 점이 이 브라우저 전용 구현의 가장 큰 가치라고 생각합니다.

흥미가 생기신 분들은 다음 단계로서

  • 문서를 파일로부터 읽어올 수 있도록 만들기
  • OpenAI의 Embedding API와 연결하여 진짜 벡터 사용하기
  • 검색 결과를 LLM에 전달하여 답변 생성시키기

등의 방향으로 발전시켜 본다면, 더욱 본격적인 RAG 이해로 이어질 것입니다.

참조 링크

IndexedDB

RAG・Embedding

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0