폼·입력

[Search with Suggestions] 디바운스 + 키보드 네비 ARIA combobox

입력에 200ms 디바운스를 걸고 제안 목록을 드롭다운으로 띄우는 표준 패턴. ↑↓ Enter Esc Home/End 키보드 네비와 ARIA combobox 속성까지 갖춘 풀세트.

ReactTypeScriptuseDebounceuseClickOutsideARIA comboboxTailwind CSS
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약

200ms 디바운스 + ↑↓ Enter Esc 키보드 네비 + ARIA combobox 속성까지 갖춘 검색 자동완성 풀세트.

이럴 때 필요해요

거의 모든 앱에 들어가는 부품이지만 매번 새로 짜면 함정에 빠진다. 디바운스 타이밍, 키보드 네비 wrap, blur 타이밍, ARIA 속성 — 하나라도 빠지면 "쓰긴 쓰는데 어색한" 컴포넌트가 된다. 한 번 제대로 정리해 두면 어디든 복붙 출발점이 된다.

어떻게 동작하나

  1. 입력을 useDebounce(query, 200)로 한 박자 늦춰 필터링 — 실무에선 이 자리에 API fetch가 들어간다. 입력값과 디바운스값을 비교해 debouncing 상태를 드롭다운에 표시.
  2. 키보드: ↓/↑ 양방향 wrap, Enter 선택, Esc 닫기(닫혀 있으면 입력 초기화), Home/End 양끝 점프.
  3. 하이라이트 변경 시 scrollIntoView({ block: "nearest" })로 옵션이 스크롤 영역 밖이면 안쪽으로 끌어온다.
  4. 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를 생성 — 같은 페이지에 콤보박스가 둘 이상이어도 충돌 없게.

이런 곳에 써요

  • 헤더 글로벌 검색 (사이트 전역 콘텐츠)
  • 카테고리·태그 선택 입력 (다중 선택 변형)
  • 멘션(@)·슬래시 명령(/) 트리거 — 키보드 네비 그대로 재활용

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { SearchWithSuggestionsDemo } from "./-components/SearchWithSuggestionsDemo";

export const metadata: Metadata = {
  title: "Search with Suggestions — 디바운스 + 키보드 네비 (데모)",
  description:
    "검색 입력에 디바운스를 적용하고 제안 목록을 드롭다운으로 띄우는 패턴. ↑↓ Enter Esc Home/End 키보드 네비 + ARIA combobox 구현 데모.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return <SearchWithSuggestionsDemo />;
}
79조회수

댓글