"둘 다 텍스트를 받아 코퍼스의 텍스트를 돌려주지만 같은 기능은 아냐. 허용되는 시간이 완전히 다르거든."
닮은 입력이 같은 일을 뜻하지 않아
검색은 '장기 투자에 관한 구절을 찾아줘'처럼 관련된 내용을 묻고, 자동완성은 '장기 투자자가 물어야 할 유일한 질문…' 다음에 아빠가 실제로 쓴 말을 묻는 기능이야. 둘 다 문자열을 받아 같은 코퍼스에서 문자열을 내놓지만, 하나는 관련성을 찾고 다른 하나는 정확한 연속을 찾지.
기다릴 수 있는 시간이 다르게 흐른다
검색은 사람이 문장을 입력하고 엔터를 누른 뒤 결과를 기다려. 수백 밀리초 동안 키워드와 벡터를 함께 찾고 두 순위를 합친 뒤 선택적으로 rerank할 여유가 있어. 자동완성은 글자 하나를 칠 때마다 실행돼. 수십 밀리초 안에 답하지 못하면 손가락보다 늦게 따라와 오히려 방해가 되지. 네트워크 왕복 한 번조차 예산을 써버릴 수 있어.
사서와 메아리는 다른 일을 해
검색은 코퍼스 어디에 있든 질문과 가장 관련 있는 구절을 순위로 보여주는 사서야. 자동완성은 현재 문구가 과거 글에서 실제로 어떻게 이어졌는지 들려주는 메아리고. 한 구현으로 둘을 해결하려 하면 자동완성에는 너무 무겁고 검색에는 너무 얕은 타협이 생겨.
아래는 공유하고 계약은 나눠
두 경로는 같은 코퍼스와 chunk, 로컬 인덱스를 함께 써. 하지만 /api/search는 벡터와 rerank를 위해 원격 서비스에 닿을 수 있고 부족한 기능을 응답에 표시해. /api/complete는 로컬 기계를 떠나지 않아. 코드가 일부 겹쳐도 두 계약을 나눈 것은 중복이 아니라, 검색 한 번과 키 입력 한 번이 같은 사건이 아니라는 사실을 정직하게 반영한 설계야.
저장소를 공유한다고 실행 경로까지 하나여야 하는 것은 아니야. 같은 chunk와 FTS 인덱스를 밑에서 함께 쓰면서도 검색은 품질을, 자동완성은 즉시성을 우선할 수 있어. 공통 기반은 재사용하고 사용자에게 약속하는 시간과 실패 방식은 따로 두는 것이 이 분리의 핵심이야.