Autocomplete
비동기 태그 입력 콤보박스 — 자유 텍스트로 검색하고 외부 주입 suggestions에서 골라 칩으로 쌓습니다. 서버 조회는 소비자가 onQueryChange로 수행하는 비제어 컴포넌트입니다.
마지막 업데이트 2026-06-11
한눈에#
비동기 태그 입력 콤보박스 — 자유 텍스트로 검색하고 외부 주입 suggestions에서 골라 칩으로 쌓습니다. 서버 조회는 소비자가 onQueryChange로 수행합니다.
아직 선택 없음
- 비동기 태그
- 칩 누적
- onQueryChange
- maxSelections
입력하면 후보가 열리고 선택하면 칩으로 쌓입니다
사용 시점#
서버 제안에서 골라 칩으로 쌓으면 Autocomplete — 그 외에는 목적에 맞는 컴포넌트를 가리킵니다.
권장 — 이렇게 쓰세요
지양 — 이러지 마세요
아직 선택 없음
쓴다 — 자유 텍스트 검색 + 서버 제안에서 골라 칩으로 누적(태그 입력)
대신 SearchField — 값 선택이 아니라 검색 실행(Enter로 결과 이동)
대신 Select — 고정 옵션 단일 선택
대신 MultiSelect — 고정 옵션 다중 선택
플레이그라운드#
컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.
Autocomplete를 직접 조작해 보세요
속성·테마·토큰을 바꾸고 React·Flutter 코드를 확인하는 풀스크린 빌더로 엽니다.
플레이그라운드는 넓은 작업 영역이 필요해 웹·태블릿에서 편집할 수 있어요.
변형#
아직 선택 없음
defaultValue로 초기 칩을 채울 수 있고, suggestions에 없는 값은 value
문자열이 그대로 칩 라벨이 됩니다. 이미 선택된 항목은 목록에서 제외됩니다.
크기#
단일 크기입니다 — 트리거 최소 높이는 input.height(44px)로 Input과 같은 폼
컨트롤 줄맞춤이고, 칩이 늘어나면 줄바꿈하며 세로로 자랍니다. 최소 폭은
16rem(MultiSelect와 동일), 팝업 리스트는 최대 16rem 높이에서 스크롤됩니다.
상태#
- Focus — 포커스 링은 wrap
:focus-within에input.border-focus보더 + 18% 링 (Input과 동일) - Loading —
loading이면 목록 대신 스피너 행(role="status", ‘로딩 중’) - Empty — 남은 후보가 없으면
emptyText(‘결과 없음’) 행 - Max 도달 — 입력
disabled+ 안내 문구(role="status",aria-describedby연결) - Disabled —
input.bg-disabled배경, 칩 제거 버튼도 함께 숨김
비제어 컴포넌트입니다 — defaultValue로 시작하고 칩 추가/제거 시
onChange(values)로 알립니다.
Props#
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
suggestions | readonly AutocompleteSuggestion[] | — | 외부 주입 후보 — 서버 조회 결과는 소비자가 onQueryChange로 갱신해 되돌린다 |
defaultValue | readonly string[] | — | 초기 선택 value 배열 — 이후 상태는 내부 소유(비제어) |
onChange | (values: string[]) => void | — | 칩 추가/제거 시 선택 value 배열 콜백 |
onQueryChange | (query: string) => void | — | 입력 쿼리 변경 콜백 — 서버 조회 트리거 지점 |
loading | boolean | false | 목록 대신 로딩 표시 |
maxSelections | number | — | 선택 상한 — 도달 시 입력 비활성 + 안내 표시 |
maxSelectionsText | string | — | 상한 도달 안내 문구 — 기본 '최대 N개까지 선택할 수 있습니다' |
emptyText | string | 결과 없음 | 남은 후보가 없을 때 표시 문구 |
ref | Ref<HTMLInputElement> | — | 검색 텍스트 <input> 요소로 병합 전달되는 ref (React 19 ref-as-prop) |
접근성#
입력이 role="combobox" + aria-autocomplete="list" +
aria-haspopup="listbox", 팝업이 role="listbox"입니다. 포커스는 항상 입력에
머물고 aria-activedescendant가 활성 옵션 id를 가리킵니다. 칩 제거 버튼은
aria-label="{label} 제거"로 이름이 붙고 44px 히트 영역을 가집니다.
| 키 | 동작 |
|---|---|
| 문자 입력 | 목록 열기 + onQueryChange 발화 |
↓ (닫힘) | 목록 열기 — 첫 옵션 활성 |
↓ / ↑ (열림) | 활성 옵션 이동 — 양 끝에서 클램프 |
Enter | 활성 옵션 선택 → 칩 추가 + 쿼리 비움 + 닫기 |
Escape | 선택 없이 닫기 |
Backspace (빈 입력) | 마지막 칩 제거 |
외부 클릭으로도 닫히며, 상한 도달 안내는 role="status" +
aria-describedby로 입력과 연결됩니다.
토큰#
component 토큰 없이 Input의 component 토큰(input.*)과 semantic을 직접
소비합니다(신설 기준 §4 미충족).
| 속성 | 토큰 |
|---|---|
| 트리거 | input.bg/fg/border/height/radius · placeholder input.placeholder |
| 포커스 | input.border-focus + color.primary 18% 링 (:focus-within) |
| 칩 | Tag(neutral sm) — color.surface-muted · color.text-muted · radius.full |
| 팝업 | color.surface-raised · radius.md · shadow.lg · z.dropdown |
| 옵션 활성 | color.surface-hover · 눌림 color.surface-pressed · 설명 잉크 color.text-subtle |
| 옵션 높이 | control.height-sm |
| 안내/빈 상태 | color.text-subtle · color.text-muted |
| 비활성 | input.bg-disabled |
| 모션 | duration.fast + ease.out/standard |