ComponentsP3 본문

MultiSelect

다중 선택 콤보박스 — 칩 트리거 + aria-multiselectable 리스트박스. Enter/Space 토글은 목록을 닫지 않고, 포커스는 트리거(검색 시 검색 입력)에 머뭅니다. 비제어(defaultValue) 우선 (DK WizDropDown 사양 승격).

마지막 업데이트 2026-06-11

한눈에#

다중 선택 콤보박스 — 칩 트리거 + aria-multiselectable 리스트박스. 선택값은 트리거 안에 제거 버튼이 달린 칩으로 요약됩니다.

  • 다중 선택
  • 칩 트리거
  • 검색 가능
  • 비제어 우선

기본 — Enter/Space 토글(목록은 닫히지 않음)

사용 시점#

여러 값을 골라 칩으로 요약하면 MultiSelect — 그 외에는 목적에 맞는 컴포넌트를 가리킵니다.

쓴다

고정 옵션에서 여러 값 동시 선택(담당자·태그·필터 조합)

대신 Select

하나만 골라야 할 때

대신 Autocomplete

서버 조회·비동기 태그 입력

대신 Chip

선택 결과를 input 칩으로 직접 다룰 때

플레이그라운드#

컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.

import { MultiSelect } from '@wds/ui-web';

<MultiSelect
  aria-label="담당 에이전트"
  options={[
    { value: 'frontend', label: '프론트엔드' },
    { value: 'backend', label: '백엔드' },
    { value: 'mobile', label: '모바일' },
    { value: 'data', label: '데이터' },
  ]}
/>

변형#

칩 상한 — maxVisibleTags 초과분은 +N 합산
showAll + allowClear — 전체 토글 행과 전체 비우기 ×

showAll이면 목록 맨 위에 "전체" 토글 행이 생기고, 전체가 선택되면 트리거에 칩 나열 대신 전체 1개가 표시됩니다(그 칩의 ×는 전체 해제).

searchable — label 부분일치(대소문자 무시) 검색

열리면 포커스가 검색 입력으로 이동합니다. 검색 중에는 "전체" 행이 숨겨집니다 — 필터된 부분집합에 대한 전체 토글은 의미가 모호하기 때문입니다.

그룹 — 연속된 같은 group은 헤더 아래 묶이고, groupSelectable이면 헤더가 토글 행

그룹 일부만 선택되면 헤더 체크박스가 부분 선택(−)으로 표시됩니다. 비활성 옵션은 전체/그룹 토글에서도 제외됩니다.

크기#

단일 크기입니다 — 트리거는 input.height(44px)를 최소 높이로 칩이 줄바꿈되면 늘어나고, 최소 폭 16rem입니다. 리스트는 최대 16rem 높이에서 스크롤됩니다.

상태#

Disabled · Loading
  • Focus — 트리거 :focus-visibleinput.border-focus 보더 + 18% 링
  • 활성 행color.surface-hover 배경(마우스 호버도 활성을 따라감), 활성+선택 행은 color.surface-selected
  • 선택 행aria-selected + color.primary-text 잉크 + 체크 표시
  • Loading — 목록 대신 Spinner / 빈 결과emptyText(role="status")
  • Disabledinput.bg-disabled 배경 + 포커스·토글 차단

모바일#

DK 모바일 재활용 표준입니다 — mobile을 켜면 팝업 대신 BottomSheet 체크리스트가 열립니다. 데스크톱과 달리 임시 선택입니다: 체크를 바꿔도 즉시 확정되지 않고, footer의 확인을 눌러야 onChange가 한 번 발화합니다. 취소·배경 클릭·Escape·핸들 드래그 다운은 임시 선택을 폐기하며, 시트는 열릴 때마다 커밋된 값으로 새로 시작합니다. searchable·showAll·groupSelectable은 시트 안에서도 동일하게 동작합니다. 라이브 데모는 데스크톱 뷰포트라 코드로 안내합니다.

<MultiSelect
  mobile
  mobileTitle="에이전트 선택"
  confirmLabel="적용"
  searchable
  showAll
  options={agentOptions}
  onChange={(values) => applyAgents(values)}
/>

Props#

Prop타입기본값설명
optionsReadonlyArray<MultiSelectOption>옵션 배열 (아래 표) — 데이터 prop 방식
defaultValueReadonlyArray<string>초기 선택 value 배열 — 이후 상태는 내부 소유(비제어)
onChange(values: string[]) => void선택 집합 변경 콜백 — 항상 새 배열로 통지
placeholderstring'선택'선택이 없을 때 트리거에 표시되는 문구
maxVisibleTagsnumber3트리거 칩 노출 상한 — 초과분은 "+N"으로 합산 표시
showAllbooleanfalse목록 맨 위 "전체" 토글 행 — 전체 선택 시 트리거에 전체 1개 표시
allLabelstring'전체'전체 행/칩의 라벨
searchablebooleanfalse팝업 상단 검색 입력 — label 부분일치(대소문자 무시)
onSearchChange(query: string) => void검색어 변경 통지 — 서버 필터 연동용
searchPlaceholderstring'검색'검색 입력 placeholder
searchLabelstring'옵션 검색'검색 입력 aria-label
groupSelectablebooleanfalse그룹 헤더를 체크박스 행으로 — 전체/부분(indeterminate) 토글
allowClearbooleanfalse트리거 우측 전체 비우기 × 버튼
clearLabelstring'모두 지우기'비우기 버튼 aria-label
loadingbooleanfalse목록 대신 로딩(Spinner) 표시
emptyTextstring'결과 없음'옵션/검색 결과 0건 표시 문구
mobilebooleanfalsetrue면 BottomSheet 체크리스트 — 임시 선택 후 확인으로 확정 (DK 모바일 표준)
mobileTitleReactNode'선택'시트 제목
confirmLabelstring'확인'mobile 확정 버튼 라벨
cancelLabelstring'취소'mobile 취소 버튼 라벨
disabledbooleanfalse비활성 — 열기·토글·포커스 차단
…restOmit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'>aria-label 등은 트리거 div(role=combobox)로 전달

MultiSelectOption:

Prop타입기본값설명
valuestring고유 value — 선택 집합의 원소
labelstring표시·검색 대상 라벨
disabledbooleanfalse선택 차단 — 전체/그룹 토글에서도 제외
groupstring같은 group 문자열의 연속 옵션은 그룹 헤더 아래 묶임

접근성#

트리거가 role="combobox" + aria-haspopup="listbox"(mobile은 "dialog")인 div입니다 — 칩 제거 버튼이 트리거 안에 살기 때문에 중첩 버튼을 피해 div + tabIndex=0으로 구현됩니다. 팝업은 ul[role="listbox"] + aria-multiselectable="true"이고, 포커스는 트리거(searchable이면 검색 입력)에 머물며 aria-activedescendant가 활성 행 id를 가리킵니다.

동작
/ / Enter / Space (닫힘)열기 — 첫 탐색 가능 행 활성
/ (열림)활성 행 이동 — 비활성 옵션 건너뜀
Home / End첫/마지막 행으로 점프 (검색 입력에서는 텍스트 편집에 양보)
Enter / Space활성 행 토글 — 목록 유지 (검색 입력의 Space는 텍스트 입력)
Escape닫기 + 트리거 포커스 복원
Tab닫고 기본 포커스 이동
  • 행 선택 상태는 aria-selected, 부분 선택(전체/그룹)은 data-indeterminate — 시각 체크박스는 aria-hidden
  • 비활성 옵션은 aria-disabled로 보고되고 클릭·키보드 토글이 차단됩니다
  • 외부 클릭으로 닫히며, 옵션 클릭은 mousedown 기본 동작 차단으로 트리거/검색 입력 포커스를 유지합니다
  • 뷰포트 하단 공간이 부족하면 팝업이 위로 플립됩니다 (Select 패턴)

토큰#

component 토큰 없이 Input·Checkbox의 component 토큰과 semantic을 직접 소비합니다(신설 기준 §4 미충족).

속성토큰
트리거input.bg/fg/border/radius · 최소 높이 input.height · placeholder input.placeholder
포커스input.border-focus + color.primary 18% 링
칩 초과(+N)color.surface-muted · color.text-muted · radius.full
팝업color.surface-raised · radius.md · shadow.lg · z.dropdown
행 활성/선택color.surface-hover · 선택 잉크 color.primary-text · 활성+선택 color.surface-selected · 눌림 color.surface-pressed
행 체크박스checkbox.bg/border/radius/check/bg-checked (Checkbox 어휘 공유)
행 높이control.height-sm — 모바일 시트 행은 control.height-md
비활성input.bg-disabled · color.text-placeholder
모션duration.fast + ease.standard/out