# 검색 기능 설계 (Search) 프로젝트: **Nexa Markdown Viewer** — 문서 검색이 없어 불편하다는 요구에 따라, 기본 단어 검색부터 정규식·저장소 전체 검색까지 **단계적으로 보완**하기 위한 설계/구현 대상 정리. 원칙 - **범위를 단계로 분리**: 현재 문서 → 탐색기 파일명 → 워크스페이스 전체(grep) 순으로 난도가 오른다. 각 단계는 독립적으로 출시 가능하도록 쪼갠다. - **공통 검색 옵션 모델**을 먼저 정의해 모든 단계가 재사용한다(대소문자/단어 단위/정규식). - **보안**: 검색은 이미 열려 있는 로컬 폴더와 인증된 GitHub 범위 안에서만. GitHub 코드 검색 시 토큰은 Rust에서만 사용(프론트로 노출 금지, HTTPS). 기존 보안 원칙 준수. --- ## 공통 검색 옵션 (모든 단계 공유) ```ts // src/lib/search.ts (신규) — 검색 질의 모델 export interface SearchQuery { text: string; caseSensitive: boolean; // Aa (기본 false = 대소문자 무시) wholeWord: boolean; // \b…\b (단어 단위) regex: boolean; // .* (정규식 모드) } // 질의를 RegExp로 컴파일(일반 검색도 내부적으로 RegExp로 통일) export function compileQuery(q: SearchQuery): RegExp | null; // - regex=false: 입력을 escapeRegExp 후 사용 // - wholeWord=true: \b 래핑 // - caseSensitive=false: 'i' 플래그 // - 'g' 플래그 항상(전체 매치 순회) // - 잘못된 정규식이면 null 반환 → UI에서 입력칸 빨강 표시 ``` UI 공통: 입력창 + 옵션 토글 3개(`Aa` / `단어` / `.*`) + 결과 카운트(`3/12`) + `↑`/`↓`(이전/다음) + `Esc` 닫기. 단축키 `Ctrl/⌘+F`(현재 문서), `Ctrl/⌘+Shift+F`(전체 검색). --- ## 단계별 구현 대상 ### S1 — 현재 문서 내 검색 (Ctrl/⌘+F) — *기본, 최우선* - [ ] 검색 바 컴포넌트 `SearchBar.tsx` (본문 우상단 오버레이) + 옵션 토글 + 카운트 + 이동/닫기 - [ ] **일반 단어 검색**(부분 일치, 대소문자 무시 기본) — `compileQuery`로 통일 - [ ] 옵션: 대소문자 구분 / 단어 단위(`\b`) - [ ] 매치 하이라이트: 렌더된 본문(`.markdown-body`)·일반텍스트/코드 뷰어의 텍스트 노드에 `` 삽입(원본 DOM 보존 위해 Range/TreeWalker 사용), 현재 매치는 강조색 + `scrollIntoView` - [ ] 이동: `Enter`/`↓` 다음, `Shift+Enter`/`↑` 이전, 순환 - [ ] 상태: zustand `search`(query/matches/activeIndex), 문서 전환 시 초기화 - 비고: 마크다운은 렌더 후 DOM 텍스트 기준으로 검색(원문 기준이 아니라 화면에 보이는 텍스트). 코드/일반텍스트 뷰어도 동일 로직 재사용. ### S2 — 정규식 지원 (현재 문서) - [ ] 옵션 `.*`(정규식) 토글 — `compileQuery`가 입력을 그대로 패턴으로 사용 - [ ] 잘못된 패턴 처리(컴파일 실패 시 입력칸 빨강 + 결과 0, 예외 무시) - [ ] 안전장치: 과도한 매치/캐타스트로픽 백트래킹 대비(타임아웃/최대 매치 수 제한) - 비고: 뷰어이므로 치환(replace)은 비대상 — 검색·하이라이트만. ### S3 — 탐색기 파일명 빠른 필터 - [ ] 좌측 트리 상단 필터 입력 → 이름 부분 일치하는 노드만 표시(경로 상위 자동 펼침) - [ ] 옵션: 대소문자/정규식, 매칭 글자 하이라이트 - [ ] 기존 파일형식 필터(`DEFAULT_FILTERS`)와 **AND 결합** - 비고: 파일 내용이 아닌 **이름**만 대상(가볍고 즉시 적용). 내용 검색은 S4. ### S4 — 워크스페이스 전체 내용 검색 (grep) — *고급* - [ ] 좌측 ActivityBar에 🔍 **검색 패널**(`SearchPanel.tsx`): 질의 + 공통 옵션 + 포함/제외 글로브 - [ ] **로컬**: Rust 커맨드 `search_in_source`(walkdir + 파일 스트리밍 매칭) — - 비동기·취소 가능(쿼리 변경 시 이전 검색 중단), 파일/매치 수 상한, 바이너리 스킵 - 결과를 이벤트로 점진 전달(`emit`)해 대용량에서도 반응성 유지 - [ ] **GitHub**: 단계적 - 1순위: 트리 순회 후 파일 fetch 매칭(레이트리밋 주의, 동시성 제한) — 작은 저장소 - 2순위: GitHub Code Search REST(`/search/code`) 활용(토큰 필요, 공개/권한 범위 한정) - [ ] **결과 패널**: 파일별 그룹 → 라인 번호 + 스니펫(매치 강조), 클릭 시 해당 파일 열고 라인으로 점프(가능하면 S1 하이라이트와 연계), 매치 총계 표시 - [ ] 검색 범위 토글: `현재 문서` / `이 저장소` / `전체 워크스페이스` - 보안: GitHub 검색은 Rust에서 토큰 사용, 프론트 미노출. 통신 HTTPS. ### S5 — UX 고도화 - [ ] 최근 검색어 기록(드롭다운), 결과 내 재검색 - [ ] 매치 컨텍스트(앞뒤 줄) 표시, 결과 정렬/그룹 토글 - [ ] 결과 카운트 배지, 검색 중 진행 표시/취소 버튼 - [ ] 접근성: 포커스 트랩, 스크린리더 라벨, 키보드 전용 흐름 --- ## 권장 진행 순서 1. **공통 옵션 모델**(`lib/search.ts`) → 2. **S1**(현재 문서 일반 검색) → 3. **S2**(정규식) → 4. **S3**(파일명 필터) → 5. **S4**(전체 내용 검색, 로컬 먼저 → GitHub) → 6. **S5**(고도화) 각 단계는 독립 커밋/출시 단위. S1~S2만으로도 "단어 검색 + 정규식"이라는 핵심 불편을 해소한다.