Skip to main content

Command Palette

Search for a command to run...

자바스크립트로 만든 오프라인 시맨틱 검색 엔진: DB·API 없이 빠르고 간편하게 구현하는 방법

Published
•3 min read•View as Markdown

# 서론 – “검색이 느려요?” 라는 문제를 해결하고 싶다면

웹 서비스나 데스크톱 애플리케이션을 개발할 때, 검색 기능은 거의 필수입니다. 그런데 서버에 요청을 보내야 하거나, 별도의 데이터베이스를 구축해야 하는 경우가 많아 속도 지연과 운영 비용이 발생합니다. 특히 인터넷 연결이 불안정한 환경에서는 검색 자체가 불가능해질 수도 있죠.

이런 문제를 겪고 있던 개발자들이 최근 Reddit r/programming에 “I built an offline semantic search engine in JS (no DB, no APIs)” 라는 글을 올려 큰 관심을 모았습니다. 이번 포스트에서는 해당 프로젝트의 핵심 아이디어와 구현 방법을 초보자도 이해할 수 있도록 단계별로 풀어보겠습니다.


# 1. 시맨틱 검색이란? – 단어가 아닌 의미를 찾는다

전통적인 키워드 매칭 검색은 입력한 문자열과 정확히 일치하는 문서를 찾아줍니다. 하지만 사용자가 “노트북 배터리 오래 가는 법”이라고 검색했을 때, “배터리 수명 연장 팁” 같은 의미가 비슷한 문서를 놓치기 쉽습니다.

시맨틱 검색은 텍스트를 벡터(숫자 배열)로 변환해 의미적 거리를 계산함으로써, 문맥과 의미가 비슷한 결과를 반환합니다. 최근에는 BERT, Sentence‑Transformers 같은 사전 학습 모델이 주로 사용되지만, 이번 프로젝트에서는 브라우저에서 바로 실행 가능한 작은 임베딩 모델을 활용했습니다.


# 2. 오프라인 검색 엔진 구현 원리 – DB·API 없이도 가능한 이유

# 2‑1. 데이터 준비와 전처리

  1. 검색 대상 문서를 JSON 혹은 텍스트 파일 형태로 로컬에 저장합니다.

  2. 각 문서를 문장 단위로 나누고, 불용어(stop words)를 제거합니다.

# 2‑2. 임베딩 생성

  • @tensorflow/tfjs 와 @tensorflow-models/universal-sentence-encoder 를 사용해 문장 임베딩을 생성합니다.

  • 모델은 브라우저에 다운로드된 뒤 캐시되므로, 최초 실행 이후에는 네트워크 요청이 없습니다.

js

import * as use from '@tensorflow-models/universal-sentence-encoder';

async function getEmbeddings(sentences) {

  const model = await use.load();

  const embeddings = await model.embed(sentences);

  return embeddings.array();   // 2‑D 배열 반환

}

# 2‑3. 코사인 유사도 계산

검색어를 같은 방법으로 임베딩한 뒤, 저장된 문서 임베딩과 코사인 유사도를 구합니다. 가장 높은 점수를 가진 상위 N개의 문서를 결과로 반환합니다.

js

function cosineSimilarity(a, b) {

  const dot = a.reduce((sum, v, i) => sum + v * b[i], 0);

  const normA = Math.sqrt(a.reduce((s, v) => s + v * v, 0));

  const normB = Math.sqrt(b.reduce((s, v) => s + v * v, 0));

  return dot / (normA * normB);

}

# 2‑4. 인덱스 저장 방식

임베딩 결과는 IndexedDB 혹은 로컬 파일(.json)에 저장합니다. 이렇게 하면 페이지를 새로 고침하거나 애플리케이션을 재시작해도 데이터를 다시 계산할 필요가 없습니다.


# 3. 주요 코드와 핵심 포인트 – 초보자를 위한 상세 해설

| 단계 | 핵심 코드 | 설명 |

|------|-----------|------|

| 데이터 로드 | fetch('data.json').then(r => r.json()) | 로컬 JSON 파일을 비동기로 읽어옵니다. |

| 임베딩 캐시 | localStorage.setItem('embeddings', JSON.stringify(vec)) | 처음 생성된 임베딩을 브라우저 스토리지에 저장해 재사용합니다. |

| 검색 함수 | async function search(query){ … } | 사용자가 입력한 쿼리를 임베딩하고, 코사인 유사도로 정렬합니다. |

| UI 업데이트 | resultDiv.innerHTML = html | 검색 결과를 동적으로 화면에 표시합니다. |

팁: 개발 중 console.time() 과 console.timeEnd() 로 임베딩·검색에 걸리는 시간을 측정하면, 성능 최적화 포인트를 쉽게 찾을 수 있습니다.


# 4. 활용 사례와 확장 가능성 – 어디에 쓸 수 있을까?

  1. 오프라인 문서 뷰어 – 전자책, 매뉴얼, 사내 위키 등을 로컬에서 빠르게 검색.

  2. 교육용 앱 – 학생들이 교재 내용에 대해 질문하면 즉시 관련 문장을 찾아줍니다.

  3. 프라이버시‑중시 서비스 – 사용자 데이터가 서버에 전송되지 않아 개인정보 보호가 가능합니다.

추가 확장으로는

  • 다중 언어 지원 : multilingual‑sentence‑encoder 모델 교체

  • 벡터 압축 : PCA 혹은 OPQ 기법을 적용해 저장 용량 최소화

  • 검색 UI 개선 : 하이라이트, 페이징, 필터링 기능 추가

이러한 확장은 모두 자바스크립트와 브라우저 API만으로 구현 가능하므로, 별도의 백엔드 인프라가 필요 없습니다.


# 결론 – 직접 만들어보고 피드백을 남겨보세요!

이번 포스트에서는 오프라인 검색 엔진을 구현하는 전체 흐름을 살펴보았습니다. 핵심은 시맨틱 검색을 위한 임베딩 생성, 로컬 캐시, 코사인 유사도 계산이라는 세 가지 단계이며, 모든 작업을 자바스크립트만으로 처리한다는 점입니다.

프로젝트를 직접 클론하거나, 본인의 데이터에 맞게 변형해 보는 것을 강력히 권장합니다. 구현 과정에서 겪은 어려움이나 개선 아이디어가 있다면 댓글이나 깃허브 이슈에 남겨 주세요. 여러분의 피드백이 더 나은 오프라인 검색 솔루션을 만드는 데 큰 도움이 됩니다!

행동 유도: 지금 바로 코드를 다운로드하고, 로컬에서 시맨틱 검색을 체험해 보세요! 🚀


SEO 키워드: 오프라인 검색 엔진, 자바스크립트, 시맨틱 검색

More from this blog

개발자 vs AI: 연말에 되돌아보는 내가 되고 싶은 개발자 유형은 무엇일까?

# 서론 – 연말, 개발자의 일상이 왜 달라지는가 12월이 되면 많은 개발자들은 데드라인이 느려지고, 팀 채팅은 조용해지며, 풀 리퀘스트(PR)는 한가해지는 것을 경험합니다. “올해는 정말 바빴다”는 감회와 함께, 동시에 “다음 해에는 어떤 개발자가 되고 싶을까?” 하는 고민이 자연스럽게 떠오릅니다. 특히 최근 AI 기술이 급격히 발전하면서, “AI가 내 일을 빼앗을까?” 라는 불안감과 “AI와 협업하면 내 역량을 어떻게 강화할 수 있을까?” ...

Dec 29, 20255 min read

인사이트 부채(Insight Debt)란? 초보 개발자를 위한 쉬운 이해와 해결 방법

# 서론 – 왜 우리는 “인사이트 부채”에 주목해야 할까? 소프트웨어 개발 현장에서 인사이트 부채(Insight Debt) 라는 용어를 들어본 적이 있나요? 프로젝트를 진행하면서 “이 부분은 나중에 다시 살펴볼게” 라는 생각을 자주 하게 됩니다. 하지만 이런 미뤄진 고민이 쌓이면 결국 코드 품질 저하, 버그 증가, 팀 커뮤니케이션 비용 등 다양한 문제를 야기합니다. 이번 포스트에서는 인사이트 부채가 정확히 무엇인지, 왜 발생하는지, 그리고 초보...

Dec 29, 20253 min read

블록체인 활용: 일상 속 5가지 실생활 적용 사례와 시작 가이드

# 서론 – “블록체인, 우리 생활에 뭐가 바뀔까?” 최근 뉴스와 SNS에서 블록체인이라는 단어를 자주 보지만, 실제로 우리 주변에 어떻게 적용되고 있는지는 잘 모르는 경우가 많습니다. “복잡하고 비싸다”는 인식이 남아 있지만, 최근는 블록체인 활용이 점점 쉬워지고 비용도 낮아져 일반인도 직접 체험할 수 있는 시대가 왔습니다. 이번 글에서는 블록체인 적용 사례 5가지를 중심으로, 초보자도 바로 이해하고 시작할 수 있는 방법을 알려드립니다. #...

Dec 29, 20254 min read

스크래핑 온 더 엣지: SvelteKit으로 만든 초고속 만화 뷰어 만들기 (50자)

# 서론 – “만화 읽기가 이렇게 불편할 수 있을까?” 우리 모두가 한 번쯤 겪어본 일입니다. 만화를 읽고 싶을 땐 원하는 사이트가 로딩이 오래 걸리거나, 페이지를 넘길 때마다 광고가 끊임없이 뜨고, 이미지가 깨져서 눈이 피로해지는 경우가 있죠. 이런 불편함을 그냥 견뎌야 할까요? 개발자라면 “코드로 해결할 수 있지 않을까?” 하는 생각이 떠오릅니다. 이번 포스트에서는 SvelteKit과 웹 스크래핑을 활용해, 초고속 만화 뷰어를 직접 만드는 ...

Dec 29, 20254 min read

Tim van der Lippe가 Mockito 유지보수자를 떠나다 – Java 테스트 생태계에 미치는 영향은?

SEO 키워드: Mockito, Java 단위 테스트, 테스트 자동화 # 서론 – “왜 이 소식이 중요한가?” 최근 Reddit의 r/programming 커뮤니티에 올라온 글에서 Tim van der Lippe가 오랜 기간 담당해 온 Mockito 프로젝트의 유지보수자를 물러난다는 소식이 전해졌습니다. Mockito는 Java 개발자 사이에서 가장 널리 쓰이는 테스트 자동화 라이브러리 중 하나이며, 수많은 오픈소스 프로젝트와 기업 서비스의 ...

Dec 29, 20253 min read2
H

HA JIN-YOUNG's blog

967 posts