블로그 기능(클랩 토글, 조회수, 공유, 댓글, 목차, 다크모드)을 확인하기 위한 글입니다. 스크롤을 천천히 내리면서 오른쪽 목차의 하이라이트가 섹션을 따라 이동하는지 확인하세요.

첫 번째 섹션

목차 하이라이트 동작을 확인하기 위한 본문입니다. 검색 엔진의 첫 단계는 문서를 수집하는 일입니다. 크롤러는 시작 URL 목록에서 출발해 페이지를 내려받고, 그 안의 링크를 큐에 추가하며 웹을 넓혀갑니다. 이 과정에서 같은 페이지를 두 번 수집하지 않도록 방문 기록을 관리하고, 서버에 부담을 주지 않도록 요청 간격을 조절합니다.

수집된 문서는 저장소에 쌓입니다. 저장소는 단순한 파일 시스템일 수도 있고, 대규모 서비스라면 분산 저장소일 수도 있습니다. 중요한 것은 이후 단계인 색인기가 문서를 순회하며 읽을 수 있는 형태로 정리되어 있다는 점입니다.

문서 하나하나에는 고유한 식별자가 부여됩니다. 이 식별자는 역색인에서 포스팅 리스트의 항목이 되어, 어떤 단어가 어떤 문서에 등장했는지를 가리키는 좌표 역할을 합니다.

하위 섹션 1-1

h3는 목차에서 들여쓰기로 표시됩니다. 한국어 문서를 다룰 때 가장 먼저 부딪히는 문제는 토크나이징입니다. 영어는 공백으로 단어를 나누면 대체로 충분하지만, 한국어는 조사가 단어에 붙어 있어 “네이버를”, “네이버가”, “네이버의”가 모두 다른 문자열이 됩니다. 이 문제를 해결하지 못하면 “네이버”로 검색했을 때 이 문서들을 찾지 못합니다.

가장 단순한 접근은 음절 n-gram입니다. 문자열을 2~3글자 단위로 잘라 색인하는 방식으로, 사전 없이도 동작하지만 관련 없는 문서가 함께 검색되는 부작용이 있습니다.

두 번째 섹션

역색인은 검색 엔진의 핵심 자료구조입니다. 일반 문서가 “문서 → 단어 목록” 방향이라면, 역색인은 “단어 → 문서 목록” 방향으로 뒤집은 것입니다. 사용자가 검색어를 입력하면 엔진은 문서 전체를 훑는 대신 역색인에서 해당 단어의 포스팅 리스트만 꺼내면 됩니다.

// 역색인의 개념적 구조
std::unordered_map<std::string, std::vector<int>> inverted_index;
inverted_index["검색"] = {1, 5, 12, 40};
inverted_index["엔진"] = {5, 12, 33};

두 단어를 모두 포함하는 문서를 찾으려면 두 포스팅 리스트의 교집합을 구합니다. 리스트가 정렬되어 있다면 투 포인터로 효율적으로 병합할 수 있습니다.

색인은 검색 속도를 위해 쓰기 비용을 지불하는 트레이드오프입니다.

하위 섹션 2-1

디스크 직렬화도 중요한 주제입니다. 메모리에 만든 역색인을 파일로 저장할 때는 포스팅 리스트를 연속된 바이너리로 기록하고, 단어 사전에는 각 리스트의 파일 내 오프셋을 함께 저장합니다. 검색 시에는 사전만 메모리에 올리고 포스팅 리스트는 필요할 때 해당 오프셋에서 읽어오는 구조가 일반적입니다.

압축도 고려 대상입니다. 문서 번호를 그대로 저장하는 대신 이전 번호와의 차이(델타)를 저장하면 숫자가 작아지고, 가변 길이 인코딩과 결합하면 색인 크기를 크게 줄일 수 있습니다.

세 번째 섹션

마지막 섹션입니다. 랭킹은 검색어와 문서의 관련도를 점수로 매기는 단계입니다. 고전적인 TF-IDF는 “이 문서에 이 단어가 자주 나오는가(TF)”와 “이 단어가 흔한 단어는 아닌가(IDF)”를 곱해 점수를 냅니다. BM25는 여기에 문서 길이 보정과 빈도 포화를 더한 개선판으로, 현대 검색 엔진 대부분의 기본 랭킹 함수입니다.

목차의 마지막 항목이 하이라이트되는지, 그리고 목차를 클릭해 위로 되돌아갈 때 부드럽게 스크롤되는지 확인하세요.