이 글에서 다루는 것
서버리스로 배포되는 사이트에 전문 검색을 붙이면서, "무엇을 함수 안에 둘 수 있고 무엇을 밖에 둬야 하는가"를 정리했다. Elasticsearch처럼 항상 떠 있어야 하는 프로세스와, 요청마다 깨어나는 함수의 차이가 핵심이다.
1. 서버리스 검색 백엔드와 비동기 검색 UI
배경지식 (개념)
- 서버리스 함수: 요청마다 실행·종료되는 무상태 환경. 메모리에 상주하는 데몬을 그대로 올리기 어렵다.
- 역색인(inverted index): 단어 → 문서 id 목록. 빌드타임에 만들어 두고 함수에서 읽을 수 있다.
- Out-of-order 응답: 느린 이전 요청이 늦게 도착해 최신 결과를 덮어쓰는 UI 버그.
어떤 상황이었나
서버리스로 배포되는 사이트에 검색이 필요했다. 상주 검색 엔진 프로세스를 함수 안에 그대로 띄울 수는 없다. 외부 호스팅 없이 배포하려면 함수 메모리에서 동작하는 라이브러리형 인덱스가 필요했다.
클라이언트는 타이핑할 때마다 검색 API를 치므로, debounce·취소·최신 요청 우선 없이는 UI가 틀어질 수 있다.
목표는 네 가지였다 — 서버리스 안에서 동작하는 인메모리 역색인(검색 백엔드), 빌드 산출물 인덱스 파일이 콜드 스타트에서도 포함(배포 안정성), UI·테스트는 provider 계약만 보게 해 나중에 상주형 엔진으로 교체 가능(교체 가능성), 최신 결과만 화면에 반영(검색 UI).
핵심 작업
- 인덱스: 사전 빌드 파일을 함수에서 읽어 인메모리 역색인으로 검색했다.
- 번들: 런타임에 읽는 빌드 산출물을 번들 추적 설정에 명시해 콜드 스타트 누락을 해결했다. (막혔던 부분 — 콜드 스타트에서 인덱스 파일을 못 찾음 → 빌드 산출물을 함수 번들에 명시적으로 포함해 해결.)
- 계약: 클라이언트는 검색 provider 계약과 단일 API 엔드포인트만 의존하게 해서, 나중에 상주형 엔진으로 바꿔도 UI·테스트는 그대로 두도록 했다.
- UI: debounce·AbortController·최신 요청 id 패턴을 적용했다.
교훈
- 상주형 vs 라이브러리형 검색의 경계는 "메모리에 계속 떠 있어야 하는가" 다.
- 비동기 검색 UI는 debounce·취소·최신 요청 id를 함께 써야 한다.
- 콘텐츠 메타는 런타임 DB보다 단일 소스에서 빌드타임에 결정하는 편이 일관적이다.
- 후속: 한국어 검색 품질이 필요해지는 시점에 상주형 엔진 전환 기준 정리.
→ Serverless stateless execution · Inverted index full-text search · Race-safe async UI requests · Single source of truth content metadata