Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Upstash Redis Search 미리보기: Redis 데이터를 위한 강력하고 확장 가능한 검색

저희는 1~2주 후에 Upstash Redis Search를 정식 출시할 예정입니다. 아직 출시 전이지만, 지금 만들고 있는 것이 무엇인지, 그리고 왜 이 제품이 기대되는지 몇 가지 이야기를 미리 나눠보려고 합니다.

왜 이 제품을 만들게 되었나

저희는 2024년 Upstash Vector부터 검색 분야에서 활동을 시작했습니다. Vector는 개발자들이 시맨틱 검색을 구현할 수 있도록 도와주었고, 이후에는 Upstash Search라는 제품으로 이 분야에 더 깊이 투자하게 되었습니다.

예를 들어 아래는 2025년 벡터 기반 시맨틱 검색 솔루션인 Upstash Search를 발표했던 트윗입니다 👇

Upstash Redis Search 미리보기: Redis 데이터를 위한 강력하고 확장 가능한 검색

하지만 저희는 Search에서 얻은 경험과 교훈을 바탕으로 한 단계 더 나아갈 수 있다고 생각합니다.

사실 저희는 기존 검색 서비스 대부분에 크게 만족하지 못했습니다. 개인적으로도 어느 것 하나 서버리스 환경에 잘 어울리는 느낌이 없었는데요, 그만큼 갈증이 컸던지 2023년에는 AWS CloudSearch 기반으로 직접 검색 앱을 만들어 쓰기까지 했습니다 💀

저희가 원했던 것은 다음과 같습니다:

  • Redis 안에서 동작하는 것 — Redis는 그만큼 빠르니까요
  • Upstash Redis SDK와 함께 작동하는 것
  • 100% 타입 세이프(type-safe)한 것
  • 실시간 입력 검색(search-as-you-type)이 가능할 만큼 빠른 것

그래서 직접 만들기로 했습니다.

Redis API를 넘어서는 첫 번째 확장

이번 출시는 저희에게 의미가 큽니다. 지금까지 @upstash/redis는 Redis 명령어와 거의 1:1로 매핑되어 왔는데, Search는 그 범위를 넘어선 첫 번째 확장 기능입니다.

내부적으로는 Rust로 작성된 풀텍스트 검색 엔진인 Tantivy를 사용합니다. Apache Lucene에서 영감을 받은 이 엔진은 속도가 정말 빠르며, 토크나이제이션(tokenization), 스테밍(stemming), 퍼지 매칭(fuzzy matching), 구문 쿼리(phrase query), BM25 스코어링 등 검색에 필요한 모든 기본 요소들을 제공해줍니다.

목표는 이 기능이 SDK와 Upstash Redis의 자연스러운 일부처럼 느껴지도록 만드는 것입니다. 현재 @upstash/redis를 사용 중이라면, 검색 기능 추가가 별도 제품을 새로 도입하는 느낌이 아니라 자연스러운 확장처럼 다가올 것입니다.

타입 세이프 스키마 빌더

개인적으로 특히 마음에 드는 부분은 새로운 스키마 빌더입니다. zod와 유사한 API로 검색 대상 필드를 정의할 수 있습니다:

import { Redis, s } from "@upstash/redis";
 
const redis = Redis.fromEnv();
 
const schema = s.object({
 name: s.string(),
 description: s.string(),
 sku: s.string().noTokenize(),
 brand: s.string().noStem(),
 price: s.number(),
 inStock: s.boolean(),
});
 
const products = await redis.search.createIndex({
 name: "products",
 dataType: "json",
 prefix: "product:",
 schema,
});

.noTokenize().noStem() 메서드를 사용하면 텍스트가 처리되는 방식을 직접 제어할 수 있습니다:

  • 토크나이제이션(Tokenization)은 텍스트를 검색 가능한 단어 단위로 분리합니다. 자연어에는 적합하지만 SKU 같은 코드에는 문제가 됩니다(SKU-12345-BLK["SKU", "12345", "BLK"]로 쪼개짐). 코드, 이메일, UUID 등에는 비활성화하는 것이 좋습니다.
  • 스테밍(Stemming)은 단어를 어근 형태로 줄여서 "running"이 "run"과 매칭되도록 합니다. 브랜드명이나 고유명사처럼 정확한 일치가 필요한 경우에는 비활성화하세요.

스키마 덕분에 쿼리에서도 완전한 타입 추론이 가능합니다. 존재하지 않는 필드를 쿼리하려고 하면 TypeScript가 즉시 오류를 잡아줍니다. 스키마 문법도 zod 문법과 최대한 비슷하게 설계해서 익숙하게 사용할 수 있도록 했습니다.

쿼리 프리미티브(기본 연산자)

대부분의 검색 사용 사례를 충분히 커버할 수 있다고 판단한 5가지 주요 연산자와 함께 출시됩니다:

$smart: 스마트 매칭

$smart 연산자는 스마트 매칭을 자동으로 적용합니다. 별도 설정 없이도 잘 동작하도록 설계되어 있어 초보자가 시작하기에 가장 좋은 선택입니다.

await products.query({
 filter: {
 name: { $smart: "wirless headphones" },
 },
});

내부적으로는 다음 단계들이 실행됩니다:

  1. 정확한 구문 매칭(가장 높은 가중치) — "wireless headphones"가 인접하고 순서대로 포함된 문서
  2. slop을 허용한 구문 매칭(중간 가중치) — 단어가 순서대로 나오지만 인접하지 않은 문서 (예: wireless bose headphones)
  3. 단어(Terms) 매칭(중간 가중치) — 모든 단어를 포함하되 순서는 무관한 문서
  4. 퍼지 매칭(가중치 없음) — "wireles headphone"처럼 오타가 포함된 문서
  5. 마지막 단어에 대한 퍼지 접두사 매칭(가중치 없음) — 실시간 입력 검색 상황을 위한 기능

이 점수들을 종합하여 가장 관련성 높은 결과를 반환합니다. 대부분의 검색창에서는 사실상 이 연산자 하나면 충분합니다. 물론 이 연산자는 아래의 기본 연산자들로 구축되어 있기 때문에, 직접 조합하고 설정을 조정해볼 수도 있습니다.

$eq: 정확한 일치

정확한 매칭이 필요한 필드에 사용합니다:

await products.query({
 filter: {
 name: { $eq: "wireless headphones" },
 price: { $eq: 200 },
 },
});

$phrase: 구문 매칭

단어들이 반드시 인접하고 순서대로 나타나야 할 때 사용합니다:

await products.query({
 filter: { description: { $phrase: "noise cancelling" } },
});

slop 옵션을 추가하면 단어 사이에 다른 단어가 끼어 있는 경우도 허용할 수 있습니다:

await products.query({
 filter: {
 description: {
 $phrase: { value: "wireless headphones", slop: 2 },
 },
 },
});

$fuzzy: 오타 허용

오타 허용 범위를 설정할 수 있는 퍼지 매칭입니다 (예: 오타 2개 허용):

await products.query({
 filter: { name: { $fuzzy: "headphonse", distance: 2 } },
});

$regex: 패턴 매칭

정규식 패턴이 필요할 때 사용합니다:

await products.query({
 filter: { sku: { $regex: "SKU-[0-9]{5}-.*" } },
});

한 가지 참고할 점: regex는 .noStem()이 적용된 필드에서 가장 잘 동작합니다. 스테밍된 텍스트는 기대한 패턴과 일치하지 않을 수 있습니다.

특정 필드에 가중치(Boost) 적용하기

boost를 적용하면 특정 매칭에 더 높은 가중치를 부여할 수 있습니다:

await products.query({
 filter: {
 $and: [
 { name: { $smart: "wireless", $boost: 2 } },
 { description: { $smart: "wireless" } },
 ],
 },
});

이렇게 하면 name 필드 매칭이 description 필드 매칭보다 두 배의 가중치를 갖게 됩니다. 제목 매칭을 본문 매칭보다 상위에 랭킹시키고 싶을 때 유용합니다.

앞으로의 계획

이 글에서 소개한 내용들은 아직 변경될 여지가 있습니다. 현재 세부 사항을 다듬고 문서를 작성하는 중이며, 정식 출시는 1~2주 후입니다.

그래도 이렇게 함께 미리 살펴볼 수 있다는 점이 정말 멋진 것 같습니다 👀

출시 후 검토할 만한 몇 가지 방향:

  • 벡터 검색 통합 (시맨틱 + 키워드 하이브리드 검색)
  • 자동 완성 및 추천(suggestions)

얼리 액세스를 원하시거나 궁금한 점이 있다면 @joshtriedcoding으로 연락해 주세요.

읽어주셔서 감사합니다 🙌