한 줄 요약
200ms 디바운스 + ↑↓ Enter Esc 키보드 네비 + ARIA combobox 속성까지 갖춘 검색 자동완성 풀세트.
이럴 때 필요해요
거의 모든 앱에 들어가는 부품이지만 매번 새로 짜면 함정에 빠진다. 디바운스 타이밍, 키보드 네비 wrap, blur 타이밍, ARIA 속성 — 하나라도 빠지면 "쓰긴 쓰는데 어색한" 컴포넌트가 된다. 한 번 제대로 정리해 두면 어디든 복붙 출발점이 된다.
어떻게 동작하나
- 입력을
useDebounce(query, 200)로 한 박자 늦춰 필터링 — 실무에선 이 자리에 API fetch가 들어간다. 입력값과 디바운스값을 비교해 debouncing 상태를 드롭다운에 표시. - 키보드: ↓/↑ 양방향 wrap, Enter 선택, Esc 닫기(닫혀 있으면 입력 초기화), Home/End 양끝 점프.
- 하이라이트 변경 시
scrollIntoView({ block: "nearest" })로 옵션이 스크롤 영역 밖이면 안쪽으로 끌어온다. - ARIA:
role="combobox"+aria-controls+aria-activedescendant, 옵션은role="option"+aria-selected로 스크린리더에 현재 위치 전달. 매칭 부분은mark로 하이라이트.
놓치기 쉬운 것
- 옵션 클릭은
onClick이 아니라onMouseDown+preventDefault로 — 안 그러면 input blur가 먼저 발생해 드롭다운이 닫혀 클릭이 무효화된다. - 결과 배열이 짧아질 때 하이라이트 인덱스가 범위 밖이 될 수 있다 — onChange·clear·키 입력 모든 경로에서 항상 유효한 인덱스로 setHighlight.
useClickOutside의 enabled 인자를 open에 묶어 닫혀 있을 땐 리스너를 안 붙인다 — 잔불 비용 절감.useId로 listbox·option id를 생성 — 같은 페이지에 콤보박스가 둘 이상이어도 충돌 없게.
이런 곳에 써요
- 헤더 글로벌 검색 (사이트 전역 콘텐츠)
- 카테고리·태그 선택 입력 (다중 선택 변형)
- 멘션(@)·슬래시 명령(/) 트리거 — 키보드 네비 그대로 재활용