Post

로컬·무료로 동작하는 문서검색 서버를 만들어보자

검색할 때마다 외부 API를 호출하면 비용이 쌓이고 오프라인에서는 아예 못 쓴다. 모델을 한 번만 로컬로 내려받아 이후 전 과정을 CPU에서 완전히 오프라인으로 돌렸다

로컬·무료로 동작하는 문서검색 서버를 만들어보자

로컬·무료로 동작하는 문서검색 서버를 만들어보자

검색에 매번 외부 API(임베딩·LLM 호출)를 쓰면 질의 한 번마다 비용이 쌓이고, 네트워크가 끊기면 검색 자체가 멈춘다. 임베딩·리랭커 모델을 로컬 ONNX로 한 번만 내려받아 그 뒤부터는 전 과정을 오프라인으로 돌렸다.

1. 서론 (Introduction)

  • 문제/상황 (Problem): 검색 질의마다 임베딩 API·LLM API를 호출하면 사용량에 비례해 비용이 쌓인다. 오프라인 환경(네트워크 제한·이동 중)에서는 검색 자체가 불가능해진다.
  • 목적 (Purpose): 모델을 로컬로 내려받는 최초 1회만 네트워크를 쓰고, 이후 검색·임베딩 전 과정을 CPU 로컬에서 완결시킨다.
  • 대상 (Target Audience): 반복 검색 비용을 없애고 오프라인에서도 동작하는 검색 도구가 필요한 개발자.

2. 방법 및 과정 (Methods & Process)

  • 배경 조사 및 데이터 (Data Collection): 검색 파이프라인은 크게 수집(acquire)·임베딩·질의 세 단계로 나뉜다. 이 중 수집 단계만 원천 데이터를 가져오는 네트워크 호출이 필요하고, 임베딩·질의는 모델만 로컬에 있으면 네트워크가 필요 없다는 걸 확인했다.

    단계네트워크 필요 여부비고
    모델 다운로드(최초 1회)필요ONNX 임베딩·리랭커 모델
    수집(acquire)필요원천 데이터 소스에서 가져올 때만
    임베딩·질의불필요로컬 ONNX 런타임, CPU
  • 접근 방법 (Approach Methods):
    • [방법 1]: 임베딩·리랭커 모델을 ONNX 포맷으로 로컬에 한 번만 내려받는다. 이후 추론은 전부 로컬 CPU에서 실행되므로 API 키·과금·요청 한도가 필요 없다.
    • [방법 2]: 메모리 예산을 16GB 이내로 못 박고 설계한다. 모델과 인덱스를 동시에 메모리에 올려도 이 한도를 넘지 않는 범위에서 인덱스 구조·배치 크기를 정한다.
  • 분석 및 해결 프로세스 (Analysis Flow):
    • 도구/기술: ONNX 런타임, psutil(또는 표준 라이브러리 resource), 인메모리 인덱스.
    • 주요 단계: 모델 최초 1회 로컬 다운로드 $\rightarrow$ 수집 단계에서 원천 데이터 확보 $\rightarrow$ 이후 임베딩·검색은 네트워크 소켓 없이 로컬 CPU에서만 수행.
    • 결과 도출 및 검증: 검색 함수를 실행하는 동안 socket.socket을 의도적으로 막아서, 소켓이 하나라도 열리면 즉시 예외가 나도록 만든 뒤 질의를 실행했다. 예외 없이 정상 응답이 왔고, 이는 이 구간에서 네트워크 호출이 실제로 0건이라는 뜻이다.

3. 결과 (Results)

  • 분석 결과 요약: 정확한 지연시간·정확도 벤치마크 수치는 없다. 확인한 건 정성적 사실 두 가지다 — (1) 수집 단계 이후로는 네트워크 호출이 구조적으로 발생하지 않는다는 것, (2) 메모리 사용량이 16GB 이내로 설계됐다는 요건뿐이다. 솔직히 이 글은 “얼마나 빠른가”보다 “정말 외부 비용이 0인가”를 검증하는 데 초점을 맞췄다.

4. 인사이트 및 액션 (Insights & Action)

  • 인사이트 (Insight): “로컬로 돈다”는 주장은 직접 네트워크를 막아보기 전까지는 그냥 주장이다. 코드를 눈으로 봐서 API 호출이 없는 것 같다는 것과, 실제로 소켓이 열리지 않는다는 걸 강제로 확인하는 것은 신뢰 수준이 다르다. 이후 외부 공개를 준비하며 점검해보니 하드코딩된 개인 절대경로가 19곳이나 남아있었고, 버전 표기도 파일마다 서로 달랐고, 라이선스 파일 자체가 없었다 — 내 컴퓨터에서 로컬로 잘 도는 것과 남이 그대로 받아서 도는 것은 서로 다른 기준선이라는 걸 이 점검 과정에서 깨달았다.
  • 실행 방안 (Action Plan): “오프라인 동작”을 주장하는 기능은 socket 패치 같은 강제 검증을 최소 한 번은 통과시킨다. 눈으로 코드 리뷰하는 것만으로 끝내지 않는다.
  • 한 줄 결론 (Key Takeaway): 모델만 한 번 로컬로 내려받으면 이후 검색·임베딩은 API 비용 없이 CPU 로컬에서 완전히 오프라인으로 돈다. 샘플 코드
  • 다음 스텝 (Next Step): 지금은 “네트워크가 열리는가”만 확인했지, 로컬 CPU 추론의 실제 지연시간이 API 호출 대비 얼마나 느린지는 측정하지 않았다. 데이터 규모가 커졌을 때 이 로컬-온리 구조가 어느 지점부터 체감 지연으로 이어지는지 정량 측정이 남아 있다.
This post is licensed under CC BY 4.0 by the author.