모두를 위한 웹 접근성
무엇이고, 어떻게 하나요? 💬 🔉

웹 접근성, 모든 사람의 콘텐츠 접근을 지향하는 기술

김무훈, A11YKR 커뮤니티

https://a11ykr.github.io

발표 슬라이드 QR

발표자 소개

컴퓨터 공학 전공, 4학년 재학 중

약 3년 간 스타트업에서 프런트엔드 서비스 개발을 맡았습니다.

  • 재직했던 곳 — 펜슬컴퍼니, 유니크굿컴퍼니, 플라네타리움 (인턴)
  • 개인 기술 블로그 — https://frontend.moe

모두를 위한 접근성 지원웹 표준 동향, 오픈소스 참여에 주목하며
웹 프런트엔드 엔지니어링에 대한 관심을 가지고 있습니다.

⬆️ URL: https://muhun.kim

웹 접근성이 정확히 무엇이고, 어떻게 지원을 하는게 좋을지...

평소 고려했던 웹 접근성의 가치접근성 준수의 경험을 담았습니다.

발표 테마

🤔

웹 접근성
무엇일까요?

웹의 힘, 누구나 접근 가능한 콘텐츠

웹의 힘은 보편성에 있습니다. 장애와 관계없이 모두의 접근은 필수적인 측면입니다.
– Tim Berners-Lee, 웹의 창시자, Accessibility | Our mission | W3C

디지털 접근성이란 사용자의 정신적 또는 신체적 능력에 관계없이 웹사이트, 앱과 의미 있고 동등한 방식으로 상호작용할 수 있도록 제품을 설계하고 빌드하는 것을 의미합니다. 접근성 원칙 - 디지털 접근성은 어떻게 측정되나요? | web.dev

웹의 힘 = 보편성, 모든 사람이 콘텐츠를 쉽게 인식하고 활용할 권리 지향 🌐✨

여러분은 이미 접근성에 대해 고려하고 있습니다.

  • 제품을 만들 때, 첫 기획 단계에서 어떤 정보를 제공할지 정의
  • 대부분 정보는 사용자가 접근할 수 있는 내용, 이는 접근성 지원의 성공 기준 중 하나
  • 접근성 지원은 늘 고려하는 사용자 중심 디자인다른 관점이라고 해석 가능
  • 이 점을 간과하는 것은 "보안을 소홀히 하거거나, 나중에 고려하겠다"와 다를 바 없음

성공기준: "이 성공 기준의 의도는 텍스트가 아닌 콘텐츠로 전달하는 정보를 대체 텍스트를 사용하여 접근 가능하게 만드는 것이다."
 텍스트가 아닌 콘텐츠 | WCAG 2 이해, A11YKR Docs

"보안을 소홀히 하거나, 나중에 고려하겠다": 『접근성을 지원한다는 착각 | 뱅크샐러드』 게시글의 첫 문단의 내용을 인용했습니다.

때론 심미성 보다, 접근성 고려가 우선

어떻게 보일지 = 심미성 VS 무엇을 = 정보 접근성

열차 항목을 티켓 형태로 재디자인하면서 화면에 표시되는 개수가 6개에서 3개로, 절반으로 줄어듦

  • 인지 부하 증가: 동일한 정보를 얻기 위해 더 많은 스크롤과 탐색이 필요
  • 효율성 감소: 탐색에 할애하는 시간이 증가

  • 비교 기능 저하: 여러 선택지를 같이 비교하기가
    어려워짐

첫번째 사진: 코레일 앱 "코레일톡" 예매 화면,
두번째 사진: 리뉴얼 된 웹 서비스 예매 화면

모든 사람이 콘텐츠를 쉽게 인지 · 조작 · 이해할 수 있는 
보편성을 지향하는
 접근성 원칙을 POUR라고 부름

  • 인지: 대체 텍스트, 자막, 적은 모션
    저시력자를 위한 설정 - 고배율, 고대비
  • 조작: 키보드 및 터치 스크린 등 조작 기능의 품질
    키보드 초점 순서에 버그가 없는가?
  • 이해: 예측 가능한 상호작용과 직관적인 콘텐츠
  • 견고함: 다양한 입력·출력 장치에 제약 받지 않음

접근성 원칙 이해하기

인지 가능, 작동 가능, 이해 가능, 견고함은 모두 서로 연결됩니다.

삽화 출처: 디지털 접근성은 어떻게 측정되나요? | web.dev

접근성을
준수하는 방법

WAI-ARIA 🧭

시맨틱 마크업을 잘 설계하면 접근성이 보장

그러나 서드파티 UI 구현이 필요한 경우,
WAI-ARIA로 접근성을 관리해야 함 📐

W3C WAI에서 만든 ARIA 작성 방법 가이드(APG)

자주 쓰이는 여러 UI 패턴의 사례가 접근성을 염두하여 잘 소개됨

접근성 준수하기

WAI-ARIA 🧭

= Web Accessibility Initiative – Accessible Rich Internet Applications

  • WAI — W3C 산하의 웹 접근성 지원 기관
  • ARIA — 리치 애플리케이션의 접근성을 지원하는 표준 기술 명세

 

장애를 갖고 있는 사용자가 일반인과 동등한 웹 앱의 사용 경험을 제공하는 것이 목표

목표 달성을 위해 ARIAHTML 어트리뷰트를 확장하여 재정의

😵‍💫 이것만 기억해두세요,

ARIA = {역할, 상태, 속성}

ARIA는 웹 콘텐츠와 웹 애플리케이션을 만드는 방법을 정의하는

역할(Roles)과 상태(States), 속성(Properties)의 집합입니다. 
 ARIA - Accessibility | MDN

ARIA의 제공 기능

ARIA 역할

대부분의 HTML 시멘틱 태그는 이미 고유한 역할 존재

  • 🖲 <button> ➡️ role="button"

HTML 시멘틱 태그에 없는 UI 패턴은 역할 재정의하여 표현

  • 🎚 스위치 UI ➡️ <button role="switch" aria-checked="false/true">레이블</button>

  • 📋 서드파티 <select/> UI ➡️  role=listbox"
    • 옵션 UI ➡️ role="option"

ARIA 역할 재정의

<input type="search"
       value="신발 브랜드"
       aria-control="search-result"
       aria-expanded="false">
<ul role="listbox"
    id="search-result">
  <li role="option">
    스케처*
  </li>
  <li role="option">
    뉴발란*
  </li>
  <li role="option">
    나이*
  </li>
</ul>

ARIA 상태

HTML에 존재하지 않는 다양한 상태 제공, 두가지 예시:

  • 📂 aria-expanded  제어하는 복합적인 UI가 펼친 여부 표현
    • "button", "listbox", "tab" 역할에 쓰임
  • aria-selected — 현재 항목이 선택된 여부를 표현
    • "option", "tab" 역할에 쓰임

더 많은 ARIA 상태 목록은 "상태 탭" | WAI-ARIA 1.2 Cheat Sheet - DigitalA11Y 참고

ARIA 속성

🔗 관계 어트리뷰트 (요소 간 연결)

  • 🧭 aria-controls → 버튼(제어 트리거)↔ 리스트/팝업 제어

  • 🏷️ aria-labelledby → 레이블 연결, 💬 aria-describedby → 설명 연결

  • 🎯 aria-activedescendant → 현재 활성화된 하위 항목 명시,
    주로 리스트 박스 UI 패턴에서 사용

📡 동작 속성 (알림/구조)

  • 🔔 aria-live → 시간에 민감 - 타이머, 채팅 로그, 주가 정보 등

  • 🪜 aria-level → 계층 구조(H1, H2, H3 등 헤딩/트리 UI)

더 많은 관계 어트리뷰트 목록은 "6.6.4 Relationship Attributes, WAI-ARIA 1.2" 명세 참조

ARIA 속성

⚙️ 기능 속성

  • 🚫 aria-disabled / aria-readonly → 비활성화 또는 읽기 전용

  • ✳️ aria-required → 필수 입력 필드

  • 📝 aria-placeholder → 입력 플레이스홀더

  • 👁️‍🗨️ aria-hidden → 스크린 리더 등 보조 도구에서 장식용 UI를 숨기기 위해 사용

ARIA 속성까지, 최종 마크업 및 데모

<h2 id="brand-label">신발 브랜드</h2>

<div role="combobox"
  aria-expanded="true"
  aria-labelledby="brand-label"
  aria-controls="brand-list"
>
  <input type="search"
    value="나이*"
    aria-activedescendant="opt-nik*"
    aria-autocomplete="list"
  />
</div>

<ul role="listbox" id="brand-list">
  <li role="option" id="opt-nik*" aria-selected="true">나이*</li>
  <li role="option" id="opt-newbalanc*" aria-selected="false">뉴발란*</li>
  <li role="option" id="opt-skecher*" aria-selected="false">스케처*</li>
</ul>

<input />의 역할을 "combobox"로 재정의하는 것은 유효하지 않아, 별도의 <div role="combobox" /> 로 감쌌습니다.

스크린리더 사용 데모

옵션 이동 시 실제로 포커스가 이동하지 않고, aria-activedescendant 로 AT에게 가상 포커스 정보를 제공

ARIA 사용 전 고려 원칙

어디까지나 기본 = 시멘틱 마크업,

WAI-ARIA = 보완 수단

ARIA 사용 전 고려 원칙

  1. 🌱 Semantic HTML First: 기본 HTML 기능을 우선 고려
    • 역할과 기능을 하는 태그 · 어트리뷰트를 찾아 사용
  2. ✅ 필요한 경우만 보완적으로 사용
    • 잘못 사용된 ARIA는 주의가 필요
      원래 의도한 맥락과 다르게 해석되어, 접근성이 저하 됨
- <div role="button">주문하기
+ <button>주문하기</button>

+ <div role="tab">
-  <h2 role="tab">제목 탭</h2>
+  <h2>제목 탭</h2>
+ </div>

- <div role="my-role">

🚫 잘못된 ARIA 사용은 피하자

No ARIA is better than Bad ARIA — Read Me First | APG | WAI | W3C

위에서 ARIA를 언제-어떻게 사용하고, 사용하면 안 되는 상황에 대해 잘 설명함

조금 더 친절한 가이드 — WAI-ARIA를 배울 때 정리해 두고 싶은 것 (일본어)
일본어지만 브라우저 번역을 통해 충분히 읽으실 수 있습니다.

접근성을 염두에 두고
UI 설계하기
♿ 🏗️

접근성을 염두에 두고 UI 개발이 가능한가?

아래 항목은 접근성 지원에 대한 일반적인 오해를 두 명제로 정리한 것

  1. 웹 접근성은 장애 사용자만을 위한 특수한 사용자 경험을 지칭하는 기술 분야이다.

  2. 따라서 보조 기술(AT) 전용 UI를 별도 구현해야 해서 구현 시간이 더 늘어난다.

저의 답변

여러 가지 오해가 있지만, 접근성을 준수하는 것은 충분히 제시간에 달성할 수 있는 목표입니다.

경험상 접근성 인터페이스를 별도로 고려하지 않고, 일반 UI와 함께 설계하는 방식을 따르면 됩니다.

저는 지난 2023년에 펜슬컴퍼니에서 “글리프“라는 콘텐츠 투고
웹 서비스의 출시 준비 3개월 전부터 프런트엔드 개발에 참여,
적절히 서비스에 웹 접근성 기술이 지원되도록 노력을 했습니다.

접근성을 함께 고려한 배경 소개

접근성을 함께 고려한 배경 소개

  • 동료 디자이너와 색상 대비에 관한 의견을 종종 주고 받았습니다.
    • 저는 오랫동안 접근성에 관한 검토 의견을 자주 제시
  • FE 동료에게 시멘틱 마크업 준수WAI-ARIA 활용을 꾸준히 제안,
    접근성을 염두한 작업 방식이 사내 컨벤션으로 상당히 수용

접근성 개선 및 ARIA 활용에 대한 제안
개발 내역은 모두 오픈소스로 열람할 수 있습니다.

색상 대비 의견을 동료 디자이너와 주고 받는 스크린샷

재직했던 회사에서 공개 허가를 받고
공유드리는 대화 이력입니다.

접근성을 고려하여 UI 색상 변경하기 ♿ 🎨

<Tooltip enabled={invalidPrice} placement="top">
  <span slot="message" aria-live="assertive"
        id={errorTooltipId}>
    {errorMessages.join('<br/>')}
  </span>
  <input
    name="price"
	aria-invalid={invalidPrice}
	aria-describeby={errorTooltipId}
	inputmode="numeric"
</Tooltip>

<style>
input[name="price"][aria-invalid="true"] {
  border: 1px solid red;
  background-color: rgb(255, 0, 0, 0.5);
}
</style>

팝업 형태의 포인트 설정 UI

  • aria-invalid — 입력한 값이 유효하지 않다는 힌트 전달

접근성을 고려하여 UI 색상 변경하기 ♿ 🎨

<Tooltip enabled={invalidPrice} placement="top">
  <span slot="message" aria-live="assertive"
        id={errorTooltipId}>
    {errorMessages.join('<br/>')}
  </span>
  <input
    name="price"
+	class="aria-[invalid='true']:(border-error-900 bg-error-50)"
	aria-invalid={invalidPrice}
	aria-describeby={errorTooltipId}
	inputmode="numeric"
</Tooltip>




오픈소스, penxle/withglyph#1296에서 확인 가능

실제로는 Tailwind 환경에서 작업했습니다.

Tailwind CSS, ARIA 어트리뷰트 기본 지원

Tailwind는 일부 ARIA 상태에 대해 예약된 variants로 기본 지원합니다.

  • aria-busy, aria-checked, aria-expanded, aria-pressed, aria-selected 등
  • 지원하지 않은 ARIA 상태의 경우,
    aria-[invalid="true"]
     형태의 표기를 손수 써야 함

리스트박스 UI 열림과 닫힘을 표현하기

button.expanded {
  background-color: lightgray;
  & > i.chevron-icon {
    transform: rotate(0.5turn);
  }
}

button > i.chevron-icon {
  transition: transform 0.5s;
}

ul {
  position: absolute;
}

닫힌 상태

열린 상태

<Toolbar>
  <button type="button"
    aria-expanded={isFontListOpen}
    class={isFontListOpen ? 'expanded' : ''}
    onclick={() => isFontListOpen = !isFontListOpen}
  >
    프리텐다드 <i class="chevron-icon" />
  </button>
  <ul role="listbox" hidden={!isFontListOpen}>
    <li role="option"
      data-value="프리텐다드"
      onclick={onSelectFontValue}
    >
      프리텐다드
      <i class="selected-icon" />
    </li>
    // 기타 다른 옵션...
  </ul>
</Toolbar>

보조 기술과 일반 사용자 각각 별도의 인터페이스 구현

  • ARIA와 별도의 클래스네임을 활용하는 수고 존재

단일 마크업을 보조 기술과 일반 사용자 양측에 사용

  • ARIA와 별도의 클래스네임을 활용하는 수고가 줄어듬
  • 접근성 힌트인 ARIA를 사용하면 CSS의 가독성이 향상

닫힌 상태

열린 상태

- button.expanded {
+ button[aria-expanded='true'] {
  background-color: lightgray;
  & > i.chevron-icon {
    transform: rotate(0.5turn);
  }
}

+ button[aria-expanded='true'] + ul[role='listbox'] {
+   display: block;
+ }

열림과 닫힘을 ARIA 주도로 표현하기

<Toolbar>
  <button type="button"
    aria-expanded={isFontListOpen}
-   class={isFontListOpen ? 'expanded' : ''}
    onclick={() => isFontListOpen = !isFontListOpen}
  >
    프리텐다드 <i class="chevron-icon" />
  </button>
- <ul role="listbox" hidden={!isFontListOpen}>
+ <ul role="listbox">
    <li role="option"
      data-value="프리텐다드"
      onclick={onSelectFontValue}
    >
      프리텐다드
      <i class="selected-icon" />
    </li>
    // 기타 다른 옵션...
  </ul>
</Toolbar>

클래스네임은 HTML 의미를 대체할 수 없다.

CSS 명세에 따르면, 클래스네임은 HTML과 달리
의미를 담고 있지 않습니다. 따라서
클래스네임은 HTML의 의미를 대체할 수 없습니다.

"CSS는 클래스 어트리뷰트에 강력한 기능을 부여하여, 고유한 역할이 거의 없는 엘리먼트(예: HTML의 <div> 및 <span>)를 기반으로 나만의 “문서 언어"를 설계하고 “class” 어트리뷰트를 통해 스타일을 지정할 수 있습니다. 하지만 문서 언어의 구조적 요소는 일반적으로 정해진 의미가 있지만, 사용자가 임의로 정의한 클래스는 그렇지 않을 수 있습니다. 따라서 이러한 관행을 피하는 것이 좋습니다."
“Class Selectors” Selectors Level 3 | W3C

선택된 항목에 ✅ 아이콘 표시, 참 쉽죠?

li[role='option']
  > i.selected-icon {
  visually: hidden;
}

li[role='option'][aria-selected='true']
  > i.selected-icon {
  /* 선택되면 ✅ 아이콘 표시 */
  visually: visible;
}

열린 상태

<Toolbar>
  <button type="button"
    aria-expanded={isFontListOpen}
    onclick={() => isFontListOpen = !isFontListOpen}
  >
    프리텐다드 <i class="chevron-icon" />
  </button>
  <ul role="listbox">
    <li role="option"
+     aria-selected={true}
      data-value="프리텐다드"
      onclick={onSelectFontValue}
    >
      프리텐다드
      <i class="selected-icon" />
    </li>
    // 기타 다른 옵션...
  </ul>
</Toolbar>

방법론이지만... 실용적입니다. 💡

ARIA 표준은 시각적 스타일링 목적으로 만들어진 것이 아님,

세가지 웹 표준 명세 HTML, CSS, WAI-ARIA 에서
ARIA를 활용한 의미론적 CSS 작성은 찾을 수 없습니다.

  • 그러나 이 방법론은 의미론적 HTML 원칙을 준수합니다.
  • 여러 웹 표준의 메커니즘을 자산으로 한 실용적인 방법론.

초점 흐름 설계 🎯

  1. 탭 시퀀스 원칙
  2. 위젯 내 초점 이동 규칙

초점 흐름 설계 원칙 🎯

  1. 탭 시퀀스 원칙
    • Tab, ⇧⇥ Shift+Tab → 입력  이동

  2. 위젯 내 초점 이동 규칙
    • 아이템 사이 초점 이동: 방향키로 제한

    • Tab 키 입력 시: 다른 입력으로 포커스 이동

운영체제에서 보이는 일반적인 GUI 운용 방식을 따른 것

Tab, ⇠⇡⇣⇢ 방향키
펜슬컴퍼니의 또 다른 제품 "리더블"의 사이트 생성 단계 페이지

자세히 설명

  1. 탭 시퀀스 원칙
  2. 입력 내 초점 이동 규칙
    • 아이템 사이 초점 이동: 방향키로 제한

    • 선택 항목만 tabindex="0", 나머지 -1 (aka. "Roving tabindex")

Tab 키 입력 시: 탐색 중인 위젯을 벗어나 다른 입력으로 포커스 이동

운영체제에서 보이는 일반적인 GUI 운용 방식을 따른 것

관련하여 키보드 제어 컨설팅을 제공한 오픈소스 PR
penxle/readable#1128

일반적인 GUI 운용 방식: "but they are strongly advised to use the same key bindings as similar components
in common GUI operating systems as demonstrated in
APG Patterns."
Keyboard Navigation Inside Components | APG

돌아보기: Roving tabindex 기능 적용하기

<h2 id="brand-label">신발 브랜드</h2>

<div role="combobox"
  aria-expanded="true"
  aria-labelledby="brand-label"
  aria-controls="brand-list"
>
  <input type="search"
    value="나이*"
    aria-activedescendant="opt-nik*"
    aria-autocomplete="list"
  />
  <ul role="listbox" id="brand-list">
    <li role="option" id="opt-nik*"
        tabindex="0"
        aria-selected="true">나이*</li>
    <li role="option" id="opt-newbalanc*" tabindex="-1"
        aria-selected="false">뉴발란*</li>
    <li role="option" id="opt-skecher*" tabindex="-1"
        aria-selected="false">스케처*</li>
  </ul>
</div>

Tab, ⇧⇥ Shift+Tab 

다음 항목으로 이동하거나, 이전 항목으로 돌아가기

다양한 UI 패턴에 해당

적용 가능한 UI 패턴: 툴바, 콤보박스, 그리드, 메뉴, 라디오 그룹, 탭, 트리 뷰 입니다.
더 자세한 정보는 "기본 키보드 탐색 규칙, 키보드 인터페이스 개발 | ARIA 작성 방법 가이드"를 참고

Headless UI에
접근성 지원을 맡기기 ⚙️

Headless UI에 접근성 지원을 맡기기

접근성 표준에 맞춘 UI를 매번 직접 구현하는 것은 중복되고, 많은 시간이 소요 🤯
이를 위해 Headless UI라는 툴킷이 등장, 스타일링을 제외한 UI 로직과 접근성 지원만을 제공

접근성 지원을 고려하지 않더라도, 자주 사용되는 UI 패턴의 보일러플레이트 구현
의존성 주입(Dependency Injection)을 유연하게 지원하므로 적극 사용을 추천

UI 테스트에
접근성 기술 활용하기 🧪

UI 테스트의 단서, ARIA

"aria-control" 같은 ARIA 관계 어트리뷰트를 통해
각 HTML 엘리먼트 간의 관계를 명확히 표현 가능

관계성 명시는 보조 기술의 접근성 향상만 아니라,
UI 테스트를 구성할 때 유용한 힌트로 쓸 수 있음

import { render } from '@testing-library/svelte'
import Select from './Select.svelte'
	
const defaultSelectValue = '프리텐다드'
	
const rendered = render(Select)

// getByRole API를 이용하여 원하는 두 엘리먼트 쿼리하기
const triggerButton = rendered.getByRole('button')
const listbox = rendered.getByRole('listbox')

// triggerButton & listbox 관계성 일치하는지 확인
expect(triggerButton).toHaveAttribute('aria-controls', listbox.id)	
expect(triggerButton).toHaveAttribute(
  'aria-activedescendant',
  makeOptionIdByFontValue(defaultSelectValue)
)

Playwrighttesting-library UI 테스트 도구에서
접근성 트리를 바탕으로 한
getByRole 쿼리 사용을
우선적으로 권장

Playwright: "To make tests resilient, we recommend prioritizing user-facing attributes and explicit contracts such as page.getByRole()."  Locating elemets, Locators

testing-library: https://testing-library.com/docs/queries/about/#priority

접근성 트리

aka. 접근성 객체 모델(AOM)

name: 쇼핑
role: region
children:
- name: shoppingbox
  role: internal frame
  children:
  - name: shoppingbox
    role: document
    children:
    - name: 
      role: generic
      children:
      - name: 
        role: generic
        children:
        - name: 
          role: tablist
          children:
          - name: 쇼핑
            role: tab
            description: 쇼핑은 광고영역입니다.
            children:
            - name: 쇼핑
              role: text leaf
          - name: 맨즈
            role: tab
            description: 맨즈는 광고영역입니다.
            children:
            - name: 맨즈
              role: text leaf
          - name: 원쁠딜
            role: tab
            children:
            - name: 원쁠딜
              role: text leaf
          - name: 쇼핑라이브
            role: tab
            children:
            - name: 쇼핑라이브
              role: text leaf
        - name: 
          role: generic
          children:
          - name: 
            role: generic
            children:
            - name: '1'
              role: text leaf
            - name: 
              role: generic
            - name: 
              role: generic
            - name: "/"
              role: text leaf
            - name: '6'
              role: text leaf
          - name: 이전 페이지
            role: button
            children:
            - name: 
              role: generic
          - name: 다음 페이지
            role: button
            children:
            - name: 
              role: generic
        - name: 
          role: tabpanel
          children:
          - name: 
            role: tablist
          - name: 
            role: list

네이버 포털 쇼핑 섹션을 개발자 도구의 접근성 패널로 살펴보면,
“region(쇼핑) → tablist → tab…” 등으로 구성된 접근성 트리를 볼 수 있습니다.

  • 브라우저는 DOM 트리와 1:1 대응하는 AOM라는 데이터 구조를 생성함
  • DOM 트리의 역할, 이름, 상태 등 추상화하여 구성
  • 네이버 쇼핑 섹션 컨테이너의 역할은 "region"이고 이름이 "쇼핑"이라면, “영역, 쇼핑” 🔉라고 들려줍니다.

키보드 제어 및 기타 상호작용 모두 접근성 트리 기반 운용

UI 테스트에 접근성 기술 활용하기 🧪

A11YKR 커뮤니티 멤버 탐정토끼님의

접근성 트리를 응용한 국제화 테스트 자동화 공유 트윗

접근성 트리를 UI 테스트 관점에서 바라본다면,
접근 가능한 정보의 변화만을 추적할 수 있습니다.

  • 두 가지 UI 테스트 방법
    • Assertion: HTML 요소를 하나씩 쿼리하여 내용 확인
    • Snapshot: 비트맵 형식의 스크린샷 비교하여 감지
  • 접근성 트리 기반 🌳
import { test, expect } from '@playwright/test';

test('Banner contains expected elements', async ({ page }) => {
  await page.goto('https://playwright.dev/');

  const banner = page.getByRole('banner');

  await expect(banner.getByRole('heading', { level: 1 })).toHaveText(
    /Playwright enables reliable end-to-end/
  );

  await expect(banner.getByRole('link').nth(0));
    .toHaveText('Get started');
  await expect(banner.getByRole('link').nth(1))
    .toHaveText('Star microsoft/playwright on GitHub');
  await expect(banner.getByRole('link').nth(2)).toHaveText(
    /[\d]+k\+ stargazers on GitHub/
  );
});

Assertion Testing – UI 요소 일일이 확인하기 😫

getByRole('link').nth(2).toHaveText(...) 검증을 항목마다 반복합니다. 🥱

Snapshot Testing – 접근성 트리 기반 테스트 📸

import { test, expect } from '@playwright/test';

test('Banner contains expected elements', async ({ page }) => {
  await page.goto('https://playwright.dev/');

  const banner = page.getByRole('banner');

  await expect(page.getByRole('banner')).toMatchAriaSnapshot(`
	- banner:
		- heading /Playwright enables reliable end-to-end/ [level=1]
		- link "Get started"
		- link "Star microsoft/playwright on GitHub"
		- link /[\\d]+k\\+ stargazers on GitHub/
	`);
});

접근성 트리를 YAML 형식으로 표현하여 스냅샷 테스팅을 지원합니다. 😇
공식 소개 겸 데모 영상: Getting started with ARIA Snapshots - 유튜브

접근성 트리와 접근성 기술 간 흐름 도식화

접근성 트리와 접근성 기술 간 흐름 도식화

다만, 이러한 활용은 다양한 브라우저와 보조기술(AT) 사이의 지원이 맞물려야 유의미함

마치며 🏁

웹 접근성 준수가 가져다주는 이점 👍

  1. 확장성이 뛰어난 HTML ⚡
  2. 의미론적 CSS 선택자 🎨
  3. UI 테스트에 적합한 접근성 트리 🧪🌲
  4. 기계가 읽을 수 있는 문서 🤖 (Machine-Readable Document)
    모두가 쉽게 접근할 수 있는 웹 페이지는 기계에게도 동일하게 적용
    SEO뿐만 아니라, LLM에도 정보 색인이 더 원활

접근성의 가치를 지키기, 우리의 역할

"효율적인 소프트웨어 개발팀은 이러한 투쟁에서 정면으로 맞서 싸운다. 소프트웨어를 안전하게 지켜야 할 책임이 있기 때문이다."
— 『클린 아키텍처』 중에서

여기서 언급한 "투쟁"은 대립이 아닌 더 나은 방향으로 나아가기 위한 노력

“특별한 도움을 필요로 하는 이들을 위해 길을 닦는 것은 모두를 위해 길을 닦는 것과 같습니다." 장애를 가진 공립학교 학생에게 영감을 받았습니다 - 일러스트 작가 케빈 앨리
  • "경사로"는 디지털 접근성과 유사
  • "경사로"를 만드는 전문가 = 프런트엔드 개발자

위 삽화는 마이클 F. 지앙그레코(Michael F. Giangreco)라는
특수교육 전문가가 2002년에 제작한 유명한 자료입니다.

추천 자료 – 더 알아보기 위한 레퍼런스 📚

➡️ https://a11ykr.github.io "다 함께 접근성 배웁시다"

발표 자료 자문슬라이드 검수A11YKR 커뮤니티의 많은 도움을 받았습니다. 🙇

A11YKR 커뮤니티

모두를 위한 웹 접근성은 무엇이고, 어떻게 할까요? 💬 🔉

By Mu Hun

모두를 위한 웹 접근성은 무엇이고, 어떻게 할까요? 💬 🔉

접근성은 장애인을 위한 특수한 경험을 넘어 모든 사람의 폭 넓은 콘텐츠 접근을 지향하는 기술입니다. 웹 접근성이 무엇인지, 웹 접근성 준수와 함께 일관된 UI 제어 경험을 어떻게 설계하는지를 제 실무 경험에 비춘 방법론을 소개합니다. 따라서 접근성에 대한 막연함을 느끼는 경우 많은 의문이 풀릴 것입니다.

  • 1,084