AI 대화 검색의 한계 LIKE에서 벡터 검색으로 넘어가는 이유
AIKeep24에 Cloudflare Vectorize 벡터 검색을 추가하는 과정을 통해 임베딩, 코사인 유사도, 벡터 DB의 개념을 좌표 비유로 쉽게 설명합니다
도서관에서 책 찾기, 두 가지 방법
도서관에 가서 “맥락 유실 문제 해결 방법”이라는 주제의 책을 찾는다고 생각해 보겠습니다. 방법은 두 가지입니다.
첫 번째는 서가를 한 칸씩 훑으면서 제목에 “맥락”이라는 글자가 들어간 책을 찾는 것입니다. 글자가 정확히 일치해야만 눈에 들어옵니다. “컨텍스트 관리 전략”이라는 제목의 책은 같은 내용을 다루고 있어도 스쳐 지나갑니다. 이것이 SQL의 LIKE '맥락' 검색입니다. AIKeep24는 지금까지 이 방식으로 저장된 대화 청크를 검색해 왔습니다.
두 번째는 사서에게 “AI 대화가 길어지면 앞부분을 까먹는 문제를 다룬 책이 있을까요?”라고 물어보는 것입니다. 사서는 “맥락”이라는 단어가 제목에 없어도, 그 주제를 다루는 책이 어디에 꽂혀 있는지 알고 있습니다. “컨텍스트 윈도우 한계”, “장기 기억 구현”, “세션 연속성”이라는 제목의 책까지 모두 꺼내 줍니다. 이것이 벡터 검색입니다. 단어가 아니라 의미로 찾는 것입니다.
임베딩이란 무엇인가
벡터 검색이 의미로 찾을 수 있는 이유는 텍스트를 숫자 좌표로 바꾸는 과정, 즉 임베딩(embedding)이 있기 때문입니다.
지도를 떠올려 보면 이해가 쉽습니다. 서울의 좌표는 (37.57, 126.98)이고 부산의 좌표는 (35.18, 129.08)입니다. 좌표만 보면 두 도시가 얼마나 가까운지, 또는 먼지 바로 계산할 수 있습니다. 임베딩은 이와 같은 일을 텍스트에 대해 수행합니다. “AI 대화 요약”이라는 문장을 768개의 숫자로 이루어진 좌표로 변환합니다. “챗봇 대화 정리”라는 다른 문장도 768개의 숫자로 변환합니다. 두 문장은 단어가 전혀 다르지만, 의미가 비슷하기 때문에 768차원 공간에서 매우 가까운 위치에 찍힙니다.
여기서 768이라는 숫자는 좌표의 차원 수입니다. 지도가 위도와 경도의 2차원이라면, 임베딩은 768차원입니다. 사람이 768차원을 상상하기는 어렵지만, 컴퓨터에게는 2차원이든 768차원이든 거리 계산이 똑같이 간단합니다.
코사인 유사도, 방향이 같으면 비슷하다
두 좌표 사이의 거리를 재는 방법도 여러 가지입니다. 벡터 검색에서 가장 널리 쓰이는 것이 코사인 유사도(cosine similarity)입니다.
직관적으로 설명하면 이렇습니다. 원점에서 두 좌표를 향해 각각 화살표를 그립니다. 두 화살표가 같은 방향을 가리키면 코사인 유사도는 1에 가깝고, 완전히 반대 방향이면 -1, 아무 관련이 없으면 0에 가깝습니다. “AI 대화 요약”과 “챗봇 대화 정리”는 거의 같은 방향을 가리키므로 0.89 같은 높은 점수가 나옵니다. 반면 “AI 대화 요약”과 “오늘 날씨가 좋다”는 방향이 전혀 다르므로 0.12 같은 낮은 점수가 나옵니다.
LIKE 검색은 일치하거나 일치하지 않거나 둘 중 하나입니다. 벡터 검색은 “얼마나 비슷한가”를 0에서 1 사이의 점수로 알려줍니다. 완벽히 같은 단어를 쓰지 않아도, 의미적으로 가까우면 찾아낼 수 있는 이유가 바로 이 점수 기반 비교입니다.
Cloudflare Vectorize, 서버 없이 벡터 검색
AIKeep24는 이미 Cloudflare의 D1(SQLite 호환 데이터베이스)과 Workers를 사용하고 있습니다. 벡터 검색을 추가하기 위해 별도의 Pinecone이나 Weaviate 같은 외부 서비스를 붙이는 대신, 같은 Cloudflare 생태계 안에 있는 Vectorize를 선택했습니다.
Vectorize는 Cloudflare의 벡터 데이터베이스입니다. Worker에서 바로 바인딩으로 접근할 수 있고, Workers AI의 임베딩 모델(@cf/baai/bge-base-en-v1.5, 768차원)을 함께 쓸 수 있습니다. 기존 worker.js에 바인딩 두 줄을 추가하면 바로 연동됩니다.
가격 구조도 합리적입니다. 무료 플랜에서 저장 벡터 500만 차원, 쿼리 3,000만 차원이 포함됩니다. AIKeep24의 현재 규모, 즉 수백에서 수천 개의 청크 요약문을 768차원으로 저장하고 하루 수십 번 검색하는 정도라면 무료 범위 안에서 충분합니다. 유료 전환 후에도 월 $0.59 수준(25,000벡터, 월 50,000쿼리 기준)이므로 부담이 거의 없습니다.

AIKeep24에 벡터 검색을 추가하는 흐름
현재 AIKeep24의 검색 흐름은 단순합니다. 사용자가 키워드를 입력하면 worker.js가 D1의 ext_chunks 테이블에 SELECT ... WHERE chunk_summary LIKE '%키워드%'를 실행합니다. 정확한 단어가 포함된 청크만 반환됩니다.
벡터 검색을 추가하면 흐름이 하나 늘어납니다. 대화 청크가 D1에 저장될 때, 동시에 chunk_summary 텍스트를 Workers AI 임베딩 모델에 보내 768차원 벡터로 변환하고, 그 벡터를 Vectorize 인덱스에 저장합니다. 벡터의 ID는 chunk_id를 그대로 사용합니다.
검색할 때는 사용자의 검색어도 같은 임베딩 모델로 벡터화한 뒤, Vectorize에 “이 벡터와 가장 비슷한 상위 5개를 찾아줘”(topK: 5)라고 질의합니다. 돌아온 chunk_id 목록으로 D1에서 원문과 메타데이터를 가져옵니다. 기존 LIKE 검색은 폴백으로 남겨 두어, 벡터 검색 결과가 부족하거나 정확한 키워드 매칭이 필요할 때 보완합니다.
정리하면 저장 시에는 D1 저장과 Vectorize 인서트가 병렬로 일어나고, 검색 시에는 벡터 유사도 검색이 먼저, LIKE 검색이 보조로 작동하는 하이브리드 구조입니다.
왜 이것이 필요한가
AIKeep24에 이미 300개가 넘는 청크가 쌓여 있습니다. “체크포인트 체이닝 방식”을 검색하면 LIKE로도 찾을 수 있습니다. 그런데 “이전 대화 내용을 다음 대화에 연결하는 방법”이라고 검색하면 아무것도 나오지 않습니다. 같은 내용을 설명하는 문장인데도 단어가 다르기 때문입니다.
바이브코딩을 하다 보면 과거에 해결했던 문제를 정확한 용어로 기억하지 못하는 경우가 많습니다. “그때 그 CORS 문제 어떻게 해결했더라”라고 검색하고 싶은데, 실제 청크 요약에는 “교차 출처 요청 차단 해결”이라고 적혀 있을 수 있습니다. 벡터 검색은 이런 상황에서 진가를 발휘합니다.
임베딩 모델 선택과 한국어
현재 Cloudflare Workers AI에서 제공하는 @cf/baai/bge-base-en-v1.5는 영어 중심 모델입니다. AIKeep24의 대화는 한국어와 영어가 섞여 있기 때문에 향후 다국어 임베딩 모델로 교체하거나, 로컬에서 EXAONE 기반 임베딩을 생성한 뒤 Vectorize에 직접 업로드하는 방식도 검토하고 있습니다. 우선은 Cloudflare 내장 모델로 시작하고, 한국어 검색 정확도를 모니터링하면서 단계적으로 개선할 계획입니다.
마무리
일반 검색은 “이 단어가 있느냐 없느냐”를 묻고, 벡터 검색은 “이 의미와 얼마나 가까우냐”를 묻습니다. 사서가 책 제목뿐 아니라 내용까지 알고 있는 것처럼, 임베딩과 벡터 DB는 텍스트의 의미를 좌표로 기억합니다. AIKeep24는 이미 대화를 요약하고 태깅하고 원문을 보존하는 구조를 갖추었고, 여기에 벡터 검색이 추가되면 “기억은 하지만 정확한 단어가 떠오르지 않는” 상황까지 커버할 수 있게 됩니다.
Cloudflare Vectorize는 무료 플랜에서 시작할 수 있고, 기존 D1 + Workers 구조에 바인딩 몇 줄로 연결됩니다. 바이브코딩 프로젝트에 의미 검색을 붙이고 싶다면, 생각보다 문턱이 낮습니다.
다음 글에서는 실제 wrangler.toml 설정, worker.js 코드 변경, 기존 청크 일괄 임베딩 마이그레이션 과정을 단계별로 다루겠습니다.
#바이브코딩 #벡터검색 #임베딩 #Vectorize #Cloudflare #AIKeep24 #코사인유사도 #의미검색 #D1 #WorkersAI