← Back to lessons|engineering
Passkeys not supported in this browser
KMS search: Enter-commit over per-keystroke live search (empty-input clears immediately)
Chose Enter-commit + immediate-clear-on-empty-input for KMS vector search UI instead of per-keystroke live search, due to 0.6s/call backend cost, Korean IME composition noise, and documented rerender constraints.
lesson_learnedsearch-uxdebounceimevector-searchreactnextjsurl-state
Created 8/3/2026, 4:51:03 AM
Content
Decision: Keep Enter-commit for the KMS list search bar instead of per-keystroke live search; add one targeted live reaction — when the input is emptied, immediately clear the URL query (q only, keeping cat filters) via a new onQueryEmptied callback. Also unified all reset affordances (search bar X, filter drawer 초기화, empty-state 필터 초기화) to clearSearch (q=null + cat=[]). Rationale: (1) /api/kms/search is a vector search measured at ~0.6s per call on localhost — per-keystroke calls would multiply backend load and constantly reshuffle ranked results; (2) Korean IME composition emits intermediate states (ㅅ→수→수수) that would fire meaningless queries even with debounce unless composition events are carefully handled; (3) a documented perf constraint in src/app/kms/page.tsx keeps the input buffer local to KmsHeader because every URL q change rerenders the page tree and re-parses react-markdown in KmsArticleCard × N; (4) server-side ranked KB search matches the explicit-commit UX convention (Google-style), unlike small client-side list filtering. The user's actual pain (stale filtered results after erasing the input, with the X button disappearing at empty) is fully solved by reacting only to the single 'input became empty' event — zero extra server cost (list query is a React Query cache hit) and no IME hazard. Files: src/features/kms/model/use-kms-search-params.ts (clearFilters removed, clearSearch unified), src/widgets/kms/KmsHeader.tsx (onQueryEmptied), src/widgets/kms/KmsFilterDrawer.tsx (clearEnabled), src/app/kms/page.tsx (wiring). Alternatives rejected: full live search with 300ms debounce + isComposing skip + min-length threshold (still pays vector-search cost and result churn); making empty-input also clear cat filters (hostile — erasing text should not nuke deliberately configured drawer filters).