Featured image of post 웹 접근성

웹 접근성

웹 접근성이란

장애인이나 고령자들이 웹 사이트에서 제공하는 정보를 비장애인과 동등하게 접근하고 이용할 수 있도록 보장하는 것
국제 웹 표준기구(W3C)에서 제정한 표준안, 총 33개의 항목으로 평가한다.


웹 접근성 준수 고려사항

시각

실명, 색각 이상, 다양한 형태의 저시력을 포함한 장애

이동성

특정 조건으로 인한 근육 속도 저하, 손으 쓰기 어렵거나 힘든 상태

청각

영상, 음성 콘텐츠에 자막, 원고, 수화등의 대체수단 부재로 인한 인식불가능 상태

인지

문제해결과 논리 능력, 집중력, 기억력에 문제(난독증, 난산증 등)

사용되는 보조기술

  • 스크린 리더
  • 화면 확대 도구
  • 음성 인식
  • 키보드 오버레이


기본적인 웹 접근성 준수 가이드

적절한 마크업 시맨틱 태그 사용

  • <header>, <nav>., <main>, <section>, <article>, <footer>와 같이 스크린 리더가 각 섹션의 역할을 인식할 수 있도록 적절한 시맨틱 태그를 사용해야 한다.
  • 제목태그 사용 <h1> ~ <h6>

---

중요하게 알아야 할 웹 접근성 항목

텍스트 외 콘텐츠에 대체 텍스트를 제공하여 이해할 수 있도록 해야 한다

  • 테이블, 이미지 등에 대한 대체 텍스트를 꼭 명시하여야 함. (주석, font-size: 0; 등은 스크린 리더도 인식하지 못 하기 떄문에 의미가 없음. CSS를 이용하여 지우기 clip:rect() 등)

  • img태그 사용 시 반드시 alt 속성을 사용하여 어떤 이미지인지 인식할 수 있도록 대체 텍스트를 제공해야 함.

  • 의미가 없는 장식용 이미지에는 alt="" 속성을 사용하여 빈 문자열로 설정해야 함.

  • 간혹 많은 양의 대체 텍스트를 제공해야 하는 경우에는 IR(Image Replacement)기법을 이용하여 마크업에서 대체 텍스트 작성 후 CSS를 통해 숨김.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.blind {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

단, visibility: hidden;, opacity: 0;, font-size: 0; 등은 스크린 리더가 인식하지 못하기 때문에 사용하면 안됨. ,<img>태그는 이미 이미지라는 의미를 갖고 있기 떄문에 대체 텍스트 내 ‘이미지’ 단어 사용은 중복임.

  • QR코드의 경우 alt 속성으로 해당 링크 주소까지 필수로 제공해야 함.
  • longdesc 속성은 HTML5부터 사용하지 않음.

모든 기능은 키보드 조작만으로 사용할 수 있어야 한다

  • 테이블에 대한 정보(이름, 유형, 분류, 내용, 날짜 등)를 <caption> 태그에 명시 후 경우에 따라 안 보이게 처리.
  • 단순 데이터용 테이블에는 <th> 요소를 제목 셀로 사용할 때 scope="row"(행), scope="col"(열) 속성을 사용하여 행과 열의 관계를 명시해야 함.

콘텐츠는 색에 관계 없이 인식될 수 있어야 함

  • 탭 메뉴 등에서 글꼴 색상만 변화하는 경우 일부 색약 사용자가 구분하기 어럽기 때문에 추가적인 표시 방법을 제공해야 함.
  • 텍스트와 배경간 명도 대비는 4.5:1 이상이어야 함. 명도대비 계산 링크 바로가기(클릭) 명도대비 부적합, +10쿠폰 박스의 텍스트, 배경간 명도대비가 2.917:1로 부적합 수준이다. 배경색은 연한 보라색, 텍스트는 흰색이다.

명도대비 적합, +10%쿠폰박스의 텍스트, 배경간 명도대비가 11.607:1로 적합 수준이다. 배경은 진한 보라색, 텍스트는 흰색이다.






실제 사이트를 검사하며 새롭게 습득

버튼에 대한 용도 설명 aria-label

간혹 아이콘만으로 이루어진 버튼에 추가 설명이 제공되지 않는 경우가 있음. 찜하기, 재입고 알림 아이콘 버튼의 설명이 제공되지 않아 스크린 리더에서 공란으로 표시됨 이 경우 스크린 리더에서 어떤 버튼인지 사용자에게 제공할 수 없음.

다음과 같이 aria-label 속성을 사용하여 버튼의 용도를 설명해야 함.

1
2
<button class="btn" type="button" aria-label="찜하기">
<button class="btn" type="button" aria-label="재입고 알림">

마크업에 aria-label 속성을 추가하여 이제는 스크린 리더에서 버튼의 용도를 제공해줌



<div>, <ul>, <li> 태그 등이 특정 기능을 한다면 role 속성을 사용하여 명시

특정 기능을 하는 요소라면 role="tab" 등과 같이 기능을 명시해야 함.



ARIA

WAI-ARIA란

W3C의 WAI(Web Accessibility Initiative)에서 만든 명세로, Accessible Rich Internet Applications의 약자. HTML만으로는 의미를 전달하기 어려운 동적 UI(탭, 모달, 아코디언, 드롭다운 등)의 정보를 스크린 리더 등 보조기술에 전달하기 위해 사용함.

  • 화면에 보이는 모양이나 동작은 전혀 바꾸지 않음. 오직 보조기술에 전달되는 정보만 바꿈.
  • 따라서 role="button"을 붙여도 키보드 조작, 포커스 이동 등은 직접 구현해야 함.

ARIA 사용 원칙

네이티브 HTML 요소로 해결할 수 있다면 ARIA를 사용하지 않는다. 잘못 사용한 ARIA는 사용하지 않은 것보다 더 큰 혼란을 줄 수 있음.

1
2
3
4
5
<!-- 비권장 -->
<div role="button" tabindex="0" onclick="...">검색</div>

<!-- 권장 : button 태그는 키보드 조작, 포커스, 역할이 기본 제공됨 -->
<button type="button">검색</button>

  • 네이티브 요소의 의미를 불필요하게 덮어쓰지 않음. (<h2 role="tab"> 등)
  • 포커스 가능한 요소에 aria-hidden="true"를 사용하지 않음. 키보드로는 이동되는데 스크린 리더는 아무것도 읽지 않는 상태가 됨.

ARIA의 세 가지 구성요소

1. Role (역할)

요소가 무엇인지 명시.

  • role="tab", role="tablist", role="tabpanel" : 탭 UI
  • role="dialog" : 모달, 팝업
  • role="alert" : 즉시 알려야 하는 중요한 메시지
  • role="status" : 처리 결과 등 덜 급한 안내 메시지

2. Property (속성)

비교적 변하지 않는 특성.

  • aria-label : 요소의 이름을 직접 지정 (아이콘 버튼 등)
  • aria-labelledby : 화면에 있는 다른 요소의 텍스트를 이름으로 사용 (id 참조)
  • aria-describedby : 추가 설명 연결 (입력 필드의 도움말, 오류 메시지 등)
  • aria-required : 필수 입력 항목 표시
  • aria-controls : 이 요소가 제어하는 대상 요소 (id 참조)
1
2
3
<label for="phone">휴대폰 번호</label>
<input type="tel" id="phone" aria-describedby="phone-help" aria-required="true">
<p id="phone-help">'-' 없이 숫자만 입력해 주세요.</p>

3. State (상태)

사용자 조작에 따라 바뀌는 값. JS로 상태가 바뀔 때 속성값도 함께 변경해야 함.

  • aria-expanded : 펼침/접힘 (아코디언, 드롭다운)
  • aria-selected : 선택 여부 (탭)
  • aria-checked : 체크 여부 (커스텀 체크박스)
  • aria-hidden : 보조기술에서 숨김 (장식용 아이콘 등)
  • aria-current : 현재 위치 (aria-current="page" 현재 페이지 메뉴)
1
2
<button type="button" aria-expanded="false" aria-controls="faq1">자주 묻는 질문</button>
<div id="faq1" hidden>답변 내용</div>
1
2
3
4
5
btn.addEventListener('click', () => {
  const expanded = btn.getAttribute('aria-expanded') === 'true';
  btn.setAttribute('aria-expanded', String(!expanded));
  panel.hidden = expanded;
});



탭 UI 예시

앞서 언급한 탭 메뉴에 role과 상태 속성을 함께 적용한 예시.

1
2
3
4
5
6
7
<div role="tablist" aria-label="상품 안내">
  <button type="button" role="tab" id="tab1" aria-selected="true" aria-controls="panel1">보장내용</button>
  <button type="button" role="tab" id="tab2" aria-selected="false" aria-controls="panel2" tabindex="-1">가입조건</button>
</div>

<div role="tabpanel" id="panel1" aria-labelledby="tab1">보장내용 영역</div>
<div role="tabpanel" id="panel2" aria-labelledby="tab2" hidden>가입조건 영역</div>
  • 스크린 리더가 “상품 안내, 탭 목록, 보장내용 탭, 선택됨, 1/2"처럼 읽어줌.
  • 탭 전환 시 aria-selected, tabindex, hidden을 JS로 함께 변경해야 함.
  • W3C 권장 패턴에서는 탭 간 이동을 좌우 방향키로 처리함. (Tab 키는 탭 목록 → 패널로 이동)



라이브 영역 aria-live

화면 일부 내용이 동적으로 바뀔 때 포커스를 옮기지 않아도 스크린 리더가 읽어주도록 함.

  • aria-live="polite" : 현재 읽던 내용을 끝낸 뒤 읽음 (대부분 이쪽 사용)
  • aria-live="assertive" : 읽던 내용을 중단하고 즉시 읽음 (긴급한 오류 등에만)
  • role="status"는 aria-live="polite", role="alert"는 aria-live="assertive"가 기본 포함되어 있음.
1
2
<!-- 페이지 로드 시점부터 빈 상태로 DOM에 있어야 함 -->
<div id="result-message" role="status"></div>
1
document.getElementById('result-message').textContent = '상담 신청이 완료되었습니다.';
  • 라이브 영역을 나중에 통째로 추가하면 스크린 리더가 변경을 감지하지 못하는 경우가 있음. 빈 영역을 미리 두고 텍스트만 채울 것.
  • alert()도 스크린 리더가 읽어주지만, 확인 버튼을 눌러야 진행되고 메시지가 바로 사라지며 사용자가 “대화상자 표시 안 함"을 체크하면 아예 안 뜨기 때문에 권장되지 않음.



장식용 아이콘 숨기기

텍스트와 함께 있는 아이콘은 중복으로 읽히지 않도록 숨김 처리.

1
2
3
4
<button type="button">
  <svg aria-hidden="true" focusable="false">...</svg>
  장바구니
</button>



참고링크 https://velog.io/@leejpsd/CSS-IR-%EA%B8%B0%EB%B2%95 https://tech.kakaopay.com/post/accessibility-stories-for-everyone/#%ED%91%9C

Hugo로 만듦
Jimmy의 Stack 테마 사용 중