사전 접근성 트리
개념

접근성 트리

gabury1고친 사람 github-actions[bot]

접근성 트리는 화면을 소리로 읽어 주는 프로그램에게 웹 페이지의 부품을 알려 줍니다. 브라우저가 웹 문서에서 사용자가 읽고 누르는 부품만 골라 따로 세워 둡니다. 브라우저 테스트 도구도 이 트리를 보고 부품을 찾습니다.

쉽고 빠른 이해

접근성 트리는 웹 페이지에 무엇이 있는지를 화면 없이 전하는 명단입니다. 「저장」이라고 적힌 버튼이 있으면 명단에 「버튼, 이름은 저장」 한 줄이 오릅니다.

화면을 소리로 읽어 주는 프로그램은 화면에 칠해진 그림을 알아보지 못합니다. 브라우저가 명단을 넘겨야 무엇을 누를 수 있는지 사용자에게 전합니다.

  1. 브라우저가 웹 문서를 읽어 태그마다 무엇인지 정합니다
  2. 꾸밈용 상자는 빼고 부품만 남겨 명단을 세웁니다
  3. 읽어 주는 프로그램과 테스트 도구가 이 명단에서 부품을 찾습니다

대가는 화면과 명단이 어긋날 수 있다는 것입니다. 눈에는 버튼으로 보여도 명단에는 글자 한 줄로 오를 수 있습니다. 그러면 화면을 못 보는 사용자는 그것이 누르는 부품인 줄 모릅니다.

상세

큰 건물 입구에는 손으로 더듬어 읽는 점자 안내판이 붙어 있곤 합니다. 벽 색깔이나 장식은 안내판에 없습니다. 층마다 무엇이 있는지와 그곳을 무엇이라 부르는지만 적혀 있습니다.

접근성 트리가 웹 페이지의 점자 안내판입니다. 화면의 생김새는 뺍니다. 부품이 무엇인지와 무엇이라 부르는지만 남깁니다.

트리를 읽는 보조 기술

접근성은 장애가 있는 사람도 남들과 똑같이 쓸 수 있는 정도를 말합니다. 접근성 트리는 그중 화면을 보지 않고 웹을 쓰는 사람을 위한 장치입니다.

보조 기술은 장애가 있는 사용자가 컴퓨터를 쓰도록 돕는 소프트웨어와 기기입니다. 그중 스크린 리더는 화면의 글과 부품을 소리나 점자로 읽어 줍니다. 사용자는 키보드로 부품 사이를 옮겨 다닙니다. 부품에 닿을 때마다 「저장, 버튼」 같은 안내를 듣습니다.

스크린 리더는 화면에 칠해진 점을 읽지 않습니다. 버튼처럼 생긴 그림이 있어도 그것이 버튼인지 그림만 보고는 알 수 없습니다. 그래서 브라우저가 「이건 버튼이고 이름은 저장이다」를 따로 정리해 건넵니다. 그 정리가 접근성 트리입니다.

웹 문서에서 세우는 두 번째 트리

브라우저는 웹 문서 하나를 읽어 트리를 여럿 세웁니다. 접근성 트리는 그중 화면을 그리는 트리와 나란히 세우는 둘째 트리입니다. 두 트리는 같은 뿌리에서 갈라져 나옵니다.

HTML(HyperText Markup Language) 문서는 태그로 짜여 있습니다. <button>저장</button> 처럼 여는 태그와 닫는 태그가 감싼 덩어리 하나를 요소라고 합니다. 버튼 하나, 제목 하나가 각각 요소입니다.

브라우저는 요소마다 객체를 하나씩 만듭니다. 객체들은 부모와 자식으로 이어져 트리를 이룹니다. 이 트리가 DOM(Document Object Model, 문서 객체 모델)입니다.

트리를 이루는 이 객체 하나하나를 노드라고 합니다. 그러니 요소는 저마다 DOM 노드 하나가 됩니다. DOM 에는 문서에 적힌 것이 전부 노드로 들어갑니다. 배치를 잡으려고 겹겹이 감싼 <div> 도, 화면에 안 나오는 <script> 도 노드가 됩니다.

브라우저는 DOM 에서 두 갈래를 뽑습니다. 한 갈래는 화면에 그릴 것만 고른 렌더 트리입니다. 다른 갈래가 보조 기술에 전할 것만 고른 접근성 트리입니다. 앞에서 부품이라 부른 것은 접근성 트리에 오르는 노드입니다.

브라우저는 접근성 트리를 운영체제에 넘깁니다. 운영체제는 화면의 부품 정보를 보조 기술에 건네는 통로를 따로 둡니다. 이 통로가 접근성 API(Application Programming Interface, 프로그램끼리 기능을 주고받는 약속)입니다. 스크린 리더는 웹 페이지든 일반 프로그램이든 이 통로 하나로 읽습니다.

flowchart TD
    H["HTML 문서"] --> D["DOM"]
    D --> R["렌더 트리"]
    D --> A["접근성 트리"]
    R --> S["화면 · 눈으로 보는 사용자"]
    A --> P["운영체제의 접근성 API"]
    P --> SR["스크린 리더 · 소리나 점자"]

같은 문서가 두 갈래로 나뉩니다. 눈에 보이는 모습과 스크린 리더가 전하는 내용은 서로 다른 트리에서 나옵니다. 둘이 어긋날 수 있는 까닭이 이 갈림에 있습니다.

스크린 리더가 HTML 을 직접 읽으면 안 되나 싶을 수 있습니다. 무엇이 숨었는지 알려면 CSS(Cascading Style Sheets)까지 계산해야 합니다. CSS 는 요소의 생김새와 표시 여부를 적는 언어입니다. 브라우저는 화면을 그리려고 이 계산을 이미 합니다. 그 결과를 정리해 넘기면 보조 기술은 계산을 되풀이하지 않습니다.

노드마다 붙는 역할 · 이름 · 상태

접근성 트리의 노드는 세 가지 정보를 듭니다. 스크린 리더가 부품에 닿았을 때 읽어 주는 것이 이 셋입니다.

정보 답하는 물음 예
역할 이 부품이 무엇인가 버튼 · 링크 · 체크박스 · 제목
이름 무엇이라 부르나 저장 · 약관 동의
상태 지금 어떤가 체크됨 · 펼쳐짐 · 누를 수 없음

「약관 동의」라는 글이 붙은 체크박스로 보겠습니다. 역할은 체크박스, 이름은 약관 동의, 상태는 체크됨이나 체크 안 됨입니다. 스크린 리더는 이것을 「약관 동의, 체크박스, 체크 안 됨」처럼 이어 읽습니다.

입력 칸에는 지금 들어 있는 값도 붙습니다. 검색 칸에 「우산」을 쳤다면 그 노드의 값은 우산입니다.

역할을 정하는 태그와 ARIA

노드의 역할은 대개 태그가 정합니다. <button> 은 버튼, href 가 붙은 <a> 는 링크, <h2> 는 제목이 됩니다. 태그가 스스로 들고 있는 이 역할을 암묵적 역할이라고 합니다.

<div> 와 <span> 은 아무 역할이 없는 상자 태그입니다. 겉모습을 꾸미려고 쓰는 태그라 전할 뜻이 없습니다. 아래 다섯 줄의 오른쪽 주석은 각 줄이 접근성 트리에 올리는 역할입니다.

HTML
<button>저장</button>         <!-- 버튼 -->
<a href="/cart">장바구니</a>   <!-- 링크 -->
<h2>주문 내역</h2>            <!-- 제목 -->
<div>저장</div>               <!-- 없음 -->
<div role="button">저장</div> <!-- 버튼 -->

넷째 줄은 CSS 로 버튼처럼 꾸며도 스크린 리더에게는 「저장」이라는 글자일 뿐입니다. 다섯째 줄은 role 속성으로 역할을 적어 이 빈틈을 메웁니다.

role 은 ARIA(Accessible Rich Internet Applications)가 정한 속성입니다. ARIA 는 태그에 속성을 덧붙여 접근성 트리의 역할 · 이름 · 상태를 바꾸는 표준입니다. 화면의 모양과 동작은 바꾸지 않습니다. 그래서 role="button" 을 붙여도 키보드로 눌리게 하는 코드는 따로 짜야 합니다.

이름을 정하는 재료

이름은 여러 재료에서 옵니다. 브라우저는 정해진 순서로 재료를 훑어 처음 찾은 것을 씁니다. 이렇게 정해진 이름을 접근 가능한 이름이라고 합니다.

재료 가운데 몇은 다른 요소를 가리켜 그 글을 빌려 옵니다. 가리킬 때는 id 를 씁니다. id 는 문서 안에서 요소 하나에만 붙이는 고유한 이름입니다.

순서 재료 예
1 aria-labelledby · 다른 요소의 글을 가리킴 aria-labelledby="title1"
2 aria-label · 속성에 이름을 직접 적음 aria-label="닫기"
3 태그가 가진 재료 입력 칸에 이어 둔 <label> 의 글 · 그림을 대신하는 alt 글
4 안에 든 글자 · 버튼과 링크와 제목만 <button>저장</button>
5 title · 마우스를 올리면 뜨는 말풍선 글 title="새로 고침"

1과 2는 ARIA 속성입니다. aria-labelledby 는 다른 요소의 id 를 적어 그 요소의 글을 이름으로 빌려 옵니다. 화면에 이미 보이는 글을 이름으로 다시 쓰는 방법입니다.

aria-label 은 화면에 안 보이는 이름을 속성에 직접 적습니다. 글자 없이 아이콘만 든 버튼처럼 빌려 올 글이 없을 때 씁니다.

아래 버튼 셋과 그림 하나의 오른쪽 주석은 각각의 이름입니다.

HTML
<button>저장</button>          <!-- 저장 -->
<button aria-label="닫기">     <!-- 닫기 -->
  ×
</button>
<img src="a.png" alt="로고">   <!-- 로고 -->
<button><svg>…</svg></button>  <!-- 없음 -->

둘째 버튼은 화면의 「×」보다 앞 순서인 aria-label 의 「닫기」를 이름으로 씁니다. 마지막 줄처럼 그림만 든 버튼은 이름 재료가 하나도 없습니다. 스크린 리더는 「버튼」이라고만 읽어 무엇을 하는 버튼인지 전하지 못합니다.

입력 칸은 안에 글자를 품지 않습니다. 그래서 바깥의 <label> 에서 이름을 가져옵니다. <label> 의 for 속성에 입력 칸의 id 를 적어 둘을 잇습니다.

HTML
<label for="q">검색어</label>
<input id="q">              <!-- 검색어 -->

「검색어」라는 글이 입력 칸 옆에 보여도 <label> 로 잇지 않았다면 입력 칸에는 이름이 없습니다. 눈으로는 짝이 맞아 보여도 접근성 트리에서는 둘이 남남입니다.

트리에서 빠지는 노드

접근성 트리는 DOM 보다 노드가 적습니다. 빠지는 노드는 두 부류입니다. 숨긴 노드와 뜻 없는 노드입니다.

화면에서 요소를 숨기는 방법은 여럿입니다. 방법마다 접근성 트리에서 빠지는지가 다릅니다.

숨기는 방법 화면 접근성 트리
display: none 안 보이고 공간도 없음 빠짐
visibility: hidden 안 보이고 빈칸만 남음 빠짐
aria-hidden="true" 보임 빠짐
글자를 화면 밖으로 밀어냄 안 보임 남음

셋째 줄의 aria-hidden 은 눈에는 보이되 스크린 리더에는 전하지 않을 때 씁니다. 글자 옆에 붙은 장식 아이콘이 그렇습니다. 이름이 이미 있는 부품에서 아이콘까지 읽히면 같은 말을 두 번 듣습니다.

넷째 줄은 반대입니다. 눈에는 안 띄게 하되 스크린 리더에는 전할 안내 글에 씁니다. 요소를 화면 바깥 좌표로 옮기거나 한 점 크기로 줄여 숨기는 방식입니다.

뜻 없는 노드는 역할도 이름도 없는 상자입니다. 브라우저는 이런 <div> 를 대개 건너뜁니다. 그 안의 자식은 남아 한 층 위에 붙습니다. 배치를 잡으려고 상자를 열 겹 감싸도 스크린 리더가 다니는 트리는 그만큼 깊어지지 않습니다.

아래 로그인 상자로 확인해 봅니다. class 는 CSS 규칙을 걸 때 쓰는 이름표입니다. 오른쪽 주석은 각 줄이 접근성 트리에서 어떻게 되는지입니다.

HTML
<div class="card">          <!-- 건너뜀 -->
  <h2>로그인</h2>           <!-- 제목 -->
  <div class="row">         <!-- 건너뜀 -->
    <label for="id">아이디</label>
    <input id="id">         <!-- 입력 칸 -->
  </div>
  <span aria-hidden="true">★</span>
  <button>들어가기</button> <!-- 버튼 -->
</div>

주석이 없는 두 줄도 있습니다. <label> 줄은 입력 칸의 이름 「아이디」를 대는 재료가 됩니다. aria-hidden 이 붙은 별 모양 줄은 트리에서 빠집니다.

아래 그림은 이 상자의 접근성 트리에서 역할이 있는 노드만 그린 것입니다.

flowchart TD
    R["문서"] --> H["제목 · 로그인"]
    R --> I["입력 칸 · 아이디"]
    R --> B["버튼 · 들어가기"]

뿌리는 페이지 전체를 뜻하는 문서 노드입니다. DOM 에서 상자 두 겹 아래에 있던 입력 칸이 여기서는 제목 · 버튼과 나란히 섭니다.

상태가 바뀔 때

페이지가 뜬 뒤에도 자바스크립트 코드가 DOM 을 바꿉니다. 브라우저는 DOM 이 바뀌면 접근성 트리도 고칩니다. 무엇이 바뀌었는지는 이벤트라는 알림 신호로 보조 기술에 전합니다. 체크박스를 누르면 「체크됨」이 바로 들리는 까닭입니다.

<div> 로 짠 부품에는 이 상태가 저절로 붙지 않습니다. 메뉴 버튼을 누를 때 CSS 클래스만 바꾸면 화면에서는 메뉴가 펼쳐집니다. 숨겨 두었던 메뉴가 보이게 되니 메뉴 항목은 접근성 트리에 오릅니다. 하지만 버튼에는 「펼쳐짐」 상태가 없습니다.

스크린 리더 사용자는 버튼을 눌러도 무엇이 열렸는지 듣지 못합니다. 이 빈틈은 ARIA 의 상태 속성이 메웁니다. 메뉴를 열 때 aria-expanded 를 true 로 함께 바꾸면 접근성 트리의 버튼에 「펼쳐짐」이 오릅니다.

개발자가 접근성 트리를 바꾸는 방법

개발자는 접근성 트리를 직접 짜지 않습니다. 브라우저가 HTML 태그와 ARIA 속성을 보고 세웁니다. 그래서 트리를 바꾸려면 이 둘을 고칩니다.

먼저 고르는 쪽은 태그입니다. 뜻에 맞는 태그를 골라 쓰는 방식을 시맨틱 HTML이라고 합니다. <button> 을 쓰면 역할과 키보드 동작이 함께 따라옵니다. <div> 에 role="button" 을 붙이면 역할만 생깁니다.

ARIA 는 맞는 태그가 없는 부품에 씁니다. 탭이나 접었다 펴는 트리 메뉴가 그런 부품입니다. ARIA 를 틀리게 붙이면 접근성 트리가 화면과 다른 말을 합니다. 버튼이 아닌 것에 버튼 역할을 붙이면 사용자는 누를 수 없는 것을 누르려 하게 됩니다.

테스트 도구가 접근성 트리를 쓰는 까닭

브라우저 테스트 도구는 페이지에서 요소를 찾아 누르고 값을 넣습니다. 요소를 찾는 방법에 따라 화면을 고칠 때 테스트가 깨지는 정도가 달라집니다.

CSS 클래스나 태그가 감싸인 순서를 적은 경로로 찾으면 화면 구조를 고칠 때마다 테스트가 깨집니다. 접근성 트리의 역할과 이름으로 찾으면 <div> 를 몇 겹 더 감싸도 「저장이라는 버튼」은 남습니다. 앞에서 본 대로 꾸밈용 상자는 트리에서 건너뛰기 때문입니다.

Playwright 는 요소를 찾을 때 이 방식을 먼저 권합니다. 아래 한 줄은 역할이 버튼이고 이름이 저장인 요소를 찾아 누릅니다.

Python
page.get_by_role("button", name="저장").click()

역할로 찾는 테스트는 접근성 확인을 겸합니다. 스크린 리더가 못 알아보는 버튼은 이 테스트도 못 찾습니다. 아이콘만 든 버튼에 이름이 빠졌다면 테스트가 먼저 실패해 알려 줍니다.

브라우저 개발자 도구에는 접근성 트리를 펼쳐 보는 패널이 있습니다. 노드마다 역할 · 이름 · 상태를 보여 줍니다. 테스트가 요소를 못 찾을 때 이 패널을 열면 그 노드의 이름이 무엇으로 정해졌는지 보입니다.

DOM · 렌더 트리와 견주기

같은 웹 문서에서 트리 셋이 나옵니다. 누가 읽는지와 무엇이 빠지는지로 가르면 아래 표가 됩니다.

DOM 렌더 트리 접근성 트리
읽는 쪽 자바스크립트 코드 화면을 그리는 단계 보조 기술 · 테스트 도구
노드가 담는 것 태그와 속성 요소마다 정해진 생김새 값 역할 · 이름 · 상태
display: none 남음 빠짐 빠짐
visibility: hidden 남음 남음 빠짐
꾸밈용 <div> 남음 남음 대개 건너뜀

visibility: hidden 줄이 두 트리를 가릅니다. 렌더 트리는 빈칸의 크기를 재야 하므로 그 노드를 남깁니다. 접근성 트리는 전할 내용이 없으므로 그 노드를 뺍니다.

관련 항목

접근성 트리를 세우는 재료

HTML · DOM · CSS · ARIA · 시맨틱 HTML · 암묵적 역할

접근성 트리 노드의 이름과 상태를 바꾸는 ARIA 속성

aria-label · aria-labelledby · aria-describedby · aria-hidden · aria-expanded · aria-live

접근성 트리 노드가 들고 있는 정보

ARIA 역할 · 접근 가능한 이름 · 접근 가능한 설명 · ARIA 상태

접근성 트리를 읽는 보조 기술

보조 기술 · 스크린 리더 · 점자 디스플레이 · 화면 확대기 · 음성 제어

접근성 트리를 보조 기술에 넘기는 운영체제 API

접근성 API · UI Automation · IAccessible2 · NSAccessibility · AT-SPI

접근성 트리와 견주는 브라우저 안의 트리

렌더 트리 · 박스 트리 · 섀도 DOM · 가상 DOM

접근성 트리로 요소를 찾거나 살피는 도구

Playwright · 로케이터 · Testing Library · 개발자 도구 · 자동 검사

접근성 트리가 속하는 상위 분류

접근성 · 웹 접근성 · WCAG · 브라우저 · 렌더링 엔진

다른 이름: accessibility tree