컴포넌트 패턴
커스텀 드롭다운 셀렉트 박스의 키보드 인터랙션: WAI-ARIA Listbox 패턴 완벽 가이드
디자인 커스터마이징을 위해 div 태그로 구현한 커스텀 셀렉트 박스가 키보드 사용자와 스크린리더에 치명적인 벽이 되는 이유와 WAI-ARIA Combobox 표준 구현법을 분석합니다.
실무 검증 기록
실무 판단 기준 및 판정 조건
본 기록은 공개 fixture와 재현 조건을 기준으로 정리한 기술 설명입니다. 실제 브라우저·스크린리더 조합의 동작은 별도 수동 검토가 필요합니다.
| 검증 상태 | 공개 fixture 기반 재현 완료 |
|---|---|
| 검증 기준 | 공개 fixture 및 재현 조건 |
| 검증 범위 | 커스텀 셀렉트 드롭다운 키보드 조작성 검증 및 명시된 입력값에 한정 |
| 수동 확인 | 실제 브라우저·키보드·스크린리더 환경에서 별도 확인 필요 |
| fixture에서 확인한 기대 결과 | [실패 상태] div 기반 셀렉트의 키보드 탭 및 방향키 먹통 ➔ [수정 목표] Combobox 패턴과 aria-activedescendant 가상 초점 완성 |
검증에 사용된 실제 픽스처 / 재현 조건
button[role='combobox'] + ul[role='listbox'] + aria-activedescendant- 자바스크립트 비활성화 환경에서는 네이티브 select fallback 필요
- 모바일 터치 디바이스에서의 네이티브 휠 피커 연동 수동 검토 필요
웹 디자인 프로젝트에서 브라우저 기본 <select> 드롭다운 요소는 디자이너들에게 가장 기피되는 태그 중 하나입니다. 브라우저와 운영체제마다 화살표 모양, 팝업 옵션 메뉴의 패딩, 스크롤바 디자인이 제각각이며 CSS로 완전히 커스텀하기가 불가능에 가깝기 때문입니다. 이로 인해 대다수의 프론트엔드 개발팀은 <select> 태그를 버리고, <div>와 <ul>, <li> 태그에 아름다운 CSS 스타일과 자바스크립트 클릭 이벤트를 결합하여 자체적인 '커스텀 셀렉트 박스(Custom Select Box)'를 직접 제작합니다. 그러나 이 과정에서 W3C ARIA 명세와 브라우저 네이티브 키보드 내비게이션을 정확히 구현하지 못해, 마우스가 없는 지체 장애인이나 시각장애인이 옵션을 선택조차 할 수 없는 거대한 접근성 결함이 양산됩니다. W3C WAI-ARIA Authoring Practices Guide(APG)는 셀렉트 박스 구현을 위한 'Combobox with Listbox Popup' 표준 패턴을 규정하고 있습니다. 이 글에서는 완벽한 키보드 접근성을 갖춘 커스텀 셀렉트 박스 구축법을 심층적으로 다룹니다.
네이티브 <select>가 제공하는 막강한 기본 접근성 기능들
커스텀 UI를 만들기 전에, 브라우저 기본 <select> 태그가 운영체제 수준에서 기본 제공하는 수많은 접근성 기능들을 되짚어보아야 합니다.
첫째, Tab 키를 누르면 즉시 초점이 닿고 포커스 링이 선명하게 표시됩니다. 둘째, Space 키나 Enter 키, 또는 Alt+ArrowDown 키를 누르면 드롭다운 옵션 목록이 펼쳐집니다. 셋째, 위/아래 방향키를 누르면 포커스가 이동하며 실시간으로 선택 항목이 바뀝니다. 넷째, 알파벳이나 한글 자음을 타이핑하면 해당 글자로 시작하는 옵션으로 포커스가 즉시 점프하는 Type-ahead 기능이 자체 지원됩니다. 다섯째, 스크린리더는 현재 선택된 값뿐만 아니라 '전체 12개 중 3번째 항목'이라는 목록 위치 정보를 자동으로 낭독합니다.
<div>와 <li>로 커스텀 셀렉트를 만든다는 것은, 위에서 언급한 다섯 가지의 복잡한 운영체제 수준 인터랙션을 자바스크립트로 한 땀 한 땀 직접 모두 구현해야 함을 의미합니다.
WAI-ARIA 1.2 Combobox 패턴의 3대 핵심 역할 구조
W3C APG 표준에 따라 접근 가능한 커스텀 셀렉트를 구축하기 위한 시맨틱 뼈대는 다음과 같습니다.
- 1. 트리거 요소: <button role='combobox' aria-haspopup='listbox' aria-expanded='false' aria-controls='custom-listbox-id'> 형태로 구성합니다. 현재 메뉴가 펼쳐져 있는지(aria-expanded)를 실시간으로 반영해야 합니다.
- 2. 팝업 목록 컨테이너: <ul role='listbox' id='custom-listbox-id' tabindex='-1' aria-labelledby='select-label-id'> 형태로 선언합니다. 스크린리더에게 이 목록이 단순 리스트가 아닌 선택형 목록상자임을 알립니다.
- 3. 개별 선택 옵션: <li role='option' id='opt-1' aria-selected='true'> 형태로 마크업합니다. 현재 선택된 옵션에는 반드시 aria-selected='true'를, 나머지 옵션에는 'false'를 정확히 동기화해야 합니다.
가상 초점 제어(aria-activedescendant)를 통한 포커스 유실 방지
드롭다운 목록이 열렸을 때 방향키로 옵션을 이동할 때, 실제 DOM 포커스를 <button>에서 <li>로 옮기게 되면 포커스 관리가 극도로 복잡해지고 포커스 트랩 문제가 발생하기 쉽습니다.
W3C가 권장하는 가장 우아한 실무 패턴은 '가상 초점(Virtual Focus)' 기법인 aria-activedescendant 속성을 활용하는 것입니다. 실제 브라우저 키보드 포커스는 상단의 <button role='combobox'>에 그대로 묶어둔 채, 사용자가 아래쪽 화살표(ArrowDown) 키를 누르면 자바스크립트가 button.setAttribute('aria-activedescendant', 'opt-2')처럼 현재 활성화된 옵션의 ID를 바인딩합니다.
이 속성이 갱신되면 브라우저 접근성 엔진은 스크린리더에게 포커스가 'opt-2'로 이동했다고 완벽하게 속여주며, 해당 옵션의 텍스트와 순번을 매끄럽게 낭독해 줍니다. 개발자는 복잡한 포커스 이동 스크립트 없이도 최상의 접근성 경험을 완성할 수 있습니다.
Esc 키 닫기 및 포커스 복귀 인터랙션 필수 구현
마지막으로 반드시 갖추어야 할 인터랙션은 '탈출 수단(Escape Mechanism)'입니다.
옵션 목록이 열려 있는 상태에서 사용자가 키보드의 Escape(Esc) 키를 누르면, 드롭다운 팝업이 즉시 닫히고 초점은 다시 셀렉트 박스 트리거 버튼에 안정적으로 머물러 있어야 합니다. 만약 Esc를 눌렀을 때 팝업만 닫히고 포커스가 웹페이지의 body 최상단으로 튕겨 나가버린다면, 키보드 사용자는 다시 Tab 키를 수십 번 눌러 원래 작업 위치로 돌아와야 하는 극심한 고통을 겪게 됩니다.
예외 조건 및 수동 검토 범위
- 자바스크립트가 비활성화된 브라우저 환경에서는 작동하지 않으므로 noscript 대체를 고려해야 합니다.
- 모바일 터치 환경에서는 네이티브 휠 피커(Wheel Picker)가 훨씬 편리하므로 모바일에서는 네이티브 select를 렌더링하는 반응형 분기가 권장됩니다.