콘텐츠로 이동

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가 필요하다고 느껴질수록 먼저 구조 모델을 다시 점검합니다.