
한빛미디어 서평단 <나는리뷰어다> 활동을 위해서 책을 협찬받아 작성된 서평입니다
TL;DR
- 익숙한 SQLite3와 PostgreSQL에서 임베딩과 검색을 거쳐 RAG로 나아가는 책이라, AI 쪽 서랍을 열어보려던 백엔드 개발자에게 잘 맞는 편이라고 느꼈습니다.
- 같은 실습의 뼈대를 다른 데이터와 검색 방식으로 반복하니, 장이 넘어갈수록 무엇이 바뀌는지 살펴보기에 어느 정도 도움이 될 듯합니다.
- 제게는 3장의 근사 검색과 4장 예제의 후보 선추출이, 가까운 몇 개와 빠짐없는 명단의 차이를 설명할 말로 남은 것 같습니다.
- 저장소 결정은 요건 표로 했고 실측은 남아 있습니다. 이 책은 그 선택을 말로 풀어내는 데 보탬이 된 듯합니다.
AI 쪽 서랍 하나를 열어보려던 참이었다
벡터 DB는 검색 증강 생성(RAG)을 위해 필요한 문서를 찾아 모델에 건네주는 저장소라고 생각했다. 사내 문서 검색을 막연히 떠올리며 백엔드 개발자로서 그 원리를 알아두고 싶어 신청하게 됐다.
지난 서평 『에이전트 시대의 AI 시스템 설계』의 ② '지식 더하기' 서랍에는 인덱스와 시맨틱 인덱싱 같은 검색 인프라를 담았다. 이번에는 그 서랍 하나를 꺼내 살펴보려 했다. 책은 니틴 보르완카르가 쓰고 정영균이 옮긴 한빛미디어의 『벡터 데이터베이스』로, ‘SQLite, PostgreSQL로 직접 만드는 시맨틱 검색과 RAG’라는 부제를 달고 있다.
책을 펴기 전, 나 스스로에게 던진 세 가지 질문
- Q1. 벡터가 만들어지고, 저장되고, 검색되는 원리를 설명할 수 있게 될까?
- Q2. 익숙한 SQL 위에서 내 백엔드 업무에 붙일 감각을 얻을 수 있을까?
- Q3. 여기서 얻은 것을 팀에서 함께 쓸 공용어로 옮길 수 있을까?
이번 과업에서 책을 다시 펼치게 됐다
나는 백엔드 개발자이고 이번에 맡은 과업은 메시징 플랫폼의 세그먼트 타게팅이었다. 최근 30일 안에 충전했고 푸시 수신에 동의했으며 특정 지역에 사는 사람처럼 조건을 만족하는 집단에만 메시지를 보내는 기능을 말한다.
필요한 결과는 두 가지였다. 화면에서 조건을 조합하면 대상 수가 수 초 안에 보여야 했고 발송 시점에는 정확한 회원 ID 명단이 나와야 했다. 화면의 숫자와 명단은 같은 상태에서 나오는 것이 조건이었다.
Druid와 ClickHouse가 생소해 라인의 플랫폼 경계, 채널톡의 마케팅 타게팅용 ClickHouse 도입, AB180의 브레이즈 소개를 찾아봤다. 가볍게 들은 하용호 님의 강의 『AI시대 데이터 직군을 위한 생존 전략』 3강에서 전용 벡터 DB보다 PostgreSQL 확장인 pgvector를 권하는 대목을 만났다. 신청한 책의 5·7장이 pgvector를 다룬다는 것이 떠올라 다시 깊게 보게 됐다.
책은 원리에서 검색, RAG로 이어진다
1~3장은 임베딩에서 FAISS·양자화·근사 최근접 이웃(ANN)·HNSW로 이어지는 원리를 다룬다. 4~8장은 SQLite3와 PostgreSQL 위에서 수집→임베딩→인덱스→검색의 뼈대를 반복하고 뒤에서 RAG로 연결하며, 재료는 레딧 댓글에서 논문을 지나 대화로 바뀐다. 9장은 벡터 쿼리 언어(VQL)를 다룬다. 앞 장의 흐름을 기억하며 달라지는 부분을 짚어 읽기에 괜찮은 구성이라고 생각한다. 교보문고에 실린 출판사 리뷰의 '사용법이 아니라 원리를 배우다'라는 소개처럼, 무료 도구로 로컬에서 원리와 실습을 이어갈 수 있는 책이다.
가까운 몇 개와 빠짐없는 명단은 다른 답이다
3장에서는 ANN을 실제 가까운 이웃을 찾아내는 비율인 재현율 일부를 내주고 속도를 얻는 접근이라고 설명한다. 추천 몇 개라면 후보 하나를 놓쳐도 목적을 이룰 수 있지만 발송 명단에서는 해당자가 빠지거나 수신 거부자가 섞여서는 안 된다. Druid의 스케치도 고유 사용자 수를 추정할 수는 있어도 정확한 회원 ID 명단을 복원할 수는 없다. 양자화는 벡터의 손실 압축이고 비트맵은 정수 ID 집합의 무손실 압축이니, 사진 해상도를 낮추는 일과 명단을 잃지 않고 접어두는 일의 차이에 빗댈 수 있다고 생각한다.
한국어판 예제 저장소의 4.8.2절은 기본값으로 요청 개수의 10배 후보를 먼저 뽑고 메타데이터 조건으로 거른다. 주석은 여러 sqlite-vss 버전에서 필터를 최근접 이웃 검색 안으로 밀어 넣지 못하기 때문이라고 설명한다. 후보를 넉넉히 뽑아도 조건이 까다로우면 남는 결과는 줄고 후보 밖의 해당자는 보이지 않는다. pgvector 문서의 Filtering 절도 근사 인덱스는 탐색 뒤 필터가 적용되므로, 조건에 맞는 행이 10%면 기본 `hnsw.ef_search` 40에서 평균 4건만 남는다고 안내한다. 내 세그먼트에서는 조건 자체가 질문이므로 해당자를 전부 찾아야 했다.
먼저 읽은 다른 서평(Owl's Repository)에서 질문과 닮았어도 이미 폐지된 규정은 현재의 판단 근거로 쓸 수 없다는 예시를 읽었다. 내 과업에서도 수신 거부자는 행동 조건에 가까워도 대상에서 빠져야 한다. 유사도 순위 조정으로 대신할 수 없는 조건이라고 생각한다. 이렇게 보면 내 문제는 순위 없이 대상 조건만 남은 검색에 가깝지 않을까 싶다.
그래서 조건별 회원 집합을 ClickHouse의 비트맵으로 들고 겹치는 방향을 골랐다. 같은 집합 상태에서 개수와 명단을 꺼내려는 접근을 택한 셈이다. 실제 발송에는 그 교집합 상태를 확정하고 회원 ID를 추출하는 과정이 필요하다.
비슷한 것을 찾는 질문과, 빠짐없이 찾는 질문. 같은 '검색'이라는 말 안에 서로 다른 약속이 들어 있었다고 생각한다.
RAG 쪽 장에서 건진 것
6장의 하이브리드 검색은 점수 체계가 다른 결과를 합치며, 한국어판 예제 저장소 6.3절은 BM25 점수를 정규화한 뒤 기본 설정으로 의미 점수에 0.7, 키워드 점수에 0.3을 곱해 더한다. 7장 예제의 7.6절은 청크로 찾은 결과를 논문 단위로 묶어 돌려준다. 여기서 나는 원천별 기준 시각을 맞추고 여러 충전 이벤트를 회원 단위로 묶되 ‘최근 30일’을 다시 계산할 충전 시각을 남기는 일을 떠올렸다.
요건별로 후보의 충족 여부를 표로 따져 ClickHouse를 직접 운영하는 방향을 잡았고 가장 가까운 대안은 Redshift Serverless로 남겼다. 아직 개념 검증(PoC)은 거치지 않았다. 책이 준 것은 결정 자체보다 '근사로 충분한 질문인가, 빠짐없어야 하는 질문인가'처럼 선택의 이유를 설명할 말이라고 생각한다.
처음의 세 질문에, 지금 답해본다면
Q1. 벡터가 만들어지고 검색되는 원리를 설명할 수 있게 되었는가.
: 약간은 그렇게 되었다고 생각한다. 임베딩→인덱스→검색→RAG의 흐름은 조금 설명할 수 있지만 실제 데이터로 HNSW를 튜닝하는 일은 남아 있다.
Q2. 익숙한 SQL에서 내 업무에 붙일 감각을 얻었는가.
: 예상과 다른 방향으로 쓰임을 찾았다고 생각한다. RAG보다 세그먼트 저장소를 가르는 기준으로 쓰였다.
Q3. 팀에서 함께 쓸 공용어로 옮길 수 있는가.
: 가능성은 있다고 생각한다. 저장소 이름에 앞서 어느 정도 누락을 허용하는 질문인지 이야기할 수 있을 것 같다.
이런 분께는 맞을 수도, 이런 분께는 조금 덜 맞을 수도
익숙한 SQLite3와 PostgreSQL로 벡터 검색 원리를 따라가고 싶은 백엔드 개발자에게는 맞을 수도 있다고 생각한다. 특정 상용 제품의 운영 가이드나 대규모 분산 검색의 운영 경험을 기대한다면, 로컬 실습 중심인 이 책은 조금 덜 맞지 않을까 싶다.
마무리
벡터 DB를 배우러 들어갔다가 질문의 모양을 더 오래 생각하게 됐다. 어떤 답이 필요한지 먼저 정의하는 일이 저장소 선택의 출발점이라고 생각한다. AI 쪽 서랍 하나를 열려던 책이 저장소 서랍 전체를 돌아보게 한 것 같다.
유사 사용자(look-alike) 타게팅은 다음 서랍으로 남겨두려 한다. 언젠가 '이 사람들과 비슷한 사용자'를 찾게 된다면, 처음 기대했던 방향으로 이 책을 다시 펼치게 될지도 모르겠다.
'Books. > 2026년' 카테고리의 다른 글
| 책 읽고 싶어서 회사를 그만뒀습니다 (0) | 2026.09.27 |
|---|---|
| 트랜스포머 아키텍처로 배우는 AI 에이전트 with 랭체인 & 랭그래프 (0) | 2026.07.06 |
| 에이전트 시대의 AI 시스템 설계 (0) | 2026.05.31 |
| 맛있는 디자인 일러스트레이터 CC 2026 (5) | 2026.04.28 |
| 이것이 Spring AI다 (0) | 2026.04.01 |