← 전체 글로 돌아가기

Next.js

React 토스트 알림이 읽히지 않을 때 aria-live를 붙인 기준

화면에는 나타나지만 스크린 리더에는 전달되지 않던 토스트 알림을 수정하며 정한 기준입니다.

성공 메시지가 보인다고 끝이 아니었다

저장 버튼을 누르면 화면 오른쪽 아래에 “저장했습니다”라는 토스트를 3초 동안 보여 주고 있었다. 마우스로 테스트할 때는 문제가 없었지만 키보드와 스크린 리더로 확인하니 아무 안내도 나오지 않았다. 토스트 DOM은 새로 생겼지만 포커스가 이동하지 않았기 때문이다.

여기서 포커스를 토스트로 강제로 옮기는 방식은 선택하지 않았다. 입력하던 위치를 잃고, 반복 저장할 때 화면이 튀는 문제가 생겼다.

상태 변화는 라이브 리전에 맡기기

알림 컨테이너에 역할과 라이브 영역을 지정했다.

<div className="toast-region" aria-live="polite" aria-atomic="true">
  {message && <div role="status">{message}</div>}
</div>

실패처럼 사용자가 즉시 알아야 하는 메시지는 role="alert"를 별도로 사용했다. 모든 메시지를 alert로 만들면 스크린 리더가 작업을 끊고 읽어 버려서, 저장 완료 같은 일반 알림에는 polite가 더 적절했다.

타이머보다 먼저 정한 테스트

토스트가 사라지는 타이밍 때문에 테스트가 흔들리지 않도록 메시지 생성과 제거를 분리했다. 테스트에서는 타이머를 가짜 시계로 바꾸고 다음을 확인했다.

expect(screen.getByRole('status')).toHaveTextContent('저장했습니다')
act(() => jest.advanceTimersByTime(3000))
expect(screen.queryByRole('status')).not.toBeInTheDocument()

문자열만 바뀌는 경우에도 같은 DOM 노드 안에서 aria-atomic이 전체 문장을 읽게 했다. 반대로 진행률처럼 계속 바뀌는 값은 별도 영역으로 두지 않으면 읽기 큐가 쌓일 수 있다.

제가 남긴 접근성 체크리스트

  • 포커스를 빼앗지 않고 상태 변화를 전달하는가
  • 일반 안내와 긴급 오류의 우선순위를 구분했는가
  • 같은 메시지가 연속으로 나와도 읽을 수 있는가
  • 자동으로 사라져야 한다면 키보드 사용자가 내용을 확인할 시간이 있는가

작은 UI라도 “보인다”와 “전달된다”는 다른 조건이었다. 이번 수정에서는 기능 코드보다 알림의 의미와 읽는 순서를 먼저 정한 것이 가장 도움이 됐다.