임베딩 검색 기능 도입기(1)
#1. 10년 전이랑 똑같이 검색해도 괜찮을까?
블로그를 처음 만들때는 그냥 제목, 본문검색 기능만 붙여서 검색 기능을 구현했다. 근데 포스팅을 하면 할 수록 이전에 썼던 포스팅을 수정하는 일이 잦아졌고, 카테고리와 태그 기능만으로는 원하는 포스팅을 찾기 어려운 경우가 종종 발생했다. 좀 더 나은 방법이 있지 않을까?
유투브나 구글 검색을 하다보면, 개떡같이 검색해도 찰떡같이 내가 원하는 데이터를 뽑아주니까, 뭔가 방법이 있을것이다.
클로드와 함께라면, 나도 한번 해볼 수 있지 않을까?
방법을 찾아보기로 했다.
#2. 임베딩 검색?
딸깍이 한테 물어보니 임베딩 검색이라는 게 있었다. 오며가며 한번씩 들어봤었다.
임베딩 검색이 무엇인지 아주 간단하게 설명하자면, 문장을 숫자로 바꿔서 의미가 비슷한 문장끼리 찾아주는 방식이다. (문장을 숫자로 바꾼다는 생각을 누가 했을까.)
예를 들어 리액트 상태관리라는 검색어가 있고, 어떤 글에는 Zustand로 상태 관리하기라는 내용이 있다고 해보자.
기존 LIKE 검색이라면 두 표현이 다르기 때문에 찾지 못할 수 있다.
하지만 임베딩 검색에서는 두 문장이 의미적으로 비슷하다는 것을 이용해서 해당 글을 찾아낼 수 있다.
내가 원했던 게 딱 이런 검색이었다.
정확히 같은 단어가 들어있는 글뿐만 아니라, 내가 찾으려고 하는 내용과 비슷한 글까지 찾아주는 것.
#3. 뒷걸음질 치다가 고장난 시계가 맞아버린 썰푼다.ssul
임베딩 검색을 구현하려면 몇 가지가 필요하다.
텍스트를 숫자로 바꿔줄 임베딩 모델이 필요하고, 그렇게 만들어진 벡터를 저장할 공간도 필요하다.
그리고 검색할 때 검색어를 벡터로 바꾼 다음, 저장되어 있는 벡터 중에서 가장 비슷한 것을 찾아야 한다.
그리고 마침맞게 지금 사용하고 있는 데이터베이스가 Neon DB라는 게 여기서 꽤 잘 맞았다.
Neon은 PostgreSQL을 사용하고 있고, PostgreSQL에는 pgvector라는 확장이 있다.
이걸 사용하면 기존 PostgreSQL 데이터베이스에 벡터를 저장하고 유사도 검색을 할 수 있다.
새로운 데이터베이스를 하나 더 운영하지 않아도 된다.
게다가 지금 PorkLog의 상황도 나쁘지 않았다.
글이 수만 개 있는 것도 아니고, 여러 명이 동시에 글을 작성하는 서비스도 아니다.
글을 쓰는 사람은 나 하나고, 많아봐야 일주일에 몇 개 정도다.
그러니 글을 저장할 때 임베딩을 한 번 더 생성하는 정도의 작업은 충분히 감당할 수 있을 것 같았다.
지금 내 블로그의 규모라면 임베딩 검색을 한번 붙여보기에 꽤 적당한 조건이었다.
#4. 그래서 어떻게 만들 것인가
일단 기존 검색을 없애지는 않기로 했다.
LIKE 검색은 정확한 단어나 기술명을 찾을 때 여전히 유용하다.
useState, pnpm, Drizzle 같은 단어를 검색했을 때는 오히려 정확하게 해당 단어가 들어있는 글을 찾아주는 게 더 좋다.
그래서 기존 검색은 그대로 두고, 임베딩 검색을 보조 검색으로 붙이기로 했다.
검색 과정은 단순하게 생각했다.
- 기존 검색으로 정확하게 일치하는 글을 찾는다.
- 검색어를 임베딩해서 비슷한 글을 찾는다.
- 기존 검색에서 찾은 글과 중복되는 결과를 제외한다.
- 남은 결과를 기존 검색 결과 뒤에 붙인다.
그리고 글 전체를 하나의 벡터로 만들지는 않기로 했다.
기술 블로그 글 하나에 여러 주제가 섞여 있을 수 있기 때문이다.
그래서 Markdown의 ## 헤딩을 기준으로 글을 여러 조각으로 나누고, 각각을 하나의 청크로 만들어 임베딩하기로 했다.
이렇게 하면 검색 결과가 단순히 "이 글이 관련 있다"에서 끝나는 게 아니라, 글의 어느 부분이 관련 있는지도 알 수 있다.
일단 이 정도면 충분해 보였다.
거창한 검색엔진을 만드는 게 목적은 아니다.
내 블로그에서 대충 검색해도 조금 더 그럴듯한 결과를 찾아주는 것.
그걸 한번 만들어보기로 했다.