카테고리 없음

시멘틱 마크업, 그리고 WAI-ARIA

caesar1030 2024. 5. 1. 13:24

 

들어가며

 

종종 프론트엔드 개발을 공부하면서 WAI ARIA라는 용어를 마주하게 됐는데, 접근성과 관련있다는 정도만 알고 그 이상은 알지 못했다. 최근에 입문하기에 적절해 보이는 을 발견해서, 가볍게 정리해보고자 한다. 참고로 해당 글에서 시멘틱 마크업에 대한 내용은 없지만, 좀 더 기술 흐름을 종합적으로 파악하기 위해 시멘틱 마크업 내용을 추가하였다.

 

 

시멘틱 마크업

 

HTML에서 h1 요소는 시맨틱 요소로, "페이지 최상위 제목"이라는 역할(또는 의미)을 텍스트에 부여한다. 기본적으로 대부분의 브라우저의 사용자 에이전트 스타일시트는 제목처럼 보이도록 큰 글꼴 크기로 h1의 스타일을 지정한다. (물론 원하는 대로 스타일을 지정할 수도 있다.) 

 

반면에 어느 요소도 최상위 제목처럼 보이게 만들 수도 있다.

 

<span style="font-size: 32px; margin: 21px 0;">Not a top-level heading!</span>

 

이렇게 하면 시각적으로는 최상위 제목처럼 렌더링 되지만 의미론적으로 어떠한 가치도 가지지 못한다. HTML은 어떻게 보이는지에 대한 스타일이 아닌 채워질 데이터에 대한 의미를 나타내도록 작성해야 한다. 어떻게 보여야하는가는 CSS의 책임이다. 

 

잘 보이면 그만 아닌가?라고 생각할 수도 있지만, 다음의 두가지 측면에서 시멘틱 마크업은 특히 중요하다.

 

- 검색 엔진은 컨텐츠를 페이지의 검색 순위에 영향을 미치는 중요한 키워드로 인식한다. (SEO)

- 스크린 리더는 시각 장애가 있는 사용자가 페이지를 탐색하는 데 도움이 되는 이정표로 사용할 수 있다.

 

자주 사용되는 시멘틱 태그 몇가지만 살펴보자.

 

- header: 소개 또는 탐색 그룹을 나타낸다. heading 엘리먼트 뿐만 아니라 로고, 검색 양식, 작성자 이름 및 기타 요소도 포함될 수 있다.

- main: document <body>의 주요 컨텐츠를 나타낸다. document의 중심 주제 또는 애플리케이션의 중심 기능과 직접적으로 관련되거나 이를 확장하는 컨텐츠로 구성된다.

- heading: 섹션의 제목을 나타낸다. 

- article: 독립적으로 배포하거나 재사용할 수 있는 구성을 나타낸다. 예를 들면 포럼 게시물, 잡지 또는 시눔ㄴ 기사, 블로그 글, 제품 카드, 사용자 댓글 등이 있다.

- section: 일반적인 독립형 섹션을 나타내며, 이를 나타내는 보다 구체적인 의미 요소가 없다. 섹션에는 극히 일부 예외를 제외하고 항상 heading이 있어야 한다.

- footer: 가장 가까운 상위 섹션의 바닥글을 나타낸다. 일반적으로 해당 섹션의 작성자에 대한 정보, 저작권 또는 관련 문서 링크가 포함된다. 

- aside: 주요 컨텐츠와 간접적으로만 관련된 컨텐츠가 있는 문서의 일부를 나타낸다. 사이드바로 표시되는 경우가 많다.

 

이 외에도 약 100여개의 태그가 있다.

 

클라이언트-서버 모델을 기반의 HTML

HTML은 웹 어플리케이션을 만들기 위해 고안된 것이 아니었다. 인터페이스 제어 기능이 제한되어 있고, 순차적인 클라이언트 서버 통신 모델을 기반으로 한다. 우리(프론트엔드 개발자)는 JavaScript를 추가하여 자체 사용자 정의 구성 요소(위젯)를 만들어 이런 한계를 극복해 왔다.

 

하지만, 이렇게 JS를 통한 커스텀한 위젯은 데스크톱 어플리케이션처럼 보이고 동작할 수는 있지만, 역할(위젯이 하는 일), 상태(checked와 같은 고유 설정) 및 기타 다른 속성은 스크린 리더와 같은 보조 기술에서 사용할 수 없다. 마치 heading 요소를 사용하지 않고 일반 텍스트를 heading 요소처럼 보이게끔 스타일링 한 것과 동일하다. 눈으로 보기에는 heading(제목)처럼 보이지만, 보조 기술을 이용하면 제목으로 표시되지 않는다.

 

AJAX로 백그라운드에서 조용히 컨텐츠를 업데이트 할 경우, 보조 기술은 이를 놓치는 경우가 있다. 보조 기술이 업데이트를 인식하더라도 사용자는 컨텐츠가 업데이트 되었다는 사실이나 업데이트된 컨텐츠를 찾는 방법을 알지 못할 수도 있다.

 

웹 어플리케이션과 데스크톱 어플리케이션의 차이

 

JS를 사용한 웹 어플리케이션으로 데스크톱 어플리케이션을 흉내내기 위해 노력해왔다. 하지만 근본적인 두 가지 큰 차이점이 있다.

 

- 데스크톱 어플리케이션은 서버에 의존하지 않는 behaviour layer를 가진다.

- 데스크톱 어플리케이션은 훨씬 더 풍부한 인터페이스 구성 요소가 있다.

 

웹 어플리케이션은 데스크톱 에플리케이션을 흉내내기 위해 JS를 사용하여 동작을 추가한다. 메뉴 항목과 상호작용할 때 메뉴 항목이 확장 및 축소되도록 할 수 있다. 또 현재 페이지의 정보를 업데이트 하기 위해 서버와 통신이 필요할 수도 있다. 서버와 상호 작용하는 경우 웹 어플리케이션은 AJAX와 같은 기술을 사용하여 백그라운드에서 서버와 조용히 통신한다.

 

HTML에는 인터페이스 구성 요소가 거의 없기 때문에 3단계 체크박스나 슬라이더 컨트롤과 같은 복잡한 위젯을 만들어야 할 때가 있다. 이러한 위젯은 그래픽으로 그린 후에, 스크립트를 추가하여 native 컴포넌트처럼 작동하도록 만들어진다.

 

리치 컴포넌트를 에뮬레이션하고 백그라운드에서 서버 요청을 수행하면 사용자에게 시각적으로 더 풍부한 경험을 제공할 수 있다. 하지만 스크린 리더와 같은 보조 기술 사용자에게 좋지 못한 접근성 문제가 발생한다.

 

- 위젯은 키보드로 액세스할 수 있는 경우가 거의 없다.

- 위젯의 역할과 위젯이 수행하는 작업(what it does)은 보조 기술에서 사용할 수 없다.

- 위젯의 상태 및 속성은 보조 기술에서 사용할 수 없다.

- 업데이트와 업데이트를 알아채는 것은 보조 기술에 보고되지 않는다.

 

WAI-ARIA 

다행히도 위에서 설명한 모든 문제는 Web Accessibility Initiative's Accessible Rich Internet Applications(WAI-ARIA) 사양으로 해결할 수 있다. ARIA는 개발자에게 할 수 없는 것을 말해주는 것이 아니라 개발자가 리치 웹 어플리케이션을 만들 수 있도록 지원하는 긍적적인 기술이다. ARIA는 구현하기도 간단하다.

 

키보드 탐색

 

텍스트가 아닌 객체에 대체 텍스트를 제공하는 것과 함께, 키보드만으로 인터페이스 요소와 상호 작용할 수 있도록 하는 것은 가장 기본적인 접근성 조항 중 하나이다. 접근성을 이해하는 개발자는 type 어트리뷰트가 image인 input 엘리먼트와 같이 focus를 받을 수 있는 위젯을 작성할 수 있다. 하지만 대부분의 위젯은 키보드 접근이 가능한 컴포넌트를 사용하여 작성되지 않고, img 요소를 사용하거나, 키보드 포커스를 받을 수 없는 div와 같은 엘리먼트 안에 존재하는 복합 요소로 구성되어 있다.

 

HTML 4에는 a, area, button, input, object, select, textarea 요소에 대한 tabindex 속성이 도입되었다. 탐색은 가장 낮은 숫자의 엘리먼트에서 시작하여 가장 높은 숫자의 엘리먼트로 진행된다. 값이 0인 엘리먼트는 마크업에 표시된 순서대로 방문한다. 

 

ARIA는 tabindex 속성을 확장하여 표시되는 모든 요소에 사용할 수 있도록 한다. 또한 ARIA에서는 키보드 tab order로 표시되어서는 안 되지만 프로그래밍 방식으로 포커스가 가능한 요소에 음수 값을 지정할 수 있다. 음수의 실제 값은 중요하지 않으므로, tab order에 포함도지 않아야 하지만 포커스를 받을 수 있어야 하는 엘리먼트에는 -1이 사용된다. 예를 들어 메뉴 자체는 tab order에 포함되지만 메뉴의 아이템들은 tab order에 포함되지 않는 메뉴 위젯을 만들 수 있다. 대신 메뉴의 아이템들은 커서 키를 이용하여 탐색이 가능하게 프로그래밍 한다. 이렇게 하면 사용자가 메뉴의 모든 항목을 탭할 필요가 없어 문서를 더 잘 탐색할 수 있게 된다. 

 

아래 예제는 tabindex attribute를 0으로 설정하여 키보드 사용자가 요소를 탐색 할 수 있도록 div 요소를 tab order에 배치한다.

<div tabindex="0">
...
</div>

 

아래 예제는 음수 tabindex를 사용하여 엘리먼트가 tab order에는 없지만, 프로그래매틱 하게 focus를 받을 수 있도록 한다.

 

<div id="progaccess" tabindex="-1">
...
</div>



var objDiv = document.getElementById('progaccess');

// Focus on the element
objDiv.focus();

 

 

ARIA role

 

ARIA는 슬라이더와 같은 위젯을 정의하고 탐색 섹션과 같은 페이지 구조를 정의하는데 도움이 되는 role attribute를 도입했다. 웹 어플리케이션의 주요 문제 중 하나는 모든 요소를 사용하여 위젯을 만들 수 있다는 것이다. HTML 요소는 이미 사전 정의된 role이 있다. 예를 들어 heading의 role은 보조 기술을 통해 잘 이해되고 있다. 기존 요소로 위젯을 만들 때 요소의 role은 위젯에서 시각적으로 무엇을 나타내는지가 아니라 보조 기술에 드러나는 것이다. 예를 들어 슬라이더 컨트롤의 thumb가 대체 텍스트가 있는 이미지 요소를 사용하여 만들어진 경우 화면 리더는 "슬라이더, 값 16%"와 같은 더 의미 있는 표현 대신, "그래픽, 엄지 손가락"으로 컨트롤을 알릴 가능성이 높다.

 

role attribute가 부여하는 role은 기본 요소의 role 보다 우선한다. 아래 예제에서 input 엘리먼트의 role attribute가 슬라이더인 경우, 보조 기술에 노출되는 role은 input이 아닌 슬라이더다.

 

<input type="image"
	   src="thumb.gif"
	   alt="Effectiveness"
	   role="slider"
	   aria-valuemin="0"
	   aria-valuemax="100"
	   aria-valuenow="42"
	   aria-valuetext="42 percent"
	   aria-labelledby="leffective">

 

이 요소가 포커스를 받으면 스크린 리더 사용자는 이 위젯이 수행하는 role을 이해한다. 

 

 

Document Landmark Roles

 

위젯을 정의하는 데 도움이 되는 role 뿐만 아니라 문서의 구조를 정의하는 데 도움이 되는 role도 있다. Document landmarks는 일반 role의 하위 집합으로, 스크린 리더 사용자가 센션의 역할을 이해하고 문서 내에서 방향을 잡는 데 도움을 준다. 

 

ARIA는 다음과 같은 Document landmark roles를 정의한다.

 

- article: 전체 블로그 게시물, 블로그의 댓글, 포럼의 게시물 등 그 자체로 의미가 있는 컨텐츠

- banner: 페이지 제목 및 로고와 같은 사이트 지향 컨텐츠

- complemantary: 주 컨텐츠를 보조하는 콘텐츠이지만 주 컨텐츠와 분리했을 때 그 자체로 의미 있는 컨텐츠. 예를 들어 포털에 나열된 날씨를 들 수 있다.

- contentinfo: 각주, 저작권, 개인정보처리방침 링크, 환경설정 링크 등과 같은 하위 컨텐츠이다.

- main: 문서의 중심 컨텐츠와 직접 관련이 있거나 이를 확장하는 컨텐츠

- navigation: 이 문서 또는 관련 문서를 탐색할 수 있는 링크가 포함된 컨텐츠

- search: 이 섹션에는 사이트를 검색할 수 있는 검색 양식이 포함되어 있다.

 

 

 

ARIA States and Properties

 

ARIA의 states 및 properties를 사옹하면 사용자가 위젯과 상호 작용하는 방법을 이해하는 데 도움이 되도록 보조 기술에 위젯에 대한 추가 정보를 제공할 수 있다. state는 개체에 대한 고유한 정보 구성을 식별한다. 예를 들어 aria-checked 프로퍼티는 true, false, mixed 세 가지 상태 값을 가진다.

 

위의 슬라이더 예시에서는 보조 기술에 대한 위젯을 설명하는 데 도움이 되는 다양한 aria-properties를 포함했다.

 

- aria-valuemin: 범위가 가질 수 있는 가장 낮은 값을 저장.

- aria-valuemax: 범위가 가질 수 있는 가장 높은 값을 저장.

- aria-valuenow: 현재 값을 저장

- aria-valuetext: 사용자가 문맥을 이해하는 데 도움이 되는 텍스트를 저장. (ex. "30 dollars")

- aria-labelledby 이 위젯에 적합한 프롬프트가 포함된 텍스트 레이블의 ID 속성을 저장.

 

일부 속성은 스크립트를 통해 업데이트 될 수 있다. 예를 들어 arai-valuenow나 aria-valuetext 프로퍼티는 thumb가 움직일 때 업데이트 된다.

 

Live Regions

 

live regions를 사용하면 사용자가 focus를 하지 않았을 때에도 문서의 요소에 변경 사항이 있을 경우 이를 알릴 수 있다. 즉 사용자는 컨텐츠 내에서 자신의 위치를 잃지 않고 업데이트에 대한 정보를 받을 수 있다. 예를 들어 채팅 어플리케이션에서 사용자가 채팅 중인 상대방의 응답을 알릴 때 텍스트 입력 필드에서 focus를 이동하지 않고도 이를 알릴 수 있다.

 

업데이트된 컨텐츠의 검색 가능성은 스크린 리더 사용자에게 가장 큰 장애물 중 하나이다. ARIA는 해당 영역의 verbosity level 수준을 나타내는 값이 있는 aria-live 속성을 제공한다.

 

- off: 기본값으로, 해당 지역이 라이브 상태가 아님을 나타낸다.

- polite: 사용자가 그들의 현재 활동을 완료할 때까지 응답할 필요가 없음을 나타낸다.

- assertive: 이 값은 우선순위가 높지만 반드시 사용자를 즉시 intterupt하지는 않는다.

 

 

aria-atomic 속성은 true 또는 false값을 가질 수 있는 라이브 영역의 선택적 속성이다. (기본값 true) 영역이 업데이트 되면 보조 기술이 변경된 영역의 전체 또는 일부를 사용자에게 표시해야 하는지 여부를 나타내는 데 aria-atomic 속성이 사용된다. true로 설정하면 전체 영역을 보조 기술은 영역 전체를 표현하고, 그렇지 않으면 일부분만 표현된다.

 

aria-busy 속성은 true 또는 false값을 가질 수 있는 라이브 영역의 선택적 속성이다. (기본값 true) 사용자에게 변경 사항을 알리기 전에 라이브 영역의 여러 부분을 로드해야 하는 경우, 마지막 부분이 로드될 때까지 aria-busy 속성을 true로 설정한 다음 업데이트가 완료되면 false로 설정할 수 있다. 이 속성을 사용하면 업데이트가 완료되기 전에 보조 기술이 변경 사항을 알리는 것을 방지할 수 있다.

 

aria-relevant 속성은 라이브 영역의 선택적 속성으로, 영역 내에서 어떤 변경 사항이 관련성이 있는 것으로 간주되는지를 나타낸다. 

 

- additions: 영역 내 DOM에 노드가 추가된다.

- removals: 영역 내 DOM에 노드를 제거한다.

- text: DOM에서 텍스트가 추가되거나 제거된다.

- all: 위의 모든 것(추가,제거, 텍스트)이 영역에 적용된다.

 

 

 

마치며

웹 접근성이 중요하다는 말은 많이 들었지만 뭐랄까 학습하기 어렵고 조금 난해한 (사실은 필요성을 못느꼈던) 그런 주제였던 듯 싶다. 프론트엔드 개발자로서, 웹 개발자로서 웹의 보편성이라는 가장 중요한 가치를 잊은 채 너무 스킬적인 측면에만 몰두해 학습해왔던 건 아닌가 싶다.