일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- CSS
- 솔리디티
- 블록체인
- 백준
- 타입스크립트
- 자바스크립트
- react
- kubernetes
- docker
- 가상화
- 컴퓨터공학
- es6
- TypeScript
- AWS
- 쿠버네티스
- next.js
- HTML
- VUE
- 파이썬
- 프론트엔드
- 백엔드
- 클라우드
- 웹
- 리액트
- BFS
- k8s
- 이슈
- 알고리즘
- 이더리움
- JavaScript
- Today
- Total
즐겁게, 코드
시각장애인에게 친절한 모달 컴포넌트 만들기 본문
일반인들은 마우스로 원하는 링크나 버튼을 눌러 웹 페이지를 자유롭게 탐색할 수 있지만, 시각장애인들은 주로 키보드의 Tab 또는 Shift + Tab 또는 별도의 장치를 통해 HTML 태그를 계층적으로 탐색합니다.
💡 시각장애인의 웹 탐색 방법이 궁금하다면 스크린 리더 라는 도구에 대해 검색해 보는 것을 추천드립니다.
따라서 프론트엔드 개발자들은 시각장애인들이 일반인과 비슷한 수준의 탐색을 경험할 수 있도록 접근성을 신경쓸 필요가 있는데요, 오늘은 모달(팝업) 컴포넌트를 개발할 때 놓치기 쉬운 접근성을 다뤄 보려 합니다.
예시를 위해 간단한 모달을 제작한 모습입니다.
export const Modal = ({ open, handleClose }: Props) => {
// Backdrop, CloseButton 등의 마크업 코드는 생략하겠습니다.
return open
? createPortal(
<Backdrop>
<CloseButton onClick={handleClose}>닫기</CloseButton>
<Body>
<Header>오늘의 주요 뉴스</Header>
<p>환율 1470원 돌파, 이대로 괜찮은가?</p>
<Author>김철수 기자</Author>
<button>기사 후원하기</button>
</Body>
</Backdrop>,
document.body
)
: null;
};
일반인은 짙은 회색의 Backdrop 영역과 흰 배경을 통해 모달이 열린 것을 알 수 있기 때문에 평범한 모달처럼 보이지만, 시각장애인들은 스크린 리더가 읽어주는 HTML 태그의 속성만으로 이를 구분해야 하기 때문에 팝업이 열리기는 했는지, 이것이 팝업이 맞기는 한지 인식하기 어려워할 가능성이 있습니다.
개선점 1. aria- 접근성 태그 추가하기
가장 기본적인 방법은 스크린 리더가 UI의 힌트를 제공할 수 있도록 접근성 속성을 추가하는 것입니다.
export const Modal = ({ open, handleClose }: Props) => {
return open
? createPortal(
<Backdrop
// 스크린 리더가 이 모달이 팝업 요소임을 알려줄 수 있게 됩니다.
role="dialog"
aria-modal="true"
// "오늘의 주요 뉴스 팝업"
aria-labelledby="modal-header"
>
<CloseButton onClick={handleClose}>닫기</CloseButton>
<Body>
<Header id="modal-header">오늘의 주요 뉴스</Header>
<p>환율 1470원 돌파, 이대로 괜찮은가?</p>
<Author>김철수 기자</Author>
<button>기사 후원하기</button>
</Body>
</Backdrop>,
document.body
)
: null;
};
모달 컴포넌트에 role="dialog"
, aria-modal="true"
속성을 부여하면 스크린 리더는 이것이 평범한 <div>
나 <main>
태그가 아닌 모달 UI라는 힌트를 시각장애인에게 전달할 수 있게 됩니다.
크롬 개발자 도구에서 접근성 트리를 확인해 보면 마크업 구조는 수정하지 않았음에도 "닫기" 버튼이 본문과 함께 모달 내에 묶이게 되었고, 현재 탐색하고 있는 UI가 "오늘의 주요 뉴스" 모달임을 명시적으로 안내할 수 있게 되었습니다.
개선점 2. Escape 키로 모달을 닫을 수 있도록 추가하기
앞서 말했듯 시각장애인은 "닫기" 버튼을 곧바로 마우스로 클릭할 수 없고, 키보드 또는 보조 입력 도구를 통해 HTML 계층을 순차적으로 탐색하게 됩니다.
따라서 컨텐츠가 많은 팝업에서는 컨텐츠를 조회하다가 팝업을 닫기 위해 뒤로가기 키를 여러 차례 눌러야 하는 불편을 겪을 수 있는데요, 이런 경험을 개선하기 위해 ESC 키를 눌렀을 때 팝업이 닫히는 기능을 추가할 필요가 있습니다.
const Modal = ({ open, handleClose }: Props) => {
// ESC 키로 모달 닫기
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape" && handleClose) {
handleClose();
}
};
if (open) {
document.addEventListener("keydown", handleKeyDown);
}
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [open, handleClose]);
return open
? createPortal(
<Backdrop
role="dialog"
aria-modal="true"
aria-labelledby="modal-header"
>
<CloseButton onClick={handleClose}>닫기</CloseButton>
<Body>
<Header id="modal-header">오늘의 주요 뉴스</Header>
<p>환율 1470원 돌파, 이대로 괜찮은가?</p>
<Author>김철수 기자</Author>
<button>기사 후원하기</button>
</Body>
</Backdrop>,
document.body
)
: null;
};
이처럼 keydown 이벤트에 간단한 핸들러를 추가하는 것으로 시각장애인들이 컨텐츠를 읽다가 Shfit + Tab을 수 차례 눌러 닫기 버튼을 다시 찾아야 하는 불편을 해결할 수 있게 되었습니다.
개선점 3. 포커스 트랩 구현하기
많은 불편이 개선되었지만 아직 한 가지 문제가 남아 있습니다.
시각장애인이 Tab 키를 통해 페이지를 탐색하는 과정에서 모달 바깥의 요소가 focus 동작의 대상이 될 수 있다는 점인데요, 이로 인해 UI상 기존 화면 위에 오버레이된 모달과 그 밑에 깔린 요소들을 분간할 수 없게 되어 탐색에 혼란을 주게 된다는 문제가 있습니다.
이를 해결할 수 있는 방법이 바로 focus trap 이라는 기법입니다.
말 그대로 포커스를 가둔다는 의미로 모달이 열려 있는 동안에는 모달 내부의 요소들만 탭을 통해 포커스가 가능하도록 하는 것인데요, 요즘은 이를 직접 구현하지 않아도 focus-trap (바닐라) 나 focus-trap-vue, focus-trap-react 를 통해 간단하게 구현할 수 있습니다.
import { FocusTrap } from "focus-trap-react";
const Modal = ({ open, handleClose }: Props) => {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape" && handleClose) {
handleClose();
}
};
if (open) {
document.addEventListener("keydown", handleKeyDown);
}
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [open, handleClose]);
return open
? createPortal(
{/* 모달이 열려있는 동안에는 Focus 가능한 영역을 모달 내부 요소로 한정합니다. */}
<FocusTrap>
<Backdrop
role="dialog"
aria-modal="true"
aria-labelledby="modal-header"
>
<CloseButton onClick={handleClose}>닫기</CloseButton>
<Body>
<Header id="modal-header">오늘의 주요 뉴스</Header>
<p>환율 1470원 돌파, 이대로 괜찮은가?</p>
<Author>김철수 기자</Author>
<button>기사 후원하기</button>
</Body>
</Backdrop>
</FocusTrap>,
document.body
)
: null;
};
이제 시각장애인이 Tab 키를 실수로 잘못 누르더라도 모달 외부의 영역으로 포커스가 이동하지 않아, 사용자가 실수로 탐색 중이던 영역을 벗어나거나 집중을 잃을 가능성을 줄일 수 있게 되었습니다.
마치며
위 세 가지 과정을 거쳤다고 해서 시각장애인들을 위한 모든 조치가 이루어진 것은 아니라고 생각합니다.
그동안은 저 역시도 비장애인의 위치에서 마크업을 빠르게 작성하는 데에만 초점을 맞춰 오지 않았나 돌아보게 되었는데요, 웹 접근성 인증 마크를 따기 위해서가 아닌 내 서비스를 애용하는 한 명의 시각장애인을 위해서라도 편리한 웹 접근성을 제공하는 습관을 들여 보려 합니다.
'🎨 프론트엔드' 카테고리의 다른 글
TreeWalker 객체로 DOM을 순회하며 필터 적용하기 (0) | 2024.12.07 |
---|---|
스크롤 UI 구현하기 : Swiper Free Mode VS. overflow-x: scroll (0) | 2024.11.26 |
XSS 공격의 위험성 체험하기 (1) | 2024.11.14 |
페이지 노출 여부를 판별하는 Page Visibility API 살펴보기 (0) | 2024.06.02 |
Server-Sent Event로 데이터 구독하기 (2) | 2024.04.27 |