도입 스토리
"WAI-ARIA 명세를 읽으려고 W3C 문서를 열었는데..." 김개발이 브라우저 탭을 보여주었습니다. "너무 방대해요. 어디서 시작해야 하죠?"
"세 가지만 이해하면 돼요." 박멘토가 화이트보드에 썼습니다. "역할(Role), 상태(State), 속성(Property). 이 세 가지가 ARIA의 전부예요."
"그리고 실제 구현 패턴은 APG라는 곳에서 찾을 수 있어요. ARIA Authoring Practices Guide — W3C가 제공하는 공식 컴포넌트 패턴 가이드예요. 코드 예제까지 있어요."
핵심 개념 설명
ARIA의 세 가지 구성 요소
역할(Role) — 요소가 무엇인지.
<div role="dialog"> <!-- 대화상자 --><div role="tablist"> <!-- 탭 그룹 --><div role="tab"> <!-- 탭 --><div role="tabpanel"> <!-- 탭 내용 --><div role="progressbar"> <!-- 진행 표시줄 -->
상태(State) — 요소의 현재 상태 (동적으로 변함).
<!-- aria-expanded: 열림/닫힘 --><button aria-expanded="false">메뉴 열기</button><button aria-expanded="true">메뉴 닫기</button><!-- aria-checked: 체크 여부 --><div role="checkbox" aria-checked="true">동의합니다</div><div role="checkbox" aria-checked="false">동의하지 않습니다</div><!-- aria-selected: 선택 여부 --><div role="tab" aria-selected="true">첫 번째 탭</div><!-- aria-disabled: 비활성 여부 --><button aria-disabled="true">제출 (처리 중)</button><!-- aria-invalid: 유효성 오류 --><input aria-invalid="true">
속성(Property) — 요소의 특성 (주로 정적).
<!-- aria-label: 접근 가능한 이름 직접 지정 --><button aria-label="닫기">X</button><!-- aria-labelledby: 다른 요소의 텍스트를 이름으로 --><h2 id="modal-title">주문 확인</h2><dialog aria-labelledby="modal-title">...</dialog><!-- aria-describedby: 설명 연결 --><input aria-describedby="hint error"><!-- aria-haspopup: 팝업을 열 수 있음 --><button aria-haspopup="menu">메뉴</button><button aria-haspopup="dialog">도움말</button><button aria-haspopup="listbox">선택</button><!-- aria-controls: 제어하는 요소 참조 --><button aria-controls="panel1">패널 열기</button><div id="panel1">...</div><!-- aria-owns: 논리적 부모-자식 관계 --><ul aria-owns="extra-items">...</ul><!-- aria-live: 실시간 업데이트 알림 --><div aria-live="polite">...</div><!-- aria-atomic: 전체/부분 읽기 --><div aria-live="polite" aria-atomic="true">...</div>
ARIA 역할 분류
| 분류 | role 예시 | 설명 |
|---|---|---|
| 위젯 | button, checkbox, tab, slider |
인터랙티브 UI 컴포넌트 |
| 복합 위젯 | tablist, menu, listbox, grid |
위젯의 컨테이너 |
| 문서 구조 | article, section, list, heading |
콘텐츠 구조화 |
| 랜드마크 | banner, navigation, main, region |
페이지 영역 |
| 실시간 | alert, log, status, timer |
동적 업데이트 |
ARIA Authoring Practices Guide (APG)
W3C APG는 자주 쓰이는 UI 패턴별 ARIA 구현 가이드와 예제 코드를 제공합니다.
https://www.w3.org/WAI/ARIA/apg/patterns/
수록된 주요 패턴:
- Accordion (아코디언)
- Alert Dialog (경고 대화상자)
- Breadcrumb (경로 탐색)
- Button (버튼)
- Carousel (슬라이더)
- Combobox (자동완성)
- Dialog Modal (모달 대화상자)
- Disclosure (열기/닫기)
- Feed (무한 스크롤)
- Grid (그리드)
- Listbox (목록 선택)
- Menu (메뉴)
- Menubar (메뉴 바)
- Radio Group (라디오 그룹)
- Slider (슬라이더)
- Spinbutton (숫자 입력)
- Switch (스위치)
- Table (테이블)
- Tabs (탭)
- Toolbar (툴바)
- Tooltip (툴팁)
- Tree (트리뷰)
ARIA 연결 관계 다이어그램
aria-labelledby ← "이 요소의 이름은 저 요소에 있다"
aria-describedby ← "이 요소의 설명은 저 요소에 있다"
aria-controls ← "이 요소는 저 요소를 제어한다"
aria-owns ← "이 요소는 저 요소를 자식으로 소유한다"
aria-flowto ← "읽기 흐름이 저 요소로 이어진다"
단계별 실습
따라하기: APG 패턴 탐색하기
https://www.w3.org/WAI/ARIA/apg/patterns/에 접속합니다.- "Tabs" 패턴을 엽니다.
- 예제 코드를 보며
role,aria-selected,aria-controls의 관계를 파악합니다. - "Keyboard Interaction" 섹션에서 키보드 동작 명세를 확인합니다.
변형하기: ARIA 속성 현황 파악하기
// 페이지 내 ARIA 속성 사용 현황 확인const ariaAttrs = [ 'role', 'aria-label', 'aria-labelledby', 'aria-describedby', 'aria-expanded', 'aria-hidden', 'aria-live', 'aria-invalid'];const summary = {};ariaAttrs.forEach(attr => { const count = document.querySelectorAll(`[${attr}]`).length; if (count > 0) summary[attr] = count;});console.table(summary);
정리와 확인
핵심 내용 요약
- ARIA 3요소: 역할(role), 상태(state), 속성(property)
- 역할: 요소가 무엇인지 (
dialog,tab,progressbar) - 상태: 현재 상태, 동적 변화 (
expanded,checked,selected) - 속성: 특성, 주로 정적 (
label,labelledby,describedby) - APG: W3C 공식 패턴 가이드 — 구현 전 반드시 확인
확인 문제
문제 1. aria-expanded는 역할, 상태, 속성 중 무엇인가?
상태(State). 드롭다운이나 아코디언이 열렸는지/닫혔는지를 나타내며,
사용자 인터랙션에 따라 동적으로 변합니다.
문제 2. aria-labelledby와 aria-label의 차이는?
aria-label: 직접 이름 문자열을 지정 (보이지 않는 이름)
aria-labelledby: 페이지 내 다른 요소의 id를 참조
같은 내용이 화면에 이미 있다면 aria-labelledby가 더 좋습니다.
동일 내용을 중복 관리하지 않아도 되기 때문입니다.
다음 챕터에서는 모달 대화상자와 드롭다운 메뉴의 실전 구현을 다룹니다. APG 패턴을 코드로 구현하는 방법을 단계별로 알아봅니다.