토스트/스낵바 접근성 완전정복: aria-live·역할·자동닫힘 타이밍 실무 가이드
왜 토스트/스낵바 접근성이 중요한가
웹앱에서 토스트·스낵바는 작게 뜨고 사라지지만, 중요한 상태 변화를 전달하는 유일한 경로가 되는 경우가 많습니다. 시각적 주목만 믿으면 스크린리더 사용자나 주의를 분산한 사용자는 내용을 놓치기 쉽습니다.
실무에서는 “저장됨” 알림이 자동으로 사라져 재시도 버튼을 보지 못하거나, 오류 메시지가 읽히지 않아 다음 단계로 진행해 버리는 문제가 자주 발생합니다. 이는 고객 지원 비용 증가와 이탈로 이어질 수 있습니다.
이 글은 aria-live, 역할(role), 자동닫힘 타이밍을 초보자 기준으로 정리해 “무엇을 설정해야 하고 무엇을 피해야 하는지”를 명확히 보여드립니다. 읽고 나면 토스트가 보이기만 하는 요소가 아니라, 누구에게나 들리고 이해되는 구성으로 바꿀 수 있습니다.
사례로 이해하는 핵심 개념: aria-live·role·자동닫힘
작동 원리는 간단합니다. 화면에 토스트가 나타날 때 스크린리더가 즉시 읽을지, 잠시 뒤에 읽을지, 아예 무시할지를 정하는 것이 aria-live와 역할(role)의 조합입니다.
메시지가 사라지는 속도는 자동닫힘 타이밍으로 통제합니다.
상황별 기본 구성이 다릅니다. 성공 알림은 aria-live="polite"로 큐에 넣어 현재 읽기를 방해하지 않게 하고, 오류·경고는 role="alert"(실제 live="assertive" 효과)로 즉시 읽히게 합니다.
자동닫힘은 맥락을 봅니다. 단순 확인 알림은 3~5초, 읽기가 긴 문장은 6~8초, 버튼이 있는 스낵바는 10초 이상 또는 사용자가 닫을 때까지 유지합니다.
> 안티패턴: 모든 토스트에 assertive를 쓰면 현재 폼 필드 안내를 끊어 사용성을 해칩니다. 긴 메시지일수록 polite를 우선 고려하세요.
실무 체크리스트:
- 정보성: role="status" 또는 aria-live="polite", 3~5초
- 오류/치명적: role="alert", 6초 이상 또는 수동 닫기
- 액션 포함: 포커스 이동 없이 키보드 접근 가능, 10초 이상 유지
- 반복 알림: 내용이 같으면 중복 노출·읽기를 방지하는 디디플리케이트 처리
바로 적용하는 설정·타이밍 체크리스트
빈 컨테이너 1개를 페이지에 고정 배치해 토스트를 모읍니다. 시맨틱을 위해 div에 role과 aria 속성을 상황별로 부여합니다.
- 컨테이너: position: fixed, z-index 충분, 포커스 이동 없음
- 성공/정보: role="status" 또는 aria-live="polite"
- 오류/위험: role="alert" (암시적 assertive)
- 중복 방지: 동일 키의 메시지는 합치거나 갱신
문구는 2줄 이내로 요약하고 첫 단어에 유형을 드러냅니다. 예: “성공: 저장되었습니다”, “오류: 네트워크가 불안정합니다”.
- 길이 기준: 90~120자 이내
- 링크·버튼 포함 시 동사 우선: “되돌리기”, “다시 시도”
- 아이콘은 보조적, 텍스트로 의미 전달 필수
자동닫힘은 내용과 상호작용 유무로 결정합니다. 읽기+행동이 필요한 스낵바는 충분한 시간을 줍니다.
- 정보성: 3–5초
- 긴 문장: 6–8초
- 버튼 있음: 10초 이상 또는 사용자가 닫을 때까지
- 마우스 호버·포커스 시 타이머 일시정지
키보드와 스크린리더 고려 사항을 함께 점검합니다. 토스트가 떠도 포커스는 원래 위치를 유지합니다.
- 닫기 버튼 제공: button 요소, aria-label="닫기"
- 스페이스/엔터로 동작, ESC로 닫기 지원
- 등장 시점에만 라이브 영역으로 읽히고, 사라질 때는 조용히 제거
> 팁: assertive 남용은 현재 안내를 끊습니다. 폼 작성 중엔 오류처럼 치명적 경우에만 alert, 나머지는 polite로 큐에 넣으세요.
흔한 실수와 선택 기준 한 번에 정리
짧게 보여도 토스트는 읽기 흐름을 끊을 수 있습니다. 역할과 타이밍을 잘못 고르면 핵심 메시지가 묻히거나 과도한 방해가 생깁니다.
- 모든 토스트에 role="alert" 사용: 폼 안내·내비게이션 읽기를 중단시켜 피로도를 높입니다.
- 자동닫힘 2초 내외 설정: 스크린리더가 문장을 끝까지 읽기 전에 사라질 수 있습니다.
- 포커스 강제 이동: 토스트로 포커스를 옮기면 키보드 사용 흐름이 꼬입니다.
- 동일 메시지 연속 송출: 큐가 길어져 중요한 경고가 늦게 도착합니다.
선택은 유형과 맥락으로 나눕니다. 정보·성공은 polite 또는 role="status", 오류·경고는 role="alert"를 우선 검토하세요.
- 읽기 길이로 타이머 결정: 짧음 3–5초, 중간 6–8초, 행동 포함 10초+ 또는 수동 닫기
- 상호작용 포함 시 포커스는 그대로, 토스트 내부 버튼만 Tab 순서에 포함
- 중복 메시지는 합치거나 최신으로 교체
- 화면 리더 우선 문구: “오류: 네트워크 불안정. 다시 시도 버튼 제공”
> 안티패턴: 시각적 애니메이션을 길게 두고 타이머는 짧게 두면, 보이지만 듣기에는 늦습니다. 애니메이션 길이 ≤ 읽기 시간으로 맞추세요.
비교 기준은 방해 수준과 실패 비용입니다. 즉시 알려야 할 위험은 assertive, 나머지는 줄 세워 읽히게 두는 편이 안전합니다.
- 사용자 행동이 취소 가능하면 스낵바로 되돌리기 제공
- 네트워크 재시도처럼 실패 비용이 큰 작업은 자동닫힘 지연
- 교육·도움말 성격은 모달·배지 등 다른 패턴 고려
- 다국어 환경은 번역 길이 증가를 감안해 타이머 여유 확보
지금 적용할 실행 요약
핵심은 유형에 맞는 역할과 충분한 표시 시간입니다. 정보·성공은 polite/role="status", 오류·경고는 role="alert", 행동이 있으면 더 오래 표시가 기본입니다.
바로 점검하세요.
- 컨테이너 1개 고정 배치, 포커스 이동 금지
- 성공/정보: role="status" 또는 aria-live="polite"
- 오류/경고: role="alert"
- 자동닫힘: 짧음 3–5초, 중간 6–8초, 행동 포함 10초+ 또는 수동 닫기
- 중복 메시지 합치기, 문구 2줄 이내로 요약
다음 행동을 추천합니다.
- 디자인 시스템에 토스트 유형·타이밍 표를 추가
- QA에서 스크린리더로 성공/오류 시나리오 읽힘 여부 확인
- 로깅으로 표시 시간과 닫힘 이유(타임아웃/수동)를 수집해 조정
> 팁: assertive 남용은 현재 안내를 끊습니다. 긴 문장과 빈번한 알림은 polite 우선, 꼭 즉시 알려야 할 위험만 alert로 한정하세요.
쉬운 토스트 접근성 가이드
토스트는 짧게 보여도 핵심만 들리면 됩니다. 성공, 오류, 버튼 있는 스낵바를 유형별로 나눠 다르게 처리하세요.
role은 스크린 리더의 읽기 우선순위를 정합니다. aria-live는 읽는 시점을 정하는데, polite는 나중에, assertive는 바로 읽습니다.
- 성공/정보: role="status" 또는 aria-live="polite", 표시 3–5초
- 오류/경고: role="alert"(assertive), 표시 6–8초
- 버튼 스낵바: role="status"+버튼 포커스, 표시 10초 이상 또는 수동 닫기
아래 코드는 구조 예시입니다. 실제 앱에서는 상태별 클래스와 타이머로 제어하세요.
<!-- 루트 컨테이너(포커스 고정) -->
<div id="toast-root" aria-live="off" aria-relevant="additions" class="toast-root"></div>
<!-- 성공 -->
<div role="status" class="toast toast--success" data-timeout="4000">
<span>성공: 저장되었습니다.</span>
</div>
<!-- 오류 -->
<div role="alert" class="toast toast--error" data-timeout="7000">
<span>오류: 네트워크가 불안정합니다.</span>
</div>
<!-- 스낵바(행동 포함) -->
<div role="status" class="toast toast--action" data-timeout="12000" aria-atomic="true">
<span>정보: 초안을 복원할 수 있습니다.</span>
<button type="button" class="toast__action">되돌리기</button>
</div>
status는 현재 읽기를 끊지 않고 대기열에 넣습니다. alert는 즉시 읽습니다.
> 안티패턴: 모든 토스트를 alert로 만들면 폼 안내가 끊깁니다. 긴/잦은 알림은 polite/status로 낮추고, 정말 긴급할 때만 alert를 쓰세요.