WCAG 2.2 · 4. 견고성 · 4.1 호환성
4.1.3 상태 메시지
Status Messages
검색 결과 수, "장바구니에 담았습니다", 진행 상황, 오류 개수 같은 상태 메시지는 포커스를 옮기지 않아도 보조 기술이 읽을 수 있도록 역할이나 속성으로 알려야 합니다.
한눈에 보기
- 성공 기준
- 4.1.3 상태 메시지
- 수준
- 수준 AA
- 원칙
- 4. 견고성
- 지침
- 4.1 호환성
- 도입
- WCAG 2.1
요구 사항
마크업 언어로 구현한 콘텐츠에서 상태 메시지는 역할(role)이나 속성(property)으로 프로그램이 판별할 수 있어야 하며, 그래서 포커스를 받지 않아도 보조 기술이 사용자에게 전달할 수 있어야 합니다.
WCAG의 정의에 따르면 상태 메시지는 맥락의 변화가 아닌 콘텐츠 변화로서, 다음 정보를 사용자에게 알려 주는 것입니다.
- 동작의 성공 또는 결과 — "장바구니에 담았습니다", "검색 결과 18건"
- 애플리케이션의 대기 상태 — "불러오는 중…"
- 프로세스의 진행 상황 — "업로드 60%"
- 오류의 존재 — "입력 항목 중 2개에 오류가 있습니다"
적용 범위: 새 콘텐츠로 포커스를 옮기는 경우(예: 대화상자가 열리며 포커스가 이동)는 상태 메시지가 아니므로 이 기준의 대상이 아닙니다. 또한 페이지 이동, 새 창처럼 맥락이 바뀌는 경우도 대상이 아닙니다. 이 기준은 "포커스는 그대로인데 화면 어딘가에 메시지가 나타나는" 상황을 다룹니다.
왜 필요한가요
시각장애인은 화면 구석에 나타난 "저장되었습니다" 같은 메시지를 볼 수 없습니다. 메시지를 읽게 하려고 포커스를 옮기면 작업 흐름이 끊깁니다. 라이브 리전(live region)은 포커스를 그대로 둔 채 화면낭독기가 메시지를 읽게 해 줍니다. 화면 확대 사용자처럼 메시지가 시야 밖에 나타나는 사용자에게도 도움이 됩니다.
도움받는 사용자
- 시각장애인(화면낭독기 사용자)
- 저시력(화면 확대) 사용자
- 인지장애 사용자
적용 방법
메시지의 성격에 맞는 라이브 리전 역할을 씁니다. 자세한 동작 원리는 라이브 리전 가이드와 알림 패턴을 참고하세요.
| 역할 / 속성 | 암묵적 값 | 쓰는 곳 |
|---|---|---|
status | aria-live="polite", aria-atomic="true" | 결과·성공 안내, 검색 결과 수, 저장 완료 |
alert | aria-live="assertive", aria-atomic="true" | 즉시 알려야 하는 중요한 오류·경고 |
log | aria-live="polite" | 채팅 메시지, 활동 기록처럼 순서대로 쌓이는 정보 |
progressbar | — | 진행률(값은 aria-valuenow) |
aria-live | — | 위 역할이 맞지 않을 때 직접 지정 |
1. 결과 안내: role="status"
<form role="search">
<label for="q">상품 검색</label>
<input id="q" type="search">
<button type="submit">검색</button>
</form>
<!-- 페이지를 처음 그릴 때부터 비어 있는 상태로 존재해야 합니다 -->
<p role="status" id="result-count"></p>function showResults(items) {
renderList(items);
document.getElementById('result-count').textContent =
'검색 결과 ' + items.length + '건';
}2. 장바구니 담기 안내
<button type="button" id="add">장바구니 담기</button>
<div role="status" class="toast" id="toast"></div>
<script>
document.getElementById('add').addEventListener('click', function () {
document.getElementById('toast').textContent = '장바구니에 담았습니다. (총 3개)';
});
</script>3. 오류 알림: role="alert"
폼을 제출할 때 포커스를 오류 요약으로 옮기지 않는 방식이라면, 오류의 존재를 alert로 알립니다. 남용하면 다른 안내를 끊고 읽으므로 정말 중요한 메시지에만 씁니다.
<div role="alert" id="form-error"></div>
<!-- 제출 실패 시 -->
<!-- form-error.textContent = '입력 항목 중 2개에 오류가 있습니다.'; -->4. 진행 상황과 대기 상태
<div role="progressbar" aria-label="파일 업로드"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="60"></div>
<p role="status" id="upload-msg">업로드 중… 60%</p>진행률을 1%마다 알리면 너무 시끄러우므로, 상태 메시지는 25%·50%처럼 의미 있는 단계나 완료 시점에만 갱신합니다. 영역 전체를 다시 그리는 동안에는 aria-busy="true"로 중간 상태를 읽지 않게 할 수 있습니다.
라이브 리전이 동작하지 않는 흔한 원인
- 라이브 리전 요소를 메시지와 동시에 DOM에 삽입하면 읽히지 않는 경우가 많습니다. 빈 컨테이너를 미리 두고 내용만 바꿉니다.
display:none이나hidden상태의 영역 안에서 바뀐 내용은 읽히지 않습니다.- 같은 문구를 다시 넣으면 변화로 인식하지 않을 수 있습니다. 내용을 비웠다가 다시 넣거나 문구를 조금 바꿉니다.
사례
눈에만 보이는 토스트 메시지
<div class="toast">저장되었습니다.</div>화면낭독기 사용자는 메시지가 나타난 것을 알 수 없습니다.
메시지와 함께 라이브 리전을 새로 삽입
var el = document.createElement('div');
el.setAttribute('role', 'status');
el.textContent = '저장되었습니다.';
document.body.appendChild(el); // 읽히지 않을 수 있음미리 둔 라이브 리전에 내용만 갱신
// HTML: <div role="status" id="msg"></div> (처음부터 존재)
document.getElementById('msg').textContent = '저장되었습니다.';채팅 메시지 기록
<div role="log" aria-label="상담 대화">
<p>상담원: 무엇을 도와드릴까요?</p>
<!-- 새 메시지가 아래에 추가되면 순서대로 읽힘 -->
</div>자주 하는 실수
- "저장되었습니다", "장바구니에 담았습니다" 같은 메시지를 시각적으로만 표시하고 라이브 리전을 쓰지 않습니다.
- 검색·필터 결과 수가 바뀌어도 보조 기술에 알리지 않습니다.
- 라이브 리전 요소를 메시지와 동시에 생성해 화면낭독기가 읽지 못합니다.
- 모든 안내에
role="alert"나aria-live="assertive"를 써서 사용자의 작업을 계속 끊습니다.
검사 방법
- 검색, 장바구니 담기, 저장, 업로드, 폼 제출 등 포커스 이동 없이 메시지가 나타나는 상황을 찾습니다.
- 개발자 도구에서 메시지 컨테이너에
role="status",role="alert",role="log",aria-live등이 있고, 메시지보다 먼저 DOM에 있는지 확인합니다. - 화면낭독기를 켠 채 동작을 실행해, 포커스가 그대로인 상태에서 메시지가 읽히는지 확인합니다.