DOM/ARIA 재현
동적 알림(Toast, Snackbar)에서 aria-live polite와 assertive의 올바른 선택 기준
화면 새로고침 없이 비동기 메시지를 스크린리더에 전달할 때 사용자의 음성 흐름을 끊지 않는 polite와 긴급 알림 assertive의 차이를 분석합니다.
실무 검증 기록
실무 판단 기준 및 판정 조건
본 기록은 공개 fixture와 재현 조건을 기준으로 정리한 기술 설명입니다. 실제 브라우저·스크린리더 조합의 동작은 별도 수동 검토가 필요합니다.
| 검증 상태 | 공개 fixture 기반 재현 완료 |
|---|---|
| 검증 기준 | 공개 fixture 및 재현 조건 |
| 검증 범위 | 동적 라이브 리전(Live Regions) 알림 검증 및 명시된 입력값에 한정 |
| 수동 확인 | 실제 브라우저·키보드·스크린리더 환경에서 별도 확인 필요 |
| fixture에서 확인한 기대 결과 | [실패 상태] 동적 알림 시 스크린리더 침묵 ➔ [수정 목표] role='status'와 polite 모드로 낭독 큐 안전 전달 |
검증에 사용된 실제 픽스처 / 재현 조건
div[role='status'][aria-live='polite'][aria-atomic='true'] toast container- 화면 낭독 도중 포커스 이동 시 음성 가로채기 동작 실기기 점검 필요
- 토스트 닫기(X) 버튼의 키보드 탈출 경로 수동 확인
모던 웹 애플리케이션에서 장바구니 담기 완료 토스트(Toast), 서버 저장 성공 알림, 혹은 네트워크 연결 끊김 경고와 같은 비동기 동적 메시지는 화면 새로고침 없이 실시간으로 생성되어 몇 초간 머물다 사라집니다. 눈으로 화면을 보는 일반 사용자는 화면 우측 상단이나 하단에 뜬 스낵바(Snackbar) 팝업을 즉시 인지하지만, 화면을 보지 못하는 시각장애인에게 이러한 시각적 팝업은 완전히 투명인간과 같습니다. 페이지 전체를 새로고침한 것이 아니므로 스크린리더는 아무런 소리도 내지 않고 침묵합니다. W3C WAI-ARIA는 이 문제를 해결하기 위해 DOM의 동적 변경 사항을 보조공학 엔진에 실시간 음성으로 쏴주는 '라이브 리전(Live Regions, aria-live)' 표준을 규정하고 있습니다. 그러나 aria-live='polite'와 aria-live='assertive'의 성격을 잘못 선택하면 사용자가 글을 읽는 내내 음성을 강제로 가로채어 극심한 짜증을 유발하는 부작용이 생깁니다. 이 글에서는 라이브 리전의 올바른 설계 원칙을 다룹니다.
라이브 리전(aria-live)이 동작하는 브라우저 내부 메커니즘
웹 브라우저는 기본적으로 사용자가 Tab 키나 가상 커서로 초점을 직접 이동시킨 요소의 정보만을 스크린리더에 전달합니다.
그러나 aria-live 속성이 선언된 컨테이너는 예외입니다. 사용자의 키보드 초점이 해당 컨테이너에 머물러 있지 않더라도, 자바스크립트가 해당 컨테이너 내부에 새로운 텍스트 노드를 삽입하거나 내용을 변경하는 순간, 브라우저 접근성 엔진이 이를 감지하여 스크린리더의 음성 합성기(TTS) 큐(Speech Queue)에 해당 문장을 즉각 밀어 넣습니다.
여기서 가장 중요한 핵심은 aria-live 속성의 '선언 타이밍'입니다. 알림 메시지를 화면에 띄우는 그 순간에 <div>를 새로 만들면서 동시에 aria-live 속성을 주면 일부 스크린리더는 이를 감지하지 못합니다. 반드시 웹페이지 초기 로드 시점에 빈 <div aria-live='...'> 컨테이너를 DOM에 미리 만들어두고, 알림이 발생했을 때 그 내부의 텍스트만 동적으로 갈아 끼워 넣어야 100% 안정적으로 음성이 출력됩니다.
aria-live="polite" vs aria-live="assertive"의 명확한 구분
W3C 가이드라인은 동적 알림의 긴급도에 따라 두 가지 레벨을 엄격히 구분하여 사용할 것을 요구합니다.
- aria-live='polite' (예의 바른 기본 모드): 스크린리더가 현재 읽고 있는 문장이나 사용자의 키보드 조작 낭독이 완전히 끝날 때까지 차분하게 기다린 뒤, 음성 공백 시간에 알림 문구를 부드럽게 들려줍니다. '장바구니에 상품이 담겼습니다', '임시 저장이 완료되었습니다', '검색 결과 15건이 로드되었습니다'와 같은 95% 이상의 일반 알림에 반드시 적용해야 하는 황금 표준입니다.
- aria-live='assertive' (긴급 가로채기 모드): 스크린리더가 현재 낭독하고 있던 모든 음성을 즉시 강제로 중단(Interrupt)하고, 현재 알림 메시지를 최우선으로 즉시 소리칩니다. 오직 '결제 세션 만료 1분 전', '네트워크 연결이 끊겼습니다', '화재 경보'와 같이 사용자의 즉각적인 안전이나 금융 자산 손실과 직결된 초긴급 재난 수준의 상황에만 극도로 절제하여 사용해야 합니다.
aria-atomic="true"와 aria-relevant 속성의 필수 결합
라이브 리전을 완벽하게 제어하기 위해서는 aria-atomic 속성을 함께 이해해야 합니다.
기본적으로 aria-atomic='false'인 상태에서는 컨테이너 내부에서 '실제로 변경된 단어'만 파편화되어 읽힙니다. 예를 들어 <div aria-live='polite'>현재 잔액: <span>50,000원</span></div>에서 금액만 60,000원으로 바뀌면 스크린리더는 단지 '60,000원'이라고만 낭독하여 앞뒤 문맥을 알 수 없게 됩니다.
컨테이너에 aria-atomic='true'를 선언해 두면, 내부의 일부 텍스트만 바뀌더라도 컨테이너 전체 내용인 '현재 잔액: 60,000원'을 처음부터 끝까지 완전한 문장으로 온전히 다시 낭독해 줍니다. 따라서 모든 토스트 알림창에는 항상 aria-atomic='true'를 함께 지정하는 것이 안전합니다.
role="alert"와 role="status"의 시맨틱 매핑
W3C ARIA 명세는 개발자의 편의를 위해 흔히 쓰이는 라이브 리전을 역할(Role)로 축약해 두었습니다.
role='status'는 암시적으로 aria-live='polite'와 aria-atomic='true'를 포함하므로 일반적인 상태 표시줄이나 토스트에 사용하기에 최적입니다.
반면 role='alert'는 암시적으로 aria-live='assertive'와 aria-atomic='true'를 내포하므로, 폼 전송 에러나 치명적인 시스템 오류 팝업에 명확한 의미론적 경고창으로 매핑할 수 있습니다.
예외 조건 및 수동 검토 범위
- 스크린리더 음성 출력 속도가 빠른 사용자에게 assertive 경고가 겹쳐서 들릴 수 있으므로 텍스트 길이를 간결하게 유지해야 합니다.
- 알림 메시지가 3초 후 화면에서 사라지더라도 저시력자를 위해 알림 내역을 다시 확인할 수 있는 알림함 UI를 병행 제공해야 합니다.