-
[React Conf 2024] Demystifying Accessibility in React AppsStudy/개발 2024. 7. 3. 22:37
React Conf 2024에서 Demystifying Accessibility in React Apps라는 발표가 있었다.
이번 포스팅에서는 발표 내용을 정리하며 React 앱에서 접근성을 어떻게 구현할 수 있는지 알아본다.
접근성의 정의
- Accessibility: 장애가 있는 사람들도 동일하게 접근하고 사용할 수 있도록 하는 것.
- 실생활 예시: 휠체어 경사로, 점자 블록 등.
- 디지털 환경: 스크린 리더 등이 해당됨.
접근성의 동작 방식
- 웹 문서는 HTML로 받아 DOM 트리로 파싱하고 UI로 사용자에게 보여진다.
- 이 때, DOM 트리를 기반으로 접근성 트리(Accessibility Tree)가 생성된다.
- 접근성 트리는 개발자 도구에서 확인할 수 있다.
- DOM 노드처럼 접근성 tree의 노드도 name, role, focusable, description 등 다양한 속성을 가진다.
- 이 트리를 통해 보조 기술(Assistive Technology)이 동작한다.
- ex) 스크린 리더는 접근성 트리를 순회하며 각 노드의 정보를 읽는다.
- Semantic HTML이 중요한 이유는 여기에 있다.
- 모든 요소가 <div>로만 구성되어 있다면 각 노드의 의미를 알기 힘들어지고 접근성이 떨어진다.
ARIA - Accessible Rich Internet Applications
- ARIA는 접근성 트리를 제어할 수 있는 도구이다.
- 요소의 동작이나 외관을 변경하지 않고도 접근성을 향상시킬 수 있다.
- ex) aria-placeholder, aria-label 등
주의!
대부분의 경우 기본 HTML만 잘 사용해도 충분하다.
통계적으로 ARIA를 사용하는 곳에서 접근성 오류가 더 많이 발생한다.예시 1: 아이콘 버튼
icon + text
- 아이콘의 경우 접근성 관점에서 불필요할 수 있다.
- 이 경우 icon을 aria-hidden을 통해 접근성 tree에서 숨길 수 있다.
<button> <span aria-hidden="true">🔍</span> Submit </button>
icon만 있는 버튼
- 텍스트가 없는 아이콘 버튼 UI는 접근 가능한 이름이 없어서 스크린 리더가 "button"이라고만 읽는다.
- aria-label을 통해 이름을 줄 수 있지만 번역은 되지 않는다.
- 대신 버튼 안에 span 태그로 텍스트를 넣고 CSS로 숨길 수 있다.
const VisuallyHidden = ({ children }) => ( <span style={{ position: 'absolute', height: '1px', width: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap' border: 0, }}> {children} </span> ); const IconButton = () => ( <button> <span aria-hidden="true">🔍</span> <VisuallyHidden>Submit</VisuallyHidden> </button> );
예시 2: 텍스트 입력
- input 만으로는 어떤 값을 입력하는지 알 수 없기 때문에 반드시 연관된 label이 필요하다.
- input과 label을 연결하기 위해 htmlFor 속성을 사용해야 한다.
// input의 name을 찾지못함. <label>Username</label> <input type="text" name="username" /> // input이 label과 연결되어 어떤 input인지 찾을 수 있음. <label htmlFor={inputId}>Username</label> <input type="text" name="username" id={inputId} />
- 라벨을 UI에서 숨기고 placeholder를 통해 노출하는 경우도 있다.
- 이때, 아까 만든 VisuallyHidden 컴포넌트를 활용할 수도 있다.
// as를 통해 태그 다형성 지원 const VisuallyHidden = ({ as: Tag = "span", ...props }) => { return <Tag className="visually-hidden" {...props} />; } <VisuallyHidden as="label" htmlFor={inputId}>Username</VisuallyHidden> <input type="text" name="username" id={inputId} placeholder="Username" />
- 인풋 하단에 인풋에 대한 설명을 보여줄 때가 있다.
- ARIA를 활용해 이 설명이 해당 인풋의 설명인 것을 명시해줄 수 있다.
- 접근성 트리의 input 노드의 description 속성에 해당 설명이 들어가서 스크린 리더가 읽을 수 있다.
<label htmlFor={inputId}>Username</label> <input type="text" name="username" id={inputId} aria-describedby={hintId} /> <span id={hintId}> Must be between 3 and 20 characters long </span>
- input의 오류 상태는 aria-invalid를 통해 나타내줄 수 있다.
- aria-invalid는 스타일링에도 사용할 수 있다.
- 오류 메시지또한 aria-describedby에 추가해줄 수 있다. 여러 id가 가능하다.
<input type="text" name="username" id={inputId} aria-describedby={[errorId, hintId].join(' ')} /> <span id={errorId}> Must be between 3 and 20 characters long </span>
.input[aria-invalid="true"] { border-color: red; }
예시 3: 알림
- 알림은 보통 어떤 동작이 일어날 때 화면에 나타난다.
- 스크린 리더는 화면을 순차적으로 읽기 때문에 이를 바로 읽지 못할 수 있다.
- 이때 aria-live 영역을 사용할 수 있다.
- 페이지의 특정 부분을 라이브 영역으로 지정하면, 그 부분은 모니터링되며 스크린 리더에 의해 읽혀진다.
- aria-live="polite"와 aria-atomic="true"가 합쳐진role="status" 을 사용해도 된다.
aria-live = | "off" | "polite" // user가 idle 한 경우. 가장 많이 사용 | "assertive" // 변화 시 즉각적으로 읽기. 즉시 전달해야할 때 사용
// 이 영역에 변화가 생기면, screen reader에 전달되어 내용이 읽어진다. <div aria-live="polite"> {message} </div> // 또는, <div role="status"> {message} </div>
맺음말
접근성은 하나의 스펙트럼이다. 완벽하게 구현하지 않아도 조금씩 개선해 나가는 것이 중요하다. 접근성을 고려하여 React 앱을 개발하면 더 많은 사용자가 편리하게 이용할 수 있다. 모두가 접근 가능한 웹을 함께 만들어 나가자.
발표 자료
Demystifying accessibility in React apps
728x90'Study > 개발' 카테고리의 다른 글
더 이상 메인 에디터로 Neovim을 쓰지 않는 이유 (1) 2024.11.23 SSE 토이 프로젝트 - 프롬프터 만들기 (0) 2024.07.18 타입스크립트 5.5 베타 요약 (0) 2024.07.02 Tmux 마우스 드래그 오류 (0) 2024.07.01 Regular Expression 종류, 동작 방식, 성능 (2) 2024.03.23