웹 접근성(a11y): 개발자를 위한 실전 가이드
모든 사용자가 웹 앱을 편리하게 이용할 수 있도록 하는 핵심 접근성 원칙, ARIA 속성, 키보드 탐색 패턴, 그리고 테스트 도구를 알아보세요.
전 세계 인구의 16%에 해당하는 13억 명 이상이 어떤 형태로든 장애를 가지고 살아가고 있습니다. 웹 접근성은 스크린 리더, 키보드 탐색, 스위치 접근 방식 또는 기타 보조 기술에 의존하는 사용자도 사이트를 이용할 수 있도록 보장합니다. 포용성 측면 외에도, 접근성이 높은 사이트는 검색 순위가 높고 모바일에서의 성능도 우수하며, 법적으로 요구되는 경우도 많습니다.
네 가지 POUR 원칙
웹 콘텐츠 접근성 지침(WCAG)은 네 가지 핵심 원칙을 기반으로 합니다. 콘텐츠는 다음을 충족해야 합니다:
- 인식 가능(Perceivable) — 정보는 사용자가 인식할 수 있는 방식으로 제공되어야 합니다(시각적인 방법에만 의존하지 않음).
- 운용 가능(Operable) — 모든 기능은 키보드로도 사용할 수 있어야 합니다.
- 이해 가능(Understandable) — 콘텐츠와 인터페이스는 이해하기 쉬워야 합니다.
- 견고함(Robust) — 콘텐츠는 보조 기술이 파싱할 수 있어야 합니다.
WCAG 2.2는 세 가지 준수 수준을 정의합니다:
- A — 최소 수준(필수)
- AA — 대부분의 조직이 목표로 하는 표준 수준
- AAA — 최고 수준(일부 콘텐츠에 대한 이상적 목표)
대부분의 법적 요구사항(ADA, EN 301 549, EAA)은 AA 준수를 요구합니다.
시맨틱 HTML 우선
접근성을 위해 가장 효과적인 방법은 목적에 맞는 올바른 HTML 요소를 사용하는 것입니다. 브라우저와 스크린 리더는 이미 시맨틱 요소의 처리 방법을 알고 있습니다:
<!-- ❌ Div soup — no semantics -->
<div class="header">
<div class="nav">
<div class="nav-item" onclick="navigate()">Home</div>
</div>
</div>
<!-- ✅ Semantic HTML — screen readers understand this -->
<header>
<nav>
<a href="/">Home</a>
</nav>
</header>
주요 시맨틱 요소와 역할:
| 요소 | 역할 |
|---|---|
<header>, <footer> |
랜드마크 영역 |
<nav> |
탐색 랜드마크 |
<main> |
주요 콘텐츠(페이지당 하나) |
<aside> |
보완 콘텐츠 |
<h1>–<h6> |
제목 계층 구조 |
<button> |
인터랙티브 컨트롤 |
<a href> |
탐색 링크 |
<label> |
폼 필드 레이블 |
<table> |
표 형식 데이터 |
이미지와 대체 텍스트
의미 있는 모든 이미지에는 내용을 설명하는 alt 속성이 필요합니다. 장식용 이미지는 alt=""를 빈 값으로 지정하여 스크린 리더가 건너뛰도록 합니다:
<!-- Meaningful image -->
<img src="chart.png" alt="Bar chart showing 40% increase in revenue Q1 2026">
<!-- Decorative image -->
<img src="divider.svg" alt="">
<!-- Icon button — describe the action, not the icon -->
<button>
<img src="trash.svg" alt="Delete item">
</button>
<!-- Avoid: redundant "image of" -->
<!-- ❌ --> <img src="cat.jpg" alt="Image of a cat">
<!-- ✅ --> <img src="cat.jpg" alt="Orange tabby cat sitting on a windowsill">
색상 대비
저시력 또는 색각 이상이 있는 사용자는 텍스트와 배경 간의 충분한 대비에 의존합니다.
WCAG AA 요구사항:
- 일반 텍스트(18pt 미만): 최소 4.5:1 대비율
- 큰 텍스트(18pt 이상 또는 14pt 굵은 글꼴): 최소 3:1 대비율
- UI 컴포넌트 및 그래픽 요소: 최소 3:1
정보 전달에 색상만 사용하지 마세요:
<!-- ❌ Color-only status indicator -->
<span class="text-red-500">Error</span>
<!-- ✅ Color + icon + text -->
<span class="text-red-500 flex items-center gap-1">
<svg aria-hidden="true"><!-- error icon --></svg>
Error: Invalid email address
</span>
폼: 레이블, 오류 메시지, 설명
모든 폼 컨트롤에는 눈에 보이는 연관된 레이블이 필요합니다:
<!-- ✅ Explicit label association -->
<label for="email">Email address</label>
<input id="email" type="email" aria-describedby="email-hint email-error">
<p id="email-hint" class="text-sm text-gray-500">We'll never share your email.</p>
<p id="email-error" role="alert" class="text-sm text-red-600" hidden>
Please enter a valid email address.
</p>
주요 폼 접근성 패턴:
for/id를 사용하여 레이블과 입력 필드를 연결- 힌트 텍스트와 오류 메시지에
aria-describedby사용 - 동적 오류 메시지에
role="alert"또는aria-live="polite"사용 aria-required="true"또는 기본required속성 사용- 관련 입력 요소를
<fieldset>과<legend>로 그룹화
키보드 탐색
모든 인터랙티브 요소는 키보드로 접근하고 조작할 수 있어야 합니다:
- Tab — 포커스 가능한 요소를 앞으로 이동
- Shift+Tab — 뒤로 이동
- Enter/Space — 버튼, 체크박스 활성화
- 화살표 키 — 컴포넌트 내 탐색(메뉴, 탭, 슬라이더)
- Escape — 모달 닫기, 드롭다운 닫기
포커스 표시기는 반드시 보여야 합니다. 대안 없이 이렇게 하지 마세요:
/* ❌ Hides focus indicator entirely */
*:focus { outline: none; }
/* ✅ Custom focus style that's still visible */
*:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
ARIA: 사용 시기와 방법
ARIA(Accessible Rich Internet Applications) 속성은 HTML만으로 충분하지 않을 때 시맨틱 의미를 추가합니다. ARIA의 첫 번째 규칙: 기본 HTML로 처리할 수 있다면 ARIA를 사용하지 마세요.
<!-- Labeling elements without visible labels -->
<button aria-label="Close dialog">✕</button>
<!-- Describing expanded state -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<!-- Live region for dynamic content -->
<div aria-live="polite" aria-atomic="true">
<!-- Screen readers announce changes to this area -->
3 results found
</div>
<!-- Landmark role when semantic element isn't available -->
<div role="search">
<input type="search" placeholder="Search...">
</div>
SPA와 모달의 포커스 관리
싱글 페이지 애플리케이션에서는 페이지 탐색 시 브라우저의 포커스가 초기화되지 않습니다. "페이지"가 로드될 때 의미 있는 위치로 포커스를 이동시키세요:
// After navigation, focus the main heading
document.querySelector("h1")?.focus();
모달의 경우:
- 모달이 열릴 때 내부의 첫 번째 포커스 가능한 요소로 포커스 이동
- 모달이 열려 있는 동안 포커스를 모달 안에 가두기(Tab이 뒤의 콘텐츠에 닿지 않도록)
- 모달이 닫힐 때 모달을 열었던 요소로 포커스 반환
접근성 테스트
자동화 도구(전체 문제의 약 30~40% 발견 가능)
- axe DevTools 브라우저 확장 프로그램
- Lighthouse Chrome DevTools의 접근성 감사
- WAVE 브라우저 확장 프로그램
수동 테스트(완전한 검증에 필수)
- 키보드 전용 탐색 — 마우스를 연결 해제하고 전체 사이트를 탐색해보기
- 스크린 리더 테스트 — NVDA + Firefox(Windows), VoiceOver + Safari(Mac/iOS)
- 200% 확대 — 콘텐츠가 사라지거나 겹치지 않는지 확인
- 색각 이상 시뮬레이션 — 브라우저 DevTools → Rendering → Emulate vision deficiencies
가독성
평이한 언어는 인지 장애가 있는 사용자의 접근성을 향상시킵니다. Readability Score 도구를 사용하여 콘텐츠의 읽기 수준을 확인하세요. 일반 독자를 대상으로 하는 경우 8~10학년 수준을 목표로 하세요.
지금 바로 실천할 수 있는 빠른 개선 사항
- 모든 이미지에
alt텍스트 추가 - 모든 폼 입력 필드에 연관된
<label>요소가 있는지 확인 - 본문 텍스트의 색상 대비가 4.5:1을 충족하는지 확인
:focus-visible스타일 추가 및 대체 스타일 없이 아웃라인을 제거하지 않기- 액션에는
<button>, 탐색에는<a href>사용(절대 반대로 사용하지 않기) <html>요소에lang="en"(또는 적절한 언어 코드) 추가- 페이지당
<h1>하나를 사용하고 논리적인 제목 계층 구조 유지
접근성은 부가적인 기능이 아니라 품질의 속성입니다. 처음부터 접근성을 고려해 제품을 만드는 것이 나중에 개선하는 것보다 훨씬 비용이 적게 들며, 모든 사람을 위해 더 나은 제품을 만드는 길입니다.