ARIA 최소 사용 원칙
ARIA는 접근성 문제의 기본 해결책이 아닙니다. 구조로 해결할 수 없는 경우에만 사용하는 보조 수단으로 취급합니다.
ARIA를 왜 최소화하는지는 접근성 철학을 따릅니다. 이 문서는 ARIA를 언제·어떻게 쓰고 무엇을 금지하는지만 다룹니다.
다음 원칙을 기준으로 ARIA 사용 여부를 판단합니다.
- HTML 기본 요소로 의미 표현이 가능한 경우 ARIA를 사용하지 않습니다.
- 역할(role), 상태(state), 속성(property)을 중복 정의하지 않습니다.
- 보조 기술에 혼란을 줄 수 있는 ARIA 사용을 지양합니다.
ARIA는 없어도 동작하는 구조를 전제로 사용해야 합니다.
허용과 금지
섹션 제목: “허용과 금지”다음과 같은 경우에 한해 ARIA 사용을 허용합니다.
- HTML 기본 요소로 의미 표현이 불가능한 경우
- 커스텀 UI 컴포넌트가 접근성 역할을 가져야 하는 경우
- 상태 변화가 시각적으로만 표현되는 경우
다음과 같은 ARIA 사용은 금지합니다.
- 이미 의미를 가진 HTML 요소에 role을 중복 지정
- 시각적 상태와 다른 ARIA 상태 전달
- 상태 변경 없이 고정된 ARIA 속성 사용
버튼이 필요하면 <button>을 씁니다.
<div>에 role과 tabindex를 얹어 버튼을 흉내내지 않습니다.
<!-- ❌ 나쁜 예: div로 버튼을 흉내내면 키보드/포커스/엔터를 직접 구현해야 한다 --><div class="btn m_primary" role="button" tabindex="0">저장</div>
<!-- ✅ 좋은 예: button은 역할·포커스·키보드 조작을 기본 제공한다 --><button class="btn m_primary" type="button">저장</button>이미 역할을 가진 요소에 같은 role을 다시 지정하지 않습니다.
<button>은 이미 button 역할이므로 role="button"은 중복입니다.
<!-- ❌ 나쁜 예: 이미 가진 역할을 role로 중복 지정 --><button class="btn" type="button" role="button">닫기</button><nav role="navigation"><!-- ... --></nav>
<!-- ✅ 좋은 예: 시맨틱 요소는 role 없이 그대로 쓴다 --><button class="btn" type="button">닫기</button><nav><!-- ... --></nav>시맨틱 요소로 표현할 수 없는 커스텀 컴포넌트에만
상태를 나타내는 최소 ARIA를 더합니다.
상태 자체는 data-state로 관리하고, ARIA는 상태를 보조 기술에 알리는 용도로만 씁니다.
<!-- ✅ 좋은 예: 커스텀 아코디언에 aria-expanded만 최소로 부여 --><div class="accordion" data-state="open"> <button class="i_trigger" type="button" aria-expanded="true" aria-controls="panel_notice">공지사항</button> <div class="i_panel" id="panel_notice"> <p class="i_text">7월 정기 점검 안내입니다.</p> </div></div>// data-state를 토글하고, 같은 값을 aria-expanded에 반영한다document.querySelector(".accordion .i_trigger").addEventListener("click", (e) => { const accordion = e.currentTarget.closest(".accordion"); const open = accordion.getAttribute("data-state") === "open"; accordion.setAttribute("data-state", open ? "close" : "open"); e.currentTarget.setAttribute("aria-expanded", String(!open));});허용되는 경우에도 ARIA는 최소 범위로만 적용합니다. 중복된 ARIA는 접근성을 개선하지 않고 혼란만 증가시킵니다.
상태 변화를 보조 기술에 알리기 (라이브 영역)
섹션 제목: “상태 변화를 보조 기술에 알리기 (라이브 영역)”시각적으로만 표현되는 상태 변화는 라이브 영역으로 보조 기술에 전달합니다.
- 토스트·상태 메시지의 기본은
role="status"입니다. 끼어들어야 하는 실패·세션 만료만role="alert"를 씁니다. - 라이브 영역 컨테이너는 미리 DOM에 있어야 읽힙니다. “빈/로딩/에러/정상 4상태를 마크업에 미리 둔다”는 원칙과 같은 이유입니다. 4상태 안내문은 항상 렌더되는
i_status(role="status") 상자 안에 두고 안내문만 전환합니다 — 상자 자체가 숨었다 나타나면 읽히지 않습니다(화면 상태 설계). - 알림은 포커스를 옮기지 않습니다.
role="alert"메시지는 자동으로 사라지게 하지 않습니다.status토스트는 자동 소멸을 허용하되, 같은 정보를 다른 곳에서 다시 확인할 수 있어야 합니다.
— APG Alert · MDN Live regions · WCAG 4.1.3 · 2.2.1 · KWCAG 8.2.1 · 6.2.1
툴팁은 호버와 포커스 모두에서 뜨고, Esc로 포커스 이동 없이 닫히며, 마우스를 툴팁 위로 옮겨도 유지되어야 합니다.
연결은 aria-describedby(설명) 또는 aria-labelledby(라벨)로 합니다.
툴팁 안에 버튼·링크를 넣지 않습니다 —
그것은 툴팁이 아니라 팝오버/다이얼로그입니다.
— WCAG 1.4.13
복잡한 위젯은 위임
섹션 제목: “복잡한 위젯은 위임”셀렉트는 네이티브 <select>를 먼저 씁니다.
커스텀 콤보박스·날짜선택기·메뉴바가 꼭 필요하면
검증된 헤드리스 라이브러리에 동작(키보드·ARIA)을 맡기고,
SLUR는 구조·네이밍·data-state만 얹습니다.
외부 라이브러리 연동의 원칙 —
라이브러리는 상태 트리거일 뿐 — 과 같은 선상입니다.
헤드리스 라이브러리 다수가 상태를 data-state 속성으로 노출하므로
SLUR 네이밍과 그대로 호환됩니다.
ARIA를 추가하기 전에는 다음을 점검합니다.
- 시맨틱 HTML로 해결 가능한가
- 구조를 개선하면 ARIA 없이 해결되는가
- ARIA가 실제 사용자에게 도움이 되는가
- 상태 변경이 정확히 반영되는가
ARIA가 필요하다고 느껴질수록 먼저 구조 모델을 다시 점검합니다.