동적 접근성
SPA 페이지 전환 시 침묵 현상을 방지하는 초점 제어와 aria-live 설계
Next.js 및 React 기반 SPA 환경에서 페이지 전환 시 스크린리더가 렌더링 변경을 인지하지 못하는 원인과, document.title 갱신, h1 초점 이동, aria-live 상태 알림 구현 기법을 다룹니다.
document.title을 갱신하고 신규 페이지의 <h1 tabIndex="-1">로 초점을 이동시켜 보조공학 침묵을 방지해야 합니다.실무 검증 기록
키보드 조작 및 초점 경로 검증
본 기록은 공개 fixture와 재현 조건을 기준으로 정리한 기술 설명입니다. 실제 브라우저·스크린리더 조합의 동작은 별도 수동 검토가 필요합니다.
| 검증 상태 | 공개 fixture 기반 재현 완료 |
|---|---|
| 검증 기준 | 공개 fixture 및 재현 조건 |
| 검증 범위 | SPA 클라이언트 라우팅 시 초점 제어 및 aria-live 알림 검증 및 명시된 입력값에 한정 |
| 수동 확인 | 실제 브라우저·키보드·스크린리더 환경에서 별도 확인 필요 |
| fixture에서 확인한 기대 결과 | [실패 상태] 새 콘텐츠를 보조기술이 인지하지 못함 ➔ [수정 목표] title·H1 초점·상태 알림을 각각 처리 |
검증에 사용된 실제 픽스처 / 재현 조건
버튼 클릭 후 URL/title만 변경되는 SPA검증 범위 및 한계
- 페이지 전환 애니메이션 도중 비동기 데이터 로딩 지연 시 스켈레톤 UI 초점 처리
- 브라우저 뒤로가기/앞으로가기(PopState) 시 직전 스크롤 위치 및 초점 복원
Single Page Application(SPA)에서 클라이언트 사이드 라우팅으로 페이지를 이동할 때, URL과 화면 콘텐츠는 갱신되지만 브라우저 전체 새로고침이 발생하지 않아 보조기술에 새 페이지 진입 사실이 전달되지 않는 문제가 발생합니다. 페이지 전환 시 document.title 갱신, 메인 제목(H1) 초점 관리 및 aria-live 알림 처리 구조를 분석합니다.
클라이언트 라우팅 시 발생하는 초점 고립과 침묵 현상
전통적인 다중 페이지 웹(MPA)에서는 페이지 이동 시 브라우저가 새 HTML 문서를 로드하며 문서 제목을 낭독하고 포커스를 최상단으로 초기화합니다.
반면 SPA에서는 router.push() 실행 후 이전 페이지에서 클릭했던 링크 위치에 초점이 머물거나 바닥 요소가 사라지면서 body로 튕겨, 보조기술 사용자가 페이지가 전환되었는지 인지하기 어렵습니다.
페이지 전환 감지 및 접근성 복구 3단계 절차
라우트 변경 시점에 문서 메타데이터 갱신과 초점 이동을 연계하여 사용자가 새로운 맥락을 인지할 수 있도록 지원합니다.
- 1단계: document.title을 고유한 페이지 제목으로 즉시 갱신
- 2단계: 새 페이지의 최상단 <main> 또는 <h1> 요소에 tabIndex={-1}을 부여하고 focus() 호출
- 3단계: 시각적 전환 안내와 함께 필요 시 aria-live='polite' 영역을 통해 페이지 로드 완료 알림 전달
초점 유실 및 상호작용 실패 원인 분석
| 초점 조작 실패 상황 | 정상 초점 제어 복구 방법 |
|---|---|
| SPA 클라이언트 라우트 변경 시 document.title만 바꾸고 초점을 이전 메뉴 위치에 방치하는 것 | 신규 렌더링된 페이지의 <h1> 요소에 tabindex='-1'을 부여하고 focus()를 호출하여 스크린리더 낭독 위치를 최상단으로 옮깁니다. |
| aria-live='assertive'를 남발하여 사용자가 듣고 있던 주요 음성 안내를 강제로 끊어버리는 것 | 페이지 전환이나 상태 안내에는 사용자 음성을 방해하지 않는 aria-live='polite' 속성을 사용합니다. |
| 동일 경로를 반복 방문할 때 aria-live 상태값이 변하지 않아 스크린리더가 라이브 리전 변경을 감지하지 못하는 것 | 라우트 전환 시 상태를 빈 문자열로 먼저 초기화한 뒤 다음 프레임에서 실제 알림 문구로 설정하여 DOM 변경을 강제로 발생시킵니다. |
수정 후 키보드 조작 회귀 점검 순서
- SPA 앱의 전역 레이아웃 컴포넌트에 aria-live='polite' aria-atomic='true' sr-only 스크린리더 전용 알림 컨테이너를 배치합니다.
- pathname 변경을 감지하는 useEffect 또는 afterEach 훅 내에서 document.title을 신규 페이지 제목으로 즉시 동기화합니다.
- 신규 페이지의 <h1> 요소에 tabindex='-1' 속성을 설정하고 pathname 변경 감지 시 .focus()를 실행하는 커스텀 훅을 전역 레이아웃에 연결합니다.
- NVDA(Windows) 또는 VoiceOver(macOS/iOS) 스크린리더를 실행하고 메뉴 클릭 후 새 페이지 H1 제목이 정상 낭독되는지와 document.title 갱신 여부를 각각 수동으로 확인합니다.
수동 키보드 탐색 시 주의할 한계
- aria-live="polite" 영역에 너무 긴 텍스트를 전달하면 사용자가 현재 탐색 중인 다른 음성 피드백을 방해할 수 있습니다.
- h1 요소에 초점을 맞출 때는 시각적 아웃라인이 디자인을 해치지 않도록 :focus:not(:focus-visible) 스타일을 적절히 조율해야 합니다.