웹 접근성 개념 · 시맨틱과 ARIA · 폼 접근성 · 이미지·미디어 접근성
웹 접근성의 정의와 한국 법적 의무, 장애 유형별 보조기술, WCAG 2.1의 4원칙(인식/운용/이해/견고), 그리고 Lighthouse·axe DevTools·키보드 테스트로 측정하는 방법을 익힙니다. 시맨틱 태그가 만드는 랜드마크, ARIA의 황금 규칙("No ARIA > Bad ARIA"), role과 aria-label/labelledby/describedby/expanded/hidden, 그리고 tabindex와 skip navigation을 익힙니다. label과 input 연결 3가지 방식, role="alert"와 aria-describedby로 에러 전달, aria-required/aria-invalid, 색상 대비 4.5:1, 그리고 절대 제거하면 안 되는 포커스 스타일. alt 텍스트 작성 원칙(장식 vs 정보 vs 링크 vs 버튼), 비디오 자막(WebVTT), 색맹을 고려한 배색, 그리고 움직임 민감 사용자를 위한 prefers-reduced-motion까지.
이 강의에서 배우는 것
- 1웹 접근성의 정의와 법적 의무를 안다
- 2장애 유형별 주요 보조기술을 파악한다
- 3WCAG 2.1의 4원칙(POUR)과 적합성 수준(A/AA/AAA)을 안다
- 4Lighthouse / axe DevTools로 접근성을 측정한다
- 5키보드만으로 페이지를 사용할 수 있는지 확인한다
- 6시맨틱 태그가 만드는 랜드마크와 스크린리더 안내를 안다
- 7ARIA의 황금 규칙을 따른다
- 8role, aria-label/labelledby/describedby를 적재적소에 쓴다
- 9aria-expanded로 토글 상태를 전달한다
- 10tabindex와 skip link로 키보드 내비를 개선한다
- 11label-for-id, 감싸기, aria-label 3가지 연결 방식을 안다
- 12role="alert"와 aria-describedby로 에러를 전달한다
- 13aria-required / aria-invalid 를 적절히 토글한다
- 14WCAG AA 색상 대비(4.5:1)를 만족한다
- 15:focus-visible로 포커스 스타일을 안전하게 유지한다
- 16장식/정보/링크/버튼 이미지의 alt 작성 원칙을 안다
- 17<track>으로 비디오 자막(WebVTT)을 추가한다
- 18색맹을 고려해 색상 + 아이콘 + 텍스트로 정보를 전달한다
- 19@media (prefers-reduced-motion) 으로 움직임을 제어한다
웹 접근성 개념
웹 접근성의 정의와 한국 법적 의무, 장애 유형별 보조기술, WCAG 2.1의 4원칙(인식/운용/이해/견고), 그리고 Lighthouse·axe DevTools·키보드 테스트로 측정하는 방법을 익힙니다.
1. 웹 접근성이란
장애가 있는 사람·고령자 등 모든 사람이 웹 콘텐츠에 접근하고 사용할 수 있도록 보장하는 것.
“웹의 힘은 보편성에 있다. 장애에 관계없이 모든 사람이 접근할 수 있는 것이 웹의 핵심이다.” — 팀 버너스리
- 법적 의무: 공공기관·일정 규모 이상 민간 웹사이트 (장애인차별금지법 제21조)
- 한국형 KWCAG 2.1 준수 기준
- 더 많은 사용자: 국내 장애인 약 260만명
- SEO 향상 + UX 향상 (비장애인에게도 편의성 ↑)
2. 장애 유형별 보조기술
| 유형 | 보조기술 | 접근 방식 |
|---|---|---|
| 시각 | 스크린리더 (NVDA, VoiceOver, JAWS) | HTML 구조와 텍스트 대안 |
| 저시력 | 화면 확대, 고대비 모드 | 텍스트 크기·색상 대비 |
| 청각 | 자막, 수화 영상 | 멀티미디어 대안 콘텐츠 |
| 운동 | 키보드만 사용, 스위치 제어 | 마우스 없이 모든 기능 |
| 인지 | 다양한 보조 앱 | 단순·명확한 UI |
<!-- 스크린리더는 이렇게 읽음 -->
<a href="/">홈</a> <!-- "홈 링크" -->
<button disabled>제출</button> <!-- "제출 버튼 비활성화됨" -->
<img src="logo.png" alt="회사 로고"> <!-- "회사 로고 이미지" -->
<img src="deco.png" alt=""> <!-- 장식 이미지는 건너뜀 -->3. WCAG 2.1 — 4원칙 (POUR)
1) 인식 가능 (Perceivable)
<img src="chart.png" alt="2024년 1분기 매출: 1억 2천만원">
<video>
<track kind="captions" src="subtitles.vtt" srclang="ko">
</video>
<!-- 색상만으로 정보 전달 금지 -->2) 운용 가능 (Operable)
<button onclick="...">제출</button> <!-- ✓ 키보드 OK -->
<div onclick="...">제출</div> <!-- ✗ 키보드 불가 -->3) 이해 가능 (Understandable)
<html lang="ko">
<!-- 에러 메시지 구체적으로 -->
<!-- ✗ "입력 오류" -->
<!-- ✓ "이메일 형식이 올바르지 않습니다 (예: user@example.com)" -->4) 견고 (Robust)
올바른 HTML, 닫는 태그, 정확한 ARIA 사용.
4. 적합성 수준
| 수준 | 설명 |
|---|---|
| A | 최소 — 미충족 시 일부 사용자 완전 차단 |
| AA | 권장 — 대부분 법적 기준이 AA |
| AAA | 최고 — 모든 상황에서 완전 충족 |
5. 측정 도구
Lighthouse (Chrome 내장)
- F12 → Lighthouse 탭
- Accessibility 체크 → Analyze page load
- 점수 및 개선 항목 확인
axe DevTools
- Chrome 웹스토어에서 axe DevTools 설치
- F12 → axe DevTools 탭 → Scan ALL of my page
- 위반 항목과 수정 방법 확인
키보드 테스트
Tab 다음 포커스
Shift+Tab 이전 포커스
Enter 링크 활성화, 버튼 클릭
Space 버튼/체크박스 토글
화살표 라디오, 선택 메뉴
Escape 모달/팝업 닫기시맨틱과 ARIA
시맨틱 태그가 만드는 랜드마크, ARIA의 황금 규칙("No ARIA > Bad ARIA"), role과 aria-label/labelledby/describedby/expanded/hidden, 그리고 tabindex와 skip navigation을 익힙니다.
1. 시맨틱 → 랜드마크
<!-- ✗ -->
<div class="header"><div class="nav">...</div></div>
<!-- ✓ -->
<header><nav>...</nav></header>| 태그 | ARIA 역할 | 스크린리더 안내 |
|---|---|---|
| <header> | banner | 배너 영역 |
| <nav> | navigation | 탐색 영역 |
| <main> | main | 주요 콘텐츠 |
| <aside> | complementary | 보완 영역 |
| <footer> | contentinfo | 콘텐츠 정보 |
| <section> | region | 섹션 |
| <article> | article | 독립적 콘텐츠 |
2. ARIA 황금 규칙
“No ARIA is better than Bad ARIA” — 잘못된 ARIA는 없는 것보다 나쁘다. 시맨틱 HTML로 해결할 수 있다면 ARIA를 쓰지 말 것.
3. role
<!-- div를 버튼처럼 (피하는 게 좋음) -->
<div role="button" tabindex="0" onclick="..." onkeydown="...">
클릭
</div>
<!-- 탭 UI -->
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel1">탭1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">탭2</button>
</div>
<div role="tabpanel" id="panel1">탭1 내용</div>
<div role="tabpanel" id="panel2" hidden>탭2 내용</div>4. aria-label / labelledby / describedby
<!-- 텍스트 없는 버튼 -->
<button aria-label="메뉴 닫기">
<svg aria-hidden="true">...</svg>
</button>
<!-- 다른 요소를 레이블로 -->
<h2 id="section-title">최신 상품</h2>
<ul aria-labelledby="section-title">...</ul>
<!-- 모달 제목 -->
<div role="dialog" aria-labelledby="modal-title" aria-modal="true">
<h2 id="modal-title">주문 확인</h2>
</div>
<!-- 추가 설명 -->
<input type="password" id="pw" aria-describedby="pw-hint">
<p id="pw-hint">8자 이상, 대소문자+숫자+특수문자</p>5. aria-expanded / aria-hidden
<button aria-expanded="false" aria-controls="menu" onclick="toggleMenu()">
메뉴 열기
</button>
<ul id="menu" hidden>...</ul>
<script>
function toggleMenu() {
const btn = document.querySelector('button');
const menu = document.getElementById('menu');
const isOpen = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', !isOpen);
menu.hidden = isOpen;
}
</script>
<!-- 장식 아이콘은 스크린리더에서 제외 -->
<button>
<svg aria-hidden="true" focusable="false">...</svg>
저장
</button>6. 키보드 내비게이션
<!-- tabindex="0": Tab으로 접근 가능 (자연 순서) -->
<div role="button" tabindex="0">클릭 가능</div>
<!-- tabindex="-1": Tab 불가, JS로 focus() 가능 -->
<div id="modal" tabindex="-1">...</div>
<!-- tabindex="1" 이상: 사용 금지! 자연 순서를 깨뜨림 -->7. Skip Navigation Link
긴 네비게이션을 키보드로 매번 통과하지 않게 하는 "본문 바로가기" 링크.
<a href="#main-content" class="skip-link">본문 바로가기</a>
<nav>... (긴 내비게이션) ...</nav>
<main id="main-content">
<h1 tabindex="-1">페이지 제목</h1>
</main>.skip-link {
position: absolute;
top: -100%;
left: 0;
padding: 8px 16px;
background: #000;
color: #fff;
z-index: 9999;
text-decoration: none;
}
.skip-link:focus { top: 0; }폼 접근성
label과 input 연결 3가지 방식, role="alert"와 aria-describedby로 에러 전달, aria-required/aria-invalid, 색상 대비 4.5:1, 그리고 절대 제거하면 안 되는 포커스 스타일.
1. label과 input 연결
방법 1: for/id (권장)
<label for="username">사용자 이름</label>
<input type="text" id="username" name="username">방법 2: 감싸기
<label>
사용자 이름
<input type="text" name="username">
</label>방법 3: aria-label
<input type="search" aria-label="검색어 입력" placeholder="검색어">placeholder는 레이블 대체품이 아닙니다! 입력 시작하면 사라지고 대비도 낮음.
2. 에러 메시지 — role="alert"
<!-- 동적 추가 시 스크린리더가 즉시 읽음 -->
<div role="alert">
이메일 형식이 올바르지 않습니다. (예: user@example.com)
</div>
<label for="email">이메일</label>
<input
type="email"
id="email"
aria-describedby="email-error"
aria-invalid="true"
required
>
<p id="email-error" role="alert">
이메일 형식이 올바르지 않습니다.
</p>3. aria-required / aria-invalid
<input type="text" required aria-required="true">
<input aria-invalid="false"> <!-- 통과 전 -->
<input aria-invalid="true" aria-describedby="error-msg"> <!-- 에러 -->
<span id="error-msg">필수 입력 항목입니다.</span>
<!-- 여러 도움말 연결 (공백 구분) -->
<input type="password" id="pw" aria-describedby="pw-hint pw-error">
<p id="pw-hint">8자 이상, 대소문자+숫자+특수문자</p>
<p id="pw-error" role="alert" hidden>비밀번호가 조건을 충족하지 않습니다.</p>4. 색상만으로 정보 전달 금지
<!-- ✗ 색상만 -->
<input style="border-color: red">
<!-- ✓ 색상 + 아이콘 + 텍스트 -->
<div class="field-error">
<input aria-invalid="true" aria-describedby="email-error">
<span class="error-icon" aria-hidden="true">⚠</span>
<p id="email-error" role="alert">이메일 형식이 올바르지 않습니다.</p>
</div>5. 색상 대비 (Contrast Ratio)
- 일반 텍스트: 최소 4.5:1
- 큰 텍스트(18pt 이상): 최소 3:1
- UI 요소(테두리, 아이콘): 최소 3:1
/* ✗ 대비율 2.3:1 */
input { border: 1px solid #cccccc; }
label { color: #aaaaaa; }
/* ✓ 4.5:1 이상 */
input { border: 2px solid #767676; }
label { color: #1a1a1a; }확인: WebAIM Contrast Checker — https://webaim.org/resources/contrastchecker/
6. 포커스 스타일 — 절대 제거 금지!
/* ✗ 절대 금지 */
:focus { outline: none; }
/* ✓ 방법 1: 기본 outline 개선 */
:focus-visible {
outline: 3px solid #0066cc;
outline-offset: 2px;
border-radius: 2px;
}
/* ✓ 방법 2: 마우스는 숨기고 키보드만 표시 */
:focus { outline: none; }
:focus-visible { outline: 3px solid #0066cc; }이미지·미디어 접근성
alt 텍스트 작성 원칙(장식 vs 정보 vs 링크 vs 버튼), 비디오 자막(WebVTT), 색맹을 고려한 배색, 그리고 움직임 민감 사용자를 위한 prefers-reduced-motion까지.
1. alt 텍스트 작성 원칙
장식용: alt=""
<img src="decoration-wave.svg" alt="">
<img src="spacer.gif" alt="">정보 전달: 구체적인 설명
<!-- ✗ -->
<img src="chart.png" alt="차트">
<img src="photo.jpg" alt="사진">
<!-- ✓ -->
<img src="sales-chart.png" alt="2024년 1분기 매출: 1월 800만원, 2월 950만원, 3월 1,200만원. 전분기 대비 50% 성장.">
<img src="profile.jpg" alt="김철수, 개발팀 시니어 엔지니어">링크 이미지: 목적지 설명
<!-- ✗ 이미지 설명 -->
<a href="/home"><img src="logo.png" alt="파란색 로고"></a>
<!-- ✓ 링크의 목적지 -->
<a href="/home"><img src="logo.png" alt="메인 페이지로 이동"></a>버튼 이미지
<!-- ✗ -->
<button><img src="close.svg" alt="X"></button>
<!-- ✓ 동작 설명 -->
<button><img src="close.svg" alt="닫기"></button>
<!-- 또는 -->
<button aria-label="닫기"><img src="close.svg" alt=""></button>복잡한 이미지
<figure>
<img src="complex-chart.png" alt="월별 판매 추이 차트" aria-describedby="chart-desc">
<figcaption id="chart-desc">
2024년 1~6월 판매 데이터. 1월 800, 2월 950, 3월 1200,
4월 1100, 5월 1350, 6월 1500건. 전반적 상승 추세.
</figcaption>
</figure>2. 비디오 자막 (WebVTT)
<video controls>
<source src="lecture.mp4" type="video/mp4">
<track
kind="captions" <!-- captions: 청각 장애인용 (말+음향), subtitles: 외국어 -->
src="lecture-ko.vtt"
srclang="ko"
label="한국어 자막"
default
>
</video>.vtt 파일 형식
WEBVTT
00:00:00.000 --> 00:00:03.500
안녕하세요. 오늘은 웹 접근성에 대해 알아봅니다.
00:00:03.500 --> 00:00:07.000
첫 번째 주제는 이미지 alt 텍스트입니다.
00:00:07.000 --> 00:00:10.500
[화면 전환음]
alt 속성은 이미지를 텍스트로 설명합니다.3. 색맹을 고려한 배색
약 8%(남성 기준)가 색각 이상. 적록 색맹이 가장 흔함.
<!-- ✗ 색상만으로 -->
<p style="color: red">오류가 발생했습니다.</p>
<p style="color: green">성공했습니다.</p>
<!-- ✓ 색상 + 아이콘 + 텍스트 -->
<p class="error">
<span aria-hidden="true">✗</span>
오류가 발생했습니다.
</p>
<p class="success">
<span aria-hidden="true">✓</span>
성공했습니다.
</p>도구: Color Oracle (https://colororacle.org) — 화면 전체를 색맹 시뮬레이션으로 볼 수 있음.
4. prefers-reduced-motion
전정기관 장애·간질 등으로 움직임에 민감한 사용자를 위해.
.spinner { animation: spin 1s linear infinite; }
.slide { transition: transform 0.3s ease; }
@media (prefers-reduced-motion: reduce) {
.spinner { animation: none; }
.slide { transition: none; }
video[autoplay] { animation: none; }
}const prefersReducedMotion =
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReducedMotion) {
element.classList.add('animate');
}댓글 0
아직 댓글이 없습니다. 첫 댓글을 남겨 주세요.
댓글은 회원만 쓸 수 있습니다.
광고·욕설·비방·개인정보·불법 내용이 담긴 댓글은 숨기거나 지웁니다. 댓글은 닉네임과 함께 공개됩니다. 개인정보처리방침