본문으로 건너뛰기

ADR-0017: 검색을 한 자리에 두고 절 제목과 인라인 코드로 색인한다

상태

Accepted

날짜

2026-08-13

맥락

사이트가 성격이 다른 두 종류의 글을 담고 있습니다. 블로그 글 4 편과 아키텍처 문서 39 개이며, 문서 쪽에 절이 534 개 있습니다. 목록 화면에 4 편이 전부 보이는 블로그와 사이드바 4 단계를 접어 가며 찾아야 하는 문서는 탐색 부담이 다릅니다.

두 가지를 정해야 했습니다.

진입점을 몇 개 둘 것인가. 상단 바는 AppShell 이 소유하고 사이드바 유무만 자식 프레임이 정합니다. 영역마다 검색바를 두려면 검색의 소유를 프레임으로 내려야 하며, 그러면 진입점과 색인이 각각 두 벌이 됩니다.

무엇까지 색인할 것인가. 독자가 문서에서 찾는 것은 두 종류입니다. 스크롤 처럼 주제로 찾는 경우와 allowedHosts 처럼 식별자를 그대로 치는 경우입니다. 후자는 실측으로 확인한 함정을 다시 꺼내 볼 때의 접근 경로이며 절 제목에는 나타나지 않습니다.

결정

진입점은 하나이고 결과를 영역별로 나눕니다

상단 바에 검색 하나를 두고 오버레이 하나가 글과 문서를 함께 찾습니다. 결과는 · 아키텍처 · 소개 로 묶어 어느 쪽에서 나온 것인지 구분하며, 묶음의 차례는 상단 메뉴와 같습니다.

셸이 여는 상태를 소유하고 오버레이는 그것을 입력으로 받습니다. 트리거가 상단 바와 하단 네비 두 곳에 있으므로 오버레이가 상태를 소유하면 트리거들이 오버레이를 참조하게 되고 지연 로드가 무효가 됩니다. 같은 이유로 ⌘K · Ctrl+K 를 듣는 것도 셸입니다. 오버레이는 열릴 때 로드되므로 자신을 여는 열쇠를 가질 수 없습니다.

단축키는 KeyboardEvent.key 가 아니라 code 로 판정합니다. 한글 자판에서는 같은 자리가 로 오므로 key 만 보면 입력기를 켠 상태에서 단축키가 듣지 않습니다.

색인은 절 제목과 인라인 코드까지입니다

빌드가 search-index.ts 를 생성합니다. 문서마다 제목 · 설명 · 태그와 절 목록을 담고, 절마다 그 안에 나오는 인라인 코드를 함께 담습니다. 코드 블록은 제외합니다. 예제 전체가 들어오면 흔한 낱말이 모든 문서에 걸립니다.

인라인 코드를 절에 붙이므로 식별자로 찾은 결과도 앵커를 갖습니다. allowedHosts 로 찾으면 개발 환경 문서의 7 절로 바로 이동합니다.

자리 가중치 사유
문서 제목 6 문서 자체를 지목한 질의입니다
인라인 코드 5 식별자를 그대로 친 질의이며 같은 글자가 산문에 우연히 섞이지 않습니다
태그 4 글의 분류이며 작성자가 직접 부여합니다
절 제목 3 문서 안의 위치를 가리킵니다
설명 2 요약이라 낱말이 넓게 걸립니다

가중치는 자리마다 더합니다. 가장 높은 자리 하나만 취하면 httpResource 처럼 여러 문서가 쓰는 이름에서 그 이름을 절 제목으로 내건 문서와 본문에 한 번 언급한 문서가 같은 점수가 됩니다.

모든 낱말이 한 문서 안에서 발견되어야 결과에 듭니다. 형태소 분석 없이 부분 문자열로 맞추므로 라우팅라우팅과 에 걸리며, 대가로 팅과 같은 조각도 걸립니다.

결과는 절 단위가 아니라 문서 단위로 내고 상한은 20 건입니다. 절마다 결과를 만들면 흔한 낱말 하나에 수백 건이 나오고, 상한을 넘기면 목록을 훑는 대신 스크롤이 판단을 대신하게 됩니다.

색인과 팔레트는 검색을 열 때 받습니다

색인은 압축 후 20kB 이고 팔레트 청크는 16.5kB 입니다. 검색을 쓰지 않는 방문에서는 한 번도 필요하지 않으므로 초기 번들에 두지 않습니다.

shared/markdown 배럴은 불러오는 함수만 내보내고 색인 자체는 내보내지 않습니다. 배럴에 실으면 사이드바가 문서 목록을 읽는 순간 색인이 함께 딸려 옵니다. 배럴이 @defer 분리를 막는 사례는 개발 환경 7절에 기록되어 있습니다.

검토한 대안

블로그와 문서에 검색바를 각각 배치

구분 내용
장점 문서를 보는 중에 글 결과가 섞이지 않습니다. 영역마다 결과 형식을 다르게 만들 수 있습니다
단점 검색의 소유가 셸에서 프레임으로 내려가 진입점과 색인이 두 벌이 됩니다
기각 사유 글이 4 편이라 블로그 검색바가 뒤지는 대상이 목록 화면에 이미 전부 보입니다. 두 벌을 유지할 대상 규모가 아닙니다

현재 영역을 기본 범위로 지정

구분 내용
장점 진입점은 하나로 두면서 문서를 보는 중에는 문서만 나옵니다
단점 검색 상태가 라우트에 의존합니다. 사용자가 지금 어느 범위를 뒤지고 있는지 매번 확인해야 합니다
기각 사유 결과를 영역별로 묶으면 섞임 문제가 해소되며, 범위를 상태로 두는 대가가 남지 않습니다

본문 전문 색인

구분 내용
장점 산문 안의 표현까지 찾을 수 있고 결과에 문맥 조각을 보여줄 수 있습니다
단점 압축 후 141kB 로 채택안의 7 배입니다
기각 사유 찾지 못해서 문제가 되는 것은 식별자였고 그것은 인라인 코드 801 개로 덮입니다. 나머지 121kB 가 사는 것은 산문 표현 검색이며, 문서가 절 번호 체계를 갖고 있어 그 수요가 구조적으로 낮습니다

모바일 진입점을 띄우는 버튼(FAB)으로 배치

구분 내용
장점 검색이 가장 눈에 띄고 엄지에 가깝습니다
단점 스크롤 중 본문 우하단을 계속 가립니다. 하단 네비 위에 띄우려면 막대 높이를 두 곳에서 맞춰야 합니다
기각 사유 하단 네비 항목이 셋이라 네 번째 칸이 비어 있었습니다. 빈 칸을 두고 그 위에 요소를 겹치는 구성이 됩니다

결과

문서 43 개와 글 4 편을 제목 · 절 제목 · 태그 · 인라인 코드로 찾을 수 있고, 절이나 식별자로 걸린 결과는 해당 절로 바로 이동합니다.

감수하는 사항은 다음과 같습니다.

  • 산문 안의 표현은 찾지 못합니다. 절 제목에도 인라인 코드에도 없는 낱말은 결과에 나오지 않습니다.
  • 부분 문자열로 맞추므로 조각이 걸립니다. 팅과 같은 입력이 라우팅과 네비게이션 에 걸립니다. 형태소 분석기를 들이지 않는 대가입니다.
  • 초기 번들이 0.6kB 늘었습니다. 순위 계산 함수와 색인을 불러오는 함수가 배럴을 통해 들어옵니다. 색인 자체와 팔레트는 별도 청크입니다.
  • 색인이 절 순서에 의존합니다. 인라인 코드를 절에 붙일 때 빌드가 목차와 같은 차례로 2·3 단계 제목을 읽어 짝지으므로, 목차 생성 규칙이 바뀌면 두 곳을 함께 고쳐야 합니다.
  • 결과에 문맥 조각이 없습니다. 어느 절에서 걸렸는지는 보이지만 그 절의 어느 문장인지는 열어야 알 수 있습니다.

개정

2026-08-13 — 소개를 색인에서 제외했습니다. 상단 메뉴와 하단 네비에 항상 나와 있어 찾을 필요가 없고, 자기소개 문장의 흔한 낱말이 문서 결과 사이에 섞여 목록을 흐렸습니다. 결정 절의 결과 묶음은 아키텍처 둘로 줄었습니다.

2026-08-13 — 입력과 결과를 분리했습니다. 입력창은 즉시 반영하고 결과는 150ms 동안 입력이 멎은 뒤에 계산합니다. 한국어 입력기가 낱자마다 값을 내보내므로 라우팅 한 낱말에 여섯 번 넘게 목록 전체가 다시 그려지고 높이도 함께 움직였습니다.

2026-08-13 — 질의와 겹치는 구간을 강조합니다. 어느 낱말 때문에 걸린 결과인지 목록에서 바로 보이게 합니다. HTML 문자열이 아니라 조각 배열로 돌려주고 화면이 텍스트 보간으로 그립니다. 문자열을 만들어 [innerHTML] 로 넣으면 ADR-0013 이 빌드 생성물에만 열어 둔 경로가 런타임 입력에도 열립니다. 색은 --mark 토큰 짝을 새로 추가했습니다.

2026-08-13 — 좁은 화면에서 화면 전체를 씁니다. DOM 이 같고 크기와 모서리만 달라지므로 반응형이며 런타임 판정을 쓰지 않습니다. 높이 전환은 상자가 화면 안에서 커졌다 작아질 때의 문제이므로 이 폭에서는 스타일 규칙이 되돌립니다.

© 2026 dev.goraebap