테마 전환

벡터 데이터베이스가 너무 비싸다면? Vectorize 무료 플랜으로 30분 만에 시맨틱 검색 구현하기

Easton editorial illustration: cost-quality-speed triangle

블로그에 지능형 검색을 추가하려다 보니 Pinecone의 가장 저렴한 플랜도 월 50달러라는 사실을 알게 됐습니다. 벡터 데이터베이스는 의미를 이해하고 비슷한 콘텐츠를 찾으며 AI에 지식 베이스를 제공할 수 있지만, 개인 프로젝트에는 너무 비쌉니다.

Cloudflare Vectorize의 무료 한도는 충분합니다. 벡터 100만 개를 3만 번 조회해도 0.31달러에 불과합니다. 이 글에서는 Vectorize가 무엇인지, 왜 저렴한지, 어떻게 사용하는지를 설명하고 30분 만에 완전한 시맨틱 검색 데모를 만들어 봅니다.

1부: 핵심 개념

벡터 데이터베이스란? 세 문장으로 이해하기

저도 처음 ‘768차원 벡터’라는 말을 봤을 때는 막막했습니다. 고등학교 수학 시간의 악몽처럼 들리지만, 실제로는 그렇게 복잡하지 않습니다.

기존 데이터베이스는 텍스트, 숫자, 날짜처럼 눈에 보이는 값을 저장합니다. 벡터 데이터베이스는 그와 달리 텍스트 뒤에 담긴 ‘의미’를 저장합니다. 예를 들어 검색창에 ‘애플 휴대폰’을 입력하면 기존 검색은 그 단어와 정확히 일치하는 결과만 찾습니다. 반면 벡터 검색은 ‘iPhone’, ‘애플 신제품’, ‘Apple 플래그십폰’이 같은 대상을 가리킨다는 사실을 이해합니다.

원리는 간단합니다. 텍스트를 OpenAI 임베딩 모델 같은 AI 모델에 넣으면 [0.23, -0.45, 0.78, ...]처럼 숫자 768개가 나옵니다. 이 숫자 묶음이 ‘벡터’이며, 텍스트의 의미를 좌표로 수치화한 것이라고 이해할 수 있습니다. 뜻이 비슷한 두 문장의 벡터는 수학적으로도 가까운 위치에 놓입니다.

실제로 제 블로그에서 ‘저렴한 노트북’을 검색했더니 키워드가 하나도 겹치지 않는 ‘가성비 좋은 laptop’ 글이 검색됐습니다. 의미가 일치했기 때문입니다. 이것이 시맨틱 검색의 힘입니다.

왜 Vectorize인가? 주요 세 가지 솔루션 비교

벡터 데이터베이스에는 Pinecone, Weaviate, Milvus처럼 전문적으로 보이는 제품이 많습니다. 그런데 왜 Vectorize를 선택해야 할까요? 저도 한동안 고민했지만 비교해 보니 소규모 프로젝트와 개인 개발자에게 Vectorize는 상당히 매력적이었습니다.

먼저 가장 현실적인 비용부터 살펴보겠습니다

Pinecone은 가장 유명하지만 가격 장벽이 높습니다. 가장 저렴한 표준 플랜도 월 최소 50달러이며, 벡터 100만 개 저장 비용은 월 약 41달러입니다. Weaviate 서버리스 버전은 월 25달러부터 시작하고, 1536차원 벡터 100만 개를 저장하고 조회하면 요금이 최대 153달러까지 오릅니다. 압축 버전은 25달러로 더 저렴합니다.

Vectorize는 어떨까요? 제가 측정해 보니 768차원 벡터 100만 개를 저장하고 하루 1,000회, 즉 한 달에 3만 회 조회해도 총비용이 0.31달러였습니다. 맞습니다. 단 0.31달러입니다. Cloudflare 공식 블로그에 따르면 조회 비용은 75%, 저장 비용은 98% 낮아졌습니다.

0.31달러
벡터 100만 개를 3만 회 조회하는 비용
Pinecone 최저 월 50달러, Weaviate 월 25달러부터, Vectorize 실측 0.31달러로 Pinecone보다 월 50달러 절약

더 좋은 점은 Vectorize가 소규모 프로젝트나 MVP 검증에 충분한 무료 한도를 제공한다는 것입니다. 실제 사용량이 늘어난 뒤 유료 전환을 고민해도 됩니다.

다음은 통합 난이도입니다

Pinecone과 Weaviate는 별도 계정을 만들고 API 키를 관리하며 네트워크 접근을 설정해야 합니다. 이미 Cloudflare Workers로 앱을 배포한다면 Vectorize는 바로 사용할 수 있습니다. wrangler.toml에 몇 줄을 추가해 바인딩하고 코드에서 env.VECTORIZE_INDEX를 호출하면 됩니다. 환경 변수도 따로 설정할 필요가 없습니다.

예전에 Pinecone을 다룰 때는 Workers에 API 키를 안전하게 저장하는 방법을 파악하는 데만 30분이 걸렸습니다. Vectorize에는 이런 번거로움이 없습니다.

마지막으로 적합한 사용 사례를 살펴보겠습니다

Vectorize가 모든 상황에 맞는 것은 아닙니다.

  • 소규모 프로젝트, MVP, 개인 블로그: 비용이 낮고 시작이 빠른 Vectorize가 확실히 유리합니다.
  • 엔터프라이즈, 초대규모(수억 개 벡터): 인프라와 기업 지원이 더 성숙한 Pinecone이 적합합니다.
  • 멀티모달 요구(이미지, 동영상, 오디오): 멀티모달 입력을 기본 지원하는 Weaviate가 더 다양한 기능을 제공합니다.

Cloudflare 공식 자료에 따르면 Vectorize는 현재 인덱스당 최대 500만 개 벡터를 지원합니다. 대부분의 앱에는 충분한 규모입니다. 제 개인 블로그는 3년 동안 글이 200여 개 쌓였을 뿐이라 상한까지는 한참 남았습니다.

500만 개 벡터
무료 한도
최대 500만 개 벡터 인덱스 지원
월 300만 회
무료 조회 한도
월 약 300만 회 조회
Pinecone보다 월 50달러 절약
비용 비교
벡터 100만 개를 3만 회 조회해도 0.31달러

핵심은 간단합니다. 벡터 검색을 처음 시도하거나 예산이 제한적이라면 Vectorize는 최고의 출발점입니다. 서비스가 실제로 성장한 뒤 이전을 검토해도 늦지 않습니다.

Vectorize로 무엇을 할 수 있나? 네 가지 실전 사례

이론보다 실제 활용이 더 궁금할 겁니다. 제가 직접 해봤거나 주변에서 본 실용적인 사례를 소개합니다.

1. 지능형 문서 검색

가장 흔한 사용법입니다. 회사에 기술 문서, 제품 설명서, 법률 문서가 수백 개 있다면 기존 Ctrl+F만으로는 정확한 내용을 찾기 어렵습니다. Vectorize로 시맨틱 검색을 만들면 직원이 ‘경비 정산은 어떻게 신청하나요’라고 입력했을 때 ‘비용 정산 절차’, ‘출장비 제출 안내’ 같은 관련 문서를 찾을 수 있습니다. 예전에 팀 내부 지식 베이스를 만들었는데, 출시 첫 주부터 ‘그 문서 어디 있나요’라는 반복 질문이 상당히 줄었습니다.

2. 글 추천 시스템

‘관련 글’ 기능은 흔하지만 많은 사이트가 태그를 하드코딩해 비교하거나 몇 개 글을 무작위로 노출합니다. Vectorize는 현재 글의 내용을 기준으로 실제 관련 글을 자동으로 찾습니다. 사용자가 ‘React Hooks 모범 사례’를 읽고 있다면 ‘Vue 입문 가이드’가 아니라 ‘useEffect의 흔한 함정’을 추천할 수 있습니다. 제 블로그에서 한 달간 운영한 결과 무작위 추천보다 클릭률이 40% 높았습니다.

3. RAG 애플리케이션(AI에 지식 베이스 제공)

RAG는 Retrieval-Augmented Generation의 약자입니다. 쉽게 말하면 ChatGPT가 비공개 데이터에 관한 질문에 답할 수 있도록 하는 방식입니다. 제품 문서 100개가 있고 사용자가 ‘이 기능은 일괄 가져오기를 지원하나요’라고 묻는다면, 시스템이 먼저 Vectorize에서 관련 문서를 찾고 그 내용을 GPT에 전달해 답변을 생성합니다. 그러면 AI가 함부로 지어내지 않고 실제 문서를 기반으로 답하게 됩니다. 현재 많은 고객 지원 챗봇이 이 방식을 사용합니다.

4. 콘텐츠 중복 제거 및 분류

사용자 피드백을 관리하면서 매일 수백 개 메시지를 받는다면 그중 상당수는 같은 문제를 말할 수 있습니다. Vectorize는 ‘로그인 실패’, ‘로그인할 수 없음’, ‘접속되지 않음’을 자동으로 같은 범주에 묶어 수작업을 줄입니다. 콘텐츠 검수 과정에서는 중복 게시물이나 유사한 마케팅 문구도 빠르게 찾을 수 있습니다.

이 네 가지는 벡터 데이터베이스의 대표적인 활용 사례를 대부분 포함합니다. 결국 ‘비슷한 콘텐츠 찾기’가 필요하다면 벡터 데이터베이스가 도움이 됩니다.

2부: 직접 구현하기

준비: 5분 환경 구성

이론을 살펴봤으니 이제 직접 만들어 보겠습니다. 코드를 쓰기 전에 환경부터 준비합니다. 전체 과정은 약 5분이면 충분합니다.

1단계: Cloudflare 계정 등록

cloudflare.com에서 무료 계정을 만듭니다. 이미 CDN, DNS 같은 다른 Cloudflare 서비스를 사용 중이라면 기존 계정을 사용하면 됩니다.

2단계: Wrangler CLI 설치

Wrangler는 Workers와 Vectorize를 관리하는 Cloudflare 명령줄 도구입니다. 터미널을 열고 다음 명령을 실행합니다.

npm install -g wrangler

yarn이나 pnpm을 사용한다면 해당 패키지 관리자의 명령으로 바꾸면 됩니다. 설치 후 다음 명령으로 확인합니다.

wrangler --version

버전 번호가 표시되면 설치가 완료된 것입니다.

3단계: Cloudflare 로그인

터미널에서 다음 명령을 실행합니다.

wrangler login

브라우저가 자동으로 열리고 권한을 요청합니다. ‘허용’을 클릭하면 터미널에 ‘Successfully logged in’이 표시됩니다.

4단계: 프로젝트 생성

원하는 위치에 프로젝트 폴더를 만듭니다.

mkdir vectorize-demo
cd vectorize-demo
wrangler init

Wrangler가 여러 항목을 물으면 Enter를 눌러 기본값을 사용하면 됩니다. TypeScript를 사용할지 묻는다면 ‘Yes’를 권장하지만 JavaScript도 문제없이 사용할 수 있습니다.

이제 프로젝트 디렉터리에 다음 파일이 있어야 합니다.

  • wrangler.toml - 설정 파일
  • src/index.ts - Workers 코드
  • package.json - 의존성 관리

환경 구성이 끝났습니다. 이제 첫 벡터 인덱스를 만들어 보겠습니다.

핵심 단계: 첫 벡터 인덱스 생성

인덱스는 모든 벡터가 저장되는 ‘집’입니다. 한 줄 명령으로 간단히 만들 수 있습니다.

인덱스 생성

프로젝트 디렉터리에서 다음 명령을 실행합니다.

wrangler vectorize create my-search-index --preset @cf/baai/bge-small-en-v1.5

my-search-index는 인덱스 이름이며 영문자, 숫자, 하이픈을 사용해 자유롭게 정할 수 있습니다. --preset은 임베딩 모델을 지정하는 매개변수입니다. 여기서는 Cloudflare 내장 BGE 모델인 768차원 모델을 사용합니다.

실행 후 다음과 비슷한 출력이 표시됩니다.

✅ Successfully created index my-search-index

preset을 간단히 설명하겠습니다. Cloudflare가 여러 일반 임베딩 모델을 내장하고 있어 OpenAI API를 별도로 구매하지 않아도 됩니다. bge-small-en-v1.5는 성능이 좋고 빠르며 비용이 낮은 768차원 소형 모델입니다. 중국어 검색을 구현한다면 @cf/baai/bge-base-zh-v1.5를 권장합니다.

wrangler.toml 설정

이제 Workers에 인덱스 사용 방법을 알려 줍니다. wrangler.toml 파일 끝에 다음 설정을 추가합니다.

[[vectorize]]
binding = "VECTORIZE_INDEX"
index_name = "my-search-index"

binding은 코드에서 인덱스를 호출할 때 사용하는 변수 이름이며, index_name은 방금 만든 인덱스 이름과 같아야 합니다.

확인하기

인덱스가 제대로 생성됐는지 확인하려면 전체 인덱스를 나열합니다.

wrangler vectorize list

목록에 방금 만든 my-search-index가 표시돼야 합니다.

자주 발생하는 오류

  • ‘Index already exists’가 표시되면 같은 이름의 인덱스가 이미 있다는 뜻입니다. 이름을 바꾸거나 wrangler vectorize delete로 기존 인덱스를 삭제하세요.
  • wrangler.toml[[vectorize]] 설정을 빠뜨리면 코드에서 env.VECTORIZE_INDEX를 호출할 때 ‘undefined’ 오류가 발생합니다.
  • preset의 모델 이름은 정확해야 합니다. 지원 모델 목록은 Cloudflare 문서에서 확인할 수 있습니다.

이제 인덱스가 준비됐습니다. 다음으로 데이터를 넣고 검색하는 코드를 작성하겠습니다.

코드 작성: 30줄로 시맨틱 검색 구현

이제 핵심 기능을 만들 차례입니다. 가장 단순한 방식으로 완전한 시맨틱 검색을 구현하며, 코드를 여러 부분으로 나눠 각 단계가 실행되도록 하겠습니다.

1단계: 데이터 삽입

사용자가 시맨틱 검색할 블로그 글이 몇 개 있다고 가정합니다. 먼저 글을 벡터로 변환해 저장합니다.

src/index.ts를 열고 다음 코드를 작성합니다.

export interface Env {
  VECTORIZE_INDEX: VectorizeIndex;
  AI: Ai; // Cloudflare Workers AI
}
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    // 插入数据的接口
    if (url.pathname === '/insert') {
      const articles = [
        {
          id: '1',
          title: 'Cloudflare Workers入门',
          content: 'Cloudflare Workers是一个无服务器计算平台,让你能在边缘节点运行代码'
        },
        {
          id: '2',
          title: 'Serverless架构指南',
          content: 'Serverless computing让部署和扩展变得非常简单,你不用管服务器'
        },
        {
          id: '3',
          title: 'JavaScript异步编程',
          content: 'Promise和async/await是处理异步操作的现代方式'
        }
      ];
      // 批量生成向量
      const embeddings = await Promise.all(
        articles.map(async (article) => {
          const embedding = await env.AI.run('@cf/baai/bge-small-en-v1.5', {
            text: `${article.title} ${article.content}`
          });
          return {
            id: article.id,
            values: embedding.data[0], // 768维向量
            metadata: {
              title: article.title,
              content: article.content
            }
          };
        })
      );
      // 插入到Vectorize
      await env.VECTORIZE_INDEX.upsert(embeddings);
      return new Response('插入成功!', { status: 200 });
    }
    return new Response('Not found', { status: 404 });
  }
};

이 코드는 다음 세 가지 작업을 합니다.

  1. 예시 글 세 개를 정의합니다.
  2. Cloudflare AI 모델로 각 글을 768차원 벡터로 변환합니다.
  3. upsert 메서드를 호출해 벡터를 Vectorize 인덱스에 저장합니다.

metadata 필드에는 원래 제목과 본문을 저장할 수 있으므로 검색 결과에서 바로 가져올 수 있습니다.

2단계: 검색 요청

데이터를 넣었으니 검색 엔드포인트를 추가합니다.

// 在 fetch 函数里加上这段
if (url.pathname === '/search') {
  const query = url.searchParams.get('q');
  if (!query) {
    return new Response('缺少查询参数 q', { status: 400 });
  }
  // 把查询文本转成向量
  const queryEmbedding = await env.AI.run('@cf/baai/bge-small-en-v1.5', {
    text: query
  });
  // 在索引里搜索最相似的5条
  const results = await env.VECTORIZE_INDEX.query(queryEmbedding.data[0], {
    topK: 5,
    returnMetadata: true
  });
  // 格式化结果
  const formattedResults = results.matches.map((match) => ({
    id: match.id,
    score: match.score, // 相似度分数,0-1之间
    title: match.metadata?.title,
    content: match.metadata?.content
  }));
  return new Response(JSON.stringify(formattedResults, null, 2), {
    headers: { 'Content-Type': 'application/json' }
  });
}

이 엔드포인트는 다음 작업을 합니다.

  1. 사용자가 입력한 ‘서버리스’ 같은 q 쿼리 매개변수를 받습니다.
  2. 검색 문장을 벡터로 변환합니다.
  3. query 메서드를 호출해 가장 비슷한 결과 5개를 찾습니다.
  4. 유사도 점수가 포함된 검색 결과를 반환합니다.

3단계: 로컬 테스트

코드 작성이 끝났으면 실행합니다.

wrangler dev

Wrangler가 보통 http://localhost:8787 주소에 로컬 서버를 실행합니다.

먼저 데이터를 넣습니다.

curl http://localhost:8787/insert

‘插入成功!‘가 표시되면 성공입니다.

이제 검색해 봅니다.

curl "http://localhost:8787/search?q=无服务器平台"

다음과 비슷한 결과가 나옵니다.

[
  {
    "id": "1",
    "score": 0.89,
    "title": "Cloudflare Workers入门",
    "content": "Cloudflare Workers是一个无服务器计算平台..."
  },
  {
    "id": "2",
    "score": 0.85,
    "title": "Serverless架构指南",
    "content": "Serverless computing让部署和扩展变得非常简单..."
  }
]

score는 0~1 사이의 유사도 점수이며 높을수록 관련성이 큽니다. ‘서버리스 플랫폼’을 뜻하는 중국어를 검색했는데도 ‘Cloudflare Workers’와 ‘Serverless’ 관련 글이 검색됐습니다. 이것이 시맨틱 검색의 힘입니다.

코드 설명

  • 왜 OpenAI API를 쓰지 않나요? Cloudflare Workers AI에는 무료 한도가 충분한 임베딩 모델이 내장되어 있고 API 키도 관리할 필요가 없습니다.
  • upsert란 무엇인가요? ‘업데이트 또는 삽입’을 뜻합니다. ID가 이미 있으면 업데이트하고, 없으면 새로 삽입합니다.
  • topK는 얼마가 적당한가요? 보통 5~10개면 충분합니다. 결과가 너무 많으면 사용자가 모두 확인하기 어렵습니다.

여기까지 알면 핵심 사용법의 80%를 익힌 셈입니다. 이제 검색 정확도를 높이는 고급 팁을 살펴보겠습니다.

고급 팁: 세 가지 최적화 방법

기본 기능을 완성한 뒤 검색 품질을 더 높이고 싶다면 다음 방법을 시도해 보세요.

1. 메타데이터 필터: 검색 범위를 정확히 제한하기

블로그에 ‘기술’, ‘생활’, ‘독서 노트’ 같은 분류가 있다고 가정하겠습니다. 사용자가 ‘Python’을 검색하면 기술 글만 보고 싶을 가능성이 큽니다. 이럴 때 메타데이터 필터를 사용합니다.

삽입 코드를 수정해 각 글에 분류를 추가합니다.

metadata: {
  title: article.title,
  content: article.content,
  category: 'tech' // 新增分类字段
}

검색 시 필터 조건을 추가합니다.

const results = await env.VECTORIZE_INDEX.query(queryEmbedding.data[0], {
  topK: 5,
  returnMetadata: true,
  filter: { category: 'tech' } // 只搜索技术类
});

이렇게 하면 ‘Python’을 검색했는데 ‘내가 키우는 비단뱀’ 같은 생활 글이 표시되는 일을 막을 수 있습니다.

2. 하이브리드 검색: 의미와 키워드의 이중 안전장치

순수 시맨틱 검색은 정확히 일치하는 결과를 놓칠 때가 있습니다. 예를 들어 ‘React 18’을 검색하면 제목에 ‘React 18’이 포함된 글이 앞에 나오길 기대할 것입니다.

이럴 때 기존 키워드 필터링을 결합할 수 있습니다.

// 先做语义搜索
const vectorResults = await env.VECTORIZE_INDEX.query(queryEmbedding.data[0], {
  topK: 20, // 多取点候选
  returnMetadata: true
});
// 再做关键词过滤和加权
const finalResults = vectorResults.matches
  .map((match) => {
    let boostedScore = match.score;
    // 如果标题完全匹配关键词,加分
    if (match.metadata?.title.includes(query)) {
      boostedScore += 0.2;
    }
    return { ...match, score: boostedScore };
  })
  .sort((a, b) => b.score - a.score)
  .slice(0, 5); // 取前5条

‘의미로 관련 항목 찾기 + 키워드 일치 항목 가중치 높이기’ 조합을 사용하면 검색 결과가 더 정확해집니다.

3. 일괄 작업: 성능 높이기

수백, 수천 개 데이터를 한 번에 넣을 때 하나씩 insert하면 매우 느립니다. Vectorize의 일괄 작업을 사용하면 성능을 몇 배 높일 수 있습니다.

// 把文章分批,每批100条
const batchSize = 100;
for (let i = 0; i < allArticles.length; i += batchSize) {
  const batch = allArticles.slice(i, i + batchSize);
  const embeddings = await Promise.all(
    batch.map(async (article) => {
      // ... 生成向量
    })
  );
  await env.VECTORIZE_INDEX.upsert(embeddings);
}

예전에 글 500개를 가져올 때 한 건씩 삽입하니 20분이 걸렸지만 일괄 처리로 바꾸자 3분 만에 끝났습니다.

Workers KV에 인기 검색 결과를 저장한다면 캐시 계층도 추가할 수 있습니다. 반복 검색은 KV에서 바로 읽어 매번 벡터 연산을 하지 않아도 됩니다.

3부: 문제 해결 가이드

자주 발생하는 문제와 해결 방법

Vectorize를 사용하면서 제가 겪었던 대표적인 문제를 정리했습니다.

문제 1: 벡터 차원 불일치 오류

오류 메시지: Dimension mismatch: expected 768, got 1536

원인은 간단합니다. 인덱스를 만들 때는 768차원 모델(bge-small)을 사용했지만, 나중에 벡터를 만들 때 1536차원 모델(예: OpenAI의 text-embedding-3-small)로 바꿨기 때문입니다. Vectorize는 같은 인덱스의 모든 벡터 차원이 같아야 합니다.

해결 방법:

  • 방안 1: 임베딩 모델과 일치하는 preset으로 인덱스를 다시 만듭니다.
  • 방안 2: 차원이 맞도록 임베딩 모델을 바꿉니다.

처음부터 사용할 모델을 정하고 중간에 바꾸지 않는 것을 권장합니다.

문제 2: 무료 한도가 실제로 충분한가

가장 많이 궁금해하는 부분입니다. Cloudflare가 아주 명확한 무료 한도 수치를 제시하지는 않았지만 가격 공식으로 계산할 수 있습니다.

저장: 768차원 벡터 약 500만 개

조회: 월 약 300만 회

소규모 프로젝트에는 충분합니다. 글 200개, 일 방문자 300명, 일 검색 약 30회인 제 블로그는 한 달 동안 요금이 0달러였습니다.

무료 한도를 넘더라도 사용량 기반 요금은 매우 저렴합니다. 벡터 100만 개를 3만 회 조회해도 0.31달러에 불과합니다.

문제 3: 어떤 임베딩 모델을 선택해야 하나

Cloudflare가 여러 내장 모델을 제공하므로 상황에 맞게 선택해야 합니다.

  • 중국어 콘텐츠: @cf/baai/bge-base-zh-v1.5(중국어에 특화)
  • 영어 콘텐츠: @cf/baai/bge-small-en-v1.5(성능과 비용의 균형)
  • 다국어 혼합: @cf/baai/bge-m3(100개 이상의 언어 지원)

최고 수준의 품질이 필요하다면 OpenAI의 text-embedding-3-small(1536차원)을 사용할 수도 있지만 API를 직접 호출해야 하며 비용이 더 듭니다.

제 경험상 대부분의 경우 Cloudflare 내장 BGE 모델이면 충분하고 품질도 매우 좋았습니다.

문제 4: 다른 벡터 데이터베이스에서 데이터 이전하기

기존에 Pinecone을 사용하다 비용을 줄이기 위해 Vectorize로 옮기고 싶다면 어떻게 해야 할까요?

방법은 간단합니다.

  1. Pinecone에서 모든 벡터와 metadata를 내보냅니다.
  2. Vectorize 형식으로 변환합니다.
  3. 새 인덱스에 일괄 upsert합니다.

간단한 이전 스크립트의 의사 코드는 다음과 같습니다.

// 从Pinecone获取所有向量
const pineconeVectors = await pineconeIndex.fetch({ ids: allIds });
// 转换格式
const vectorizeFormat = Object.entries(pineconeVectors.vectors).map(
  ([id, vector]) => ({
    id,
    values: vector.values,
    metadata: vector.metadata
  })
);
// 批量插入Vectorize
const batchSize = 100;
for (let i = 0; i < vectorizeFormat.length; i += batchSize) {
  const batch = vectorizeFormat.slice(i, i + batchSize);
  await env.VECTORIZE_INDEX.upsert(batch);
}

벡터 차원은 반드시 같아야 합니다. Pinecone에서 1536차원을 사용했다면 Vectorize 인덱스도 1536차원 preset을 사용해야 합니다.

문제 5: 검색 결과가 정확하지 않을 때

검색 결과가 예상과 크게 다르면 다음 원인을 살펴보세요.

  1. 임베딩 모델이 도메인에 맞지 않음: 의료 콘텐츠처럼 전문 영역에서는 범용 모델의 효과가 낮을 수 있으므로 도메인 특화 모델을 고려합니다.
  2. 입력 텍스트가 너무 짧음: 벡터가 의미를 정확히 표현하려면 충분한 문맥이 필요합니다. 제목만 사용하는 것보다 제목과 요약을 함께 사용하는 편이 좋습니다.
  3. 데이터 정제를 하지 않음: 텍스트에 HTML 태그나 특수문자가 너무 많으면 벡터 품질이 떨어집니다.

저는 제목, 요약, 본문 앞 200자를 합쳐 벡터를 생성합니다. 제목만 사용할 때보다 효과가 확실히 좋습니다.

언제 다른 솔루션으로 업그레이드해야 하나

Vectorize는 매력적이지만 만능은 아닙니다. 다음 상황에서는 다른 방식을 고려해야 할 수 있습니다.

신호 1: 벡터 수가 500만 개를 넘음

Vectorize는 현재 단일 인덱스에서 최대 500만 개 벡터를 지원합니다. 전자상거래 플랫폼의 상품이 수천만 개처럼 데이터 규모가 더 크다면 Pinecone이나 자체 Milvus 클러스터를 고려해야 합니다. 다만 대부분의 앱은 이 규모에 도달하지 않습니다.

신호 2: 멀티모달 검색이 필요함

텍스트 외에 이미지, 오디오, 동영상도 검색해야 한다면 현재 텍스트 벡터만 지원하는 Vectorize로는 부족합니다. 이 경우 멀티모달 데이터를 기본 지원하는 Weaviate가 더 나은 선택입니다.

신호 3: 복잡한 그래프 데이터베이스 요구

애플리케이션이 지식 그래프와 결합되어야 한다고 가정해 봅시다. 예를 들어 ‘2024년에 게시됐고 작성자가 YY이며 XX와 관련된 모든 글 찾기’ 같은 복잡한 질의에는 Vectorize만으로 부족할 수 있습니다. Neo4j 같은 그래프 데이터베이스와 벡터 검색을 결합하는 GraphRAG 방식을 고려할 수 있습니다.

신호 4: 매우 낮은 지연 시간이 필요함

Vectorize의 조회 지연은 보통 50~200ms이며 대부분의 앱에는 충분히 빠릅니다. 하지만 10ms 이내 응답이 필요한 실시간 추천 시스템을 만든다면 Redis + Faiss 같은 메모리 기반 방식이 필요할 수 있습니다.

제안

처음부터 완벽한 솔루션을 찾지 마세요. 먼저 Vectorize로 기능을 완성하고 제품 방향을 검증합니다. 사업이 실제로 성장해 병목이 생긴 뒤 업그레이드를 고려해도 됩니다. 조기 최적화는 모든 악의 근원이며, 이전 비용도 높지 않습니다. 벡터 데이터 형식은 대부분 비슷해서 스크립트 하나로 몇 시간 안에 옮길 수 있습니다.

기술 선택만 한 달 내내 고민하다 결국 프로젝트를 시작하지 못한 사람을 많이 봤습니다. 먼저 만들고 문제가 생기면 조정하는 편이 낫습니다.

결론

처음 질문으로 돌아가 보겠습니다. 벡터 데이터베이스는 정말 비쌀까요?

답은 어떤 제품을 선택하느냐에 달려 있습니다. Pinecone은 최저 월 50달러지만 Vectorize의 무료 한도는 일정 규모의 앱을 운영하기에 충분합니다. 제 블로그 시맨틱 검색은 두 달째 운영 중인데도 요금이 여전히 0달러입니다.

이 글에서는 개념부터 실전까지, 기본 사용법부터 고급 팁까지 살펴봤습니다. 여러분이 Vectorize를 빠르게 시작하는 데 도움이 되길 바랍니다. 프로젝트에 시맨틱 검색을 추가하고 싶다면 직접 시도해 보세요. 데모 완성까지 30분이면 충분하고 비용은 거의 0이라 커피 한 잔보다 낮은 비용으로 실험할 수 있습니다.

다음 단계:

  1. Cloudflare 공식 예제를 참고해 5분 안에 실행하고 결과를 확인합니다.
  2. Cloudflare Discord에 참여해 궁금한 점을 질문합니다.
  3. 회사 지식 베이스 검색, 개인 노트 검색, 지능형 고객 지원 챗봇처럼 자신의 데이터를 연결한 실용적인 앱을 만듭니다.

시맨틱 검색은 생각만큼 어렵지 않습니다. 중요한 것은 첫걸음을 떼는 것입니다. 직접 사용해 보면 실제 문제를 꽤 많이 해결할 수 있다는 사실을 알게 될 것입니다.

이 글이 도움이 됐다면 AI 애플리케이션을 만들고 있는 친구에게도 공유해 주세요. 함께 비용을 아끼며 성장할 수 있습니다.

30분 만에 Vectorize 시맨틱 검색을 구현하는 전체 과정

환경 구성과 벡터 인덱스 생성부터 30줄 코드로 완전한 시맨틱 검색 기능을 구현하는 방법, 고급 팁과 문제 해결 가이드까지 설명합니다.

Estimated time: PT30M

  1. 1

    Step 1: 5분 환경 구성: Cloudflare 가입과 Wrangler CLI 설치

    1단계: Cloudflare 계정 등록
  2. 2

    Step 2: 첫 벡터 인덱스 생성: 한 줄 명령으로 완료

    인덱스 생성:
  3. 3

    Step 3: 30줄 코드로 시맨틱 검색 구현: 데이터 삽입과 검색 요청

    1단계 데이터 삽입: src/index.ts를 열고 예시 글 세 개를 정의합니다. Cloudflare AI 모델로 각 글을 768차원 벡터로 변환한 뒤 upsert 메서드로 Vectorize 인덱스에 저장합니다. metadata에는 원래 제목과 본문을 넣어 검색 결과에서 바로 가져올 수 있습니다. 2단계 검색 요청: fetch 함수에 검색 엔드포인트를 추가합니다. ‘서버리스’ 같은 q 쿼리 매개변수를 받아 검색 문장을 벡터로 변환하고 query 메서드로 가장 비슷한 결과 5개를 찾아 점수와 함께 반환합니다. 3단계 로컬 테스트: wrangler dev를 실행하면 보통 http://localhost:8787에서 로컬 서버가 시작됩니다. curl http://localhost:8787/insert로 데이터를 넣고 ‘插入成功!‘가 표시되는지 확인합니다. 이어서 curl “http://localhost:8787/search?q=无服务器平台”로 검색합니다. id, score(01 유사도 점수), title, content가 포함된 JSON 결과가 표시됩니다. score가 높을수록 관련성이 큽니다. ‘서버리스 플랫폼’을 검색해도 ‘Cloudflare Workers’와 ‘Serverless’ 관련 글을 찾는 것이 시맨틱 검색의 힘입니다. Cloudflare Workers AI에는 무료 한도가 충분한 임베딩 모델이 내장되어 있어 OpenAI API와 API 키가 필요 없습니다. upsert는 ID가 있으면 업데이트하고 없으면 삽입합니다. topK는 보통 510개면 충분합니다.
  4. 4

    Step 4: 고급 팁: 메타데이터 필터, 하이브리드 검색, 일괄 작업

    1. 메타데이터 필터: 검색 범위를 정확히 제한합니다. 블로그에 ‘기술’, ‘생활’, ‘독서 노트’ 같은 분류가 있다면 사용자가 ‘Python’을 검색할 때 기술 글만 보도록 metadata에 category 필드를 추가하고 query에 filter 조건을 넣습니다. 그러면 ‘Python’ 검색 결과에 ‘내가 키우는 비단뱀’ 같은 생활 글이 섞이지 않습니다. 2. 하이브리드 검색: 의미와 키워드를 함께 사용합니다. 순수 시맨틱 검색이 정확히 일치하는 결과를 놓칠 수 있으므로 ‘React 18’처럼 제목에 키워드가 있는 글에 가중치를 줍니다. 먼저 topK를 20으로 설정해 후보를 더 많이 가져온 뒤 키워드 필터와 가중치를 적용하고 정렬해 상위 5개를 선택합니다. 3. 일괄 작업: 수백, 수천 건을 하나씩 insert하면 느리므로 100개씩 나눠 처리합니다. 글 500개를 한 건씩 넣을 때 20분 걸렸지만 일괄 처리로 바꾸자 3분 만에 완료됐습니다. 인기 검색 결과를 Workers KV에 저장해 캐시하면 반복 검색 때마다 벡터 연산을 하지 않아도 됩니다.
  5. 5

    Step 5: 문제 해결 가이드: 자주 발생하는 문제와 해결 방법

    문제 1 벡터 차원 불일치: Dimension mismatch: expected 768, got 1536은 768차원 모델로 만든 인덱스에 1536차원 모델의 벡터를 넣을 때 발생합니다. 임베딩 모델과 맞는 preset으로 인덱스를 다시 만들거나 차원이 맞는 모델로 바꿉니다. 문제 2 무료 한도: 가격 공식상 768차원 벡터 약 500만 개를 저장하고 월 약 300만 회 조회할 수 있습니다. 글 200개, 일 방문자 300명, 일 검색 약 30회인 제 블로그는 한 달 요금이 0달러였고, 한도를 넘더라도 벡터 100만 개를 3만 회 조회하는 비용은 0.31달러입니다. 문제 3 임베딩 모델: 중국어는 @cf/baai/bge-base-zh-v1.5, 영어는 @cf/baai/bge-small-en-v1.5, 다국어 혼합은 @cf/baai/bge-m3를 사용합니다. 최고 품질이 필요하면 OpenAI text-embedding-3-small(1536차원)을 사용할 수 있지만 API를 직접 호출해야 하고 비용이 더 듭니다. 문제 4 검색 정확도: 모델이 도메인에 맞지 않거나, 입력이 너무 짧거나, HTML 태그와 특수문자 등 데이터를 정제하지 않은 경우 결과가 부정확할 수 있습니다. 제목, 요약, 본문 앞 200자를 합쳐 벡터를 만들면 제목만 사용할 때보다 효과가 좋습니다.

FAQ

Vectorize는 Pinecone, Weaviate와 비교해 어떤 장점이 있으며 비용은 얼마인가요?
비용 비교:

Pinecone:
• 가장 저렴한 플랜도 월 50달러
• 벡터 100만 개 저장 시 월 약 41달러

Weaviate:
• 서버리스 버전 시작가 월 25달러
• 1536차원 벡터 100만 개를 저장하고 조회하면 최대 153달러(압축 버전은 25달러)

Vectorize 실측:
• 768차원 벡터 100만 개, 하루 1,000회 조회(월 3만 회)의 총비용이 0.31달러
• 잘못 본 것이 아닙니다. 단 0.31달러입니다.
• Cloudflare 공식 블로그에 따르면 조회 비용은 75%, 저장 비용은 98% 낮아졌습니다.
• Vectorize는 소규모 프로젝트나 MVP 검증에 충분한 무료 한도도 제공합니다.

통합 난이도:
• Pinecone과 Weaviate는 별도 가입, API 키 관리, 네트워크 접근 설정이 필요합니다.
• 이미 Cloudflare Workers로 앱을 배포한다면 Vectorize는 wrangler.toml에 몇 줄만 추가해 바인딩할 수 있습니다. 코드에서 env.VECTORIZE_INDEX를 바로 호출하므로 환경 변수도 필요 없습니다.

적합한 경우:
• 소규모 프로젝트, MVP, 개인 블로그에는 비용이 낮고 시작이 빠른 Vectorize가 유리합니다.
• 수억 개 규모의 엔터프라이즈 환경에는 인프라와 기업 지원이 더 성숙한 Pinecone이 적합합니다.
• 이미지, 동영상, 오디오 같은 멀티모달 요구에는 기본 멀티모달 입력을 지원하는 Weaviate가 더 풍부한 기능을 제공합니다.
Vectorize의 무료 한도는 얼마이며 실제로 충분한가요?
무료 한도:
• 768차원 벡터 약 500만 개 저장
• 월 약 300만 회 조회

소규모 프로젝트에는 충분합니다. 글 200개, 일 방문자 300명, 일 검색 약 30회인 제 블로그는 한 달 동안 요금이 0달러였습니다.

무료 한도를 넘더라도 사용량 기반 요금은 매우 저렴합니다. 벡터 100만 개를 3만 회 조회해도 0.31달러입니다.

Cloudflare 공식 자료에 따르면 Vectorize 인덱스는 현재 최대 500만 개 벡터를 지원합니다. 대부분의 앱에는 충분한 규모입니다. 3년간 운영한 제 블로그에도 글이 200여 개뿐이라 상한까지는 한참 남았습니다.

벡터 검색을 처음 시도하거나 예산이 제한적이라면 Vectorize는 좋은 출발점입니다. 서비스가 실제로 성장한 뒤 이전을 검토해도 늦지 않습니다.
30분 만에 시맨틱 검색을 구현하려면 어떤 단계를 거쳐야 하나요?
5분 환경 구성:

1단계: Cloudflare 계정 등록(cloudflare.com에서 무료 가입)
2단계: Wrangler CLI 설치(npm install -g wrangler 실행 후 wrangler --version으로 확인)
3단계: Cloudflare 로그인(터미널에서 wrangler login을 실행하면 브라우저가 열리고 권한을 요청함)
4단계: 프로젝트 생성(mkdir vectorize-demo, cd vectorize-demo, wrangler init)

첫 벡터 인덱스 생성:
• 프로젝트 디렉터리에서 wrangler vectorize create my-search-index --preset @cf/baai/bge-small-en-v1.5 실행

wrangler.toml 설정:
• wrangler.toml 끝에 [[vectorize]] 설정 추가
• binding = "VECTORIZE_INDEX"
• index_name = "my-search-index"

30줄 코드로 시맨틱 검색 구현:

1단계: 데이터 삽입
• 예시 글을 정의하고 Cloudflare AI 모델로 각 글을 768차원 벡터로 변환
• upsert 메서드로 Vectorize 인덱스에 저장

2단계: 검색 요청
• q 쿼리 매개변수를 받아 검색 문장을 벡터로 변환
• query 메서드로 가장 유사한 결과 5개 검색
• 점수가 포함된 결과 반환

3단계: 로컬 테스트
• wrangler dev 실행
• curl http://localhost:8787/insert로 데이터 삽입
• curl "http://localhost:8787/search?q=无服务器平台"로 검색 테스트
Vectorize로 무엇을 할 수 있으며 어떤 실전 사례가 있나요?
1. 지능형 문서 검색:
• 가장 일반적인 활용법입니다.
• 회사에 기술 문서, 제품 설명서, 법률 문서가 수백 개 있다면 기존 Ctrl+F로는 원하는 내용을 정확히 찾기 어렵습니다.
• Vectorize로 시맨틱 검색을 만들면 직원이 '경비 정산은 어떻게 신청하나요'라고 입력했을 때 '비용 정산 절차', '출장비 제출 안내' 같은 관련 문서를 찾을 수 있습니다.
• 팀 내부 지식 베이스에 적용했을 때 첫 주부터 '그 문서 어디 있나요'라는 반복 질문이 크게 줄었습니다.

2. 글 추천 시스템:
• 많은 사이트의 '관련 글'은 태그를 하드코딩해 비교하거나 무작위로 글을 노출합니다.
• Vectorize는 현재 글의 내용을 기준으로 실제 관련 글을 자동으로 찾습니다.
• 사용자가 'React Hooks 모범 사례'를 읽을 때 'Vue 입문 가이드'가 아니라 'useEffect의 흔한 함정'을 추천할 수 있습니다.
• 제 블로그에서 한 달간 운영한 결과 무작위 추천보다 클릭률이 40% 높았습니다.

3. RAG 애플리케이션(AI에 지식 베이스 제공):
• RAG는 Retrieval-Augmented Generation의 약자로, ChatGPT가 비공개 데이터에 관한 질문에 답하도록 하는 방식입니다.
• 제품 문서 100개가 있고 사용자가 '이 기능은 일괄 가져오기를 지원하나요'라고 묻는다면 먼저 Vectorize에서 관련 문서를 찾고 GPT에 전달해 답변을 생성합니다.
• 답변이 실제 문서에 근거하므로 AI의 임의 추측을 줄일 수 있습니다.
• 현재 많은 고객 지원 챗봇이 이 방식을 사용합니다.

4. 콘텐츠 중복 제거 및 분류:
• 사용자 피드백을 관리할 때 매일 들어오는 수백 개 메시지 중 상당수는 같은 문제를 말합니다.
• Vectorize를 사용하면 '로그인 실패', '로그인할 수 없음', '접속되지 않음'을 자동으로 같은 범주에 묶을 수 있습니다.
• 콘텐츠 검수에서는 중복 게시물이나 유사한 마케팅 문구도 빠르게 찾을 수 있습니다.
Vectorize 사용 중 자주 겪는 문제는 무엇이며 어떻게 해결하나요?
문제 1: 벡터 차원 불일치
• 오류 메시지: Dimension mismatch: expected 768, got 1536
• 768차원 모델(bge-small)로 인덱스를 만들고 이후 1536차원 모델(예: OpenAI text-embedding-3-small)로 벡터를 생성했기 때문에 발생합니다.
• Vectorize는 같은 인덱스의 모든 벡터가 동일한 차원이어야 합니다.
• 해결책:
방안 1: 임베딩 모델과 맞는 preset으로 인덱스를 다시 생성
방안 2: 차원이 일치하도록 임베딩 모델 변경
• 처음부터 사용할 모델을 정하고 중간에 바꾸지 않는 편이 좋습니다.

문제 2: 무료 한도가 충분한지
• Cloudflare가 아주 명확한 무료 한도 수치를 제시하지는 않았지만 가격 공식으로 계산할 수 있습니다.
• 저장: 768차원 벡터 약 500만 개
• 조회: 월 약 300만 회
• 소규모 프로젝트에는 충분합니다. 글 200개, 일 방문자 300명, 일 검색 약 30회인 제 블로그는 한 달 요금이 0달러였습니다.
• 무료 한도를 넘더라도 벡터 100만 개를 3만 회 조회하는 비용이 0.31달러에 불과합니다.

문제 3: 임베딩 모델 선택
• Cloudflare가 제공하는 여러 내장 모델 중 상황에 맞게 선택해야 합니다.
• 중국어 콘텐츠: @cf/baai/bge-base-zh-v1.5
• 영어 콘텐츠: @cf/baai/bge-small-en-v1.5
• 다국어 혼합: @cf/baai/bge-m3(100개 이상의 언어 지원)
• 최고 수준의 품질이 필요하면 OpenAI text-embedding-3-small(1536차원)을 사용할 수 있지만 API를 직접 호출해야 하고 비용이 더 듭니다.
• 대부분의 경우 Cloudflare 내장 BGE 모델만으로도 충분히 좋은 품질을 얻을 수 있었습니다.

문제 4: 검색 결과가 부정확한 경우
• 가능한 원인은 다음과 같습니다.
1) 임베딩 모델이 도메인에 맞지 않음(의료 콘텐츠에 범용 모델 대신 도메인 특화 모델 고려)
2) 입력 문장이 너무 짧음(제목만 쓰는 것보다 제목과 요약을 함께 쓰는 편이 좋음)
3) 데이터 정제를 하지 않음(대량의 HTML 태그와 특수문자가 벡터 품질에 영향)
• 저는 제목, 요약, 본문 앞 200자를 합쳐 벡터를 생성하며, 제목만 사용할 때보다 효과가 확실히 좋았습니다.
언제 다른 솔루션으로 업그레이드해야 하며 Vectorize에는 어떤 한계가 있나요?
신호 1: 벡터 수가 500만 개를 넘음
• Vectorize는 현재 단일 인덱스에서 최대 500만 개 벡터를 지원합니다.
• 전자상거래 플랫폼의 상품이 수천만 개처럼 데이터 규모가 더 크다면 Pinecone이나 자체 Milvus 클러스터를 고려해야 합니다.
• 다만 대부분의 앱은 이 규모에 도달하지 않습니다.

신호 2: 멀티모달 검색이 필요함
• 텍스트 외에 이미지, 오디오, 동영상도 검색해야 한다면 현재 텍스트 벡터만 지원하는 Vectorize로는 부족합니다.
• 이 경우 멀티모달 데이터를 기본 지원하는 Weaviate가 더 좋은 선택입니다.

신호 3: 복잡한 그래프 데이터베이스 요구
• '2024년에 게시됐고 작성자가 YY이며 XX와 관련된 모든 글 찾기'처럼 지식 그래프를 결합한 복잡한 질의에는 Vectorize만으로 부족할 수 있습니다.
• Neo4j 같은 그래프 데이터베이스와 벡터 검색을 결합하는 GraphRAG 방식을 고려할 수 있습니다.

신호 4: 매우 낮은 지연 시간이 필요함
• Vectorize 조회 지연은 보통 50~200ms로 대부분의 앱에는 충분히 빠릅니다.
• 하지만 10ms 이내 응답이 필요한 실시간 추천 시스템에는 Redis + Faiss 같은 메모리 기반 방식이 필요할 수 있습니다.

처음부터 완벽한 솔루션을 찾기보다 Vectorize로 기능을 완성하고 제품 방향을 검증해 보세요. 사업이 성장해 병목이 실제로 생긴 뒤 업그레이드해도 됩니다. 벡터 데이터 형식은 비슷해서 스크립트 하나로 몇 시간 안에 이전할 수 있습니다. 한 달 내내 기술 선택만 고민하다 프로젝트를 시작하지 못하는 것보다 먼저 만들고 문제가 생길 때 조정하는 편이 낫습니다.

4분 읽기 · 게시일: 2025년 12월 1일 · 수정일: 2026년 9월 4일

댓글

GitHub로 로그인하여 댓글을 남기세요

Easton BlogEaston Blog