name: "a11y" description: "웹 접근성 체크 및 개선 가이드를 실행합니다." user-invocable: true
A11y (Accessibility) Skill
웹 접근성 체크 및 개선 가이드를 실행합니다.
WCAG 2.1 원칙
P - Perceivable 인식 가능
O - Operable 조작 가능
U - Understandable 이해 가능
R - Robust 견고함
체크리스트
1. 인식 가능 (Perceivable)
이미지 대체 텍스트
<!-- ✅ Good -->
<img src="logo.png" alt="회사 로고">
<img src="chart.png" alt="2024년 매출 추이: 1분기 100억, 2분기 120억...">
<!-- 장식용 이미지 -->
<img src="decoration.png" alt="" role="presentation">
<!-- ❌ Bad -->
<img src="logo.png">
<img src="chart.png" alt="차트">
색상 대비
텍스트: 4.5:1 이상 (일반), 3:1 이상 (큰 텍스트)
UI 컴포넌트: 3:1 이상
도구: WebAIM Contrast Checker
색상만으로 정보 전달 금지
<!-- ❌ Bad: 색상만으로 에러 표시 -->
<input style="border-color: red">
<!-- ✅ Good: 아이콘 + 텍스트 + 색상 -->
<input aria-invalid="true">
<span class="error">⚠️ 이메일 형식이 올바르지 않습니다</span>
2. 조작 가능 (Operable)
키보드 접근성
<!-- 모든 인터랙티브 요소는 키보드로 접근 가능해야 함 -->
<!-- ✅ Good -->
<button onclick="handleClick()">클릭</button>
<a href="/page">링크</a>
<!-- ❌ Bad -->
<div onclick="handleClick()">클릭</div>
<span onclick="navigate()">링크</span>
<!-- 커스텀 요소에 키보드 지원 추가 -->
<div
role="button"
tabindex="0"
onclick="handleClick()"
onkeydown="if(event.key === 'Enter') handleClick()"
>
버튼
</div>
포커스 관리
/* 포커스 표시 유지 */
:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
/* ❌ Bad: 포커스 표시 제거 */
:focus { outline: none; }
건너뛰기 링크
<body>
<a href="#main-content" class="skip-link">
본문으로 바로가기
</a>
<nav>...</nav>
<main id="main-content">...</main>
</body>
<style>
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 0;
}
</style>
3. 이해 가능 (Understandable)
폼 레이블
<!-- ✅ Good -->
<label for="email">이메일</label>
<input id="email" type="email" required>
<!-- 또는 -->
<label>
이메일
<input type="email" required>
</label>
<!-- aria-label 사용 -->
<input type="search" aria-label="사이트 검색">
에러 메시지
<label for="email">이메일</label>
<input
id="email"
type="email"
aria-describedby="email-error"
aria-invalid="true"
>
<span id="email-error" role="alert">
유효한 이메일 주소를 입력해주세요
</span>
4. 견고함 (Robust)
시맨틱 HTML
<!-- ✅ Good: 시맨틱 태그 사용 -->
<header>...</header>
<nav>...</nav>
<main>
<article>
<h1>제목</h1>
<section>...</section>
</article>
</main>
<footer>...</footer>
<!-- ❌ Bad: div 남용 -->
<div class="header">...</div>
<div class="nav">...</div>
ARIA 속성
<!-- 랜드마크 -->
<div role="navigation" aria-label="주 메뉴">...</div>
<div role="main">...</div>
<div role="search">...</div>
<!-- 상태 전달 -->
<button aria-expanded="false" aria-controls="menu">메뉴</button>
<ul id="menu" aria-hidden="true">...</ul>
<!-- 라이브 영역 -->
<div aria-live="polite">업데이트된 내용이 여기 표시됩니다</div>
<div aria-live="assertive">긴급 알림!</div>
컴포넌트별 가이드
모달
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<h2 id="modal-title">확인</h2>
<p>정말 삭제하시겠습니까?</p>
<button>취소</button>
<button>확인</button>
</div>
// 포커스 트랩
function trapFocus(element) {
const focusables = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusables[0];
const last = focusables[focusables.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
if (e.key === 'Escape') closeModal();
});
}
탭
<div role="tablist" aria-label="프로필 탭">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1"
>
기본 정보
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1"
>
보안 설정
</button>
</div>
<div
role="tabpanel"
id="panel-1"
aria-labelledby="tab-1"
>
기본 정보 내용
</div>
드롭다운
<div class="dropdown">
<button
aria-haspopup="listbox"
aria-expanded="false"
aria-controls="dropdown-list"
>
선택하세요
</button>
<ul
id="dropdown-list"
role="listbox"
aria-label="옵션"
hidden
>
<li role="option" aria-selected="false">옵션 1</li>
<li role="option" aria-selected="false">옵션 2</li>
</ul>
</div>
테스트 도구
자동화
# axe-core
npm install axe-core
# pa11y
npm install pa11y
pa11y https://example.com
# Lighthouse
# Chrome DevTools > Lighthouse > Accessibility
수동 테스트
1. 키보드만으로 모든 기능 사용 가능?
2. 스크린 리더로 내용 이해 가능?
3. 200% 확대해도 사용 가능?
4. 색상 제거해도 정보 전달 가능?
출력 형식
## Accessibility Audit
### Summary
- 전체 점수: XX/100
- Critical: N개
- Major: N개
- Minor: N개
### Issues
#### Critical
| 요소 | 문제 | WCAG | 해결책 |
|------|------|------|--------|
| `<img>` | alt 없음 | 1.1.1 | alt 추가 |
#### Recommendations
```html
<!-- Before -->
<div onclick="...">클릭</div>
<!-- After -->
<button onclick="...">클릭</button>
Testing Checklist
- 키보드 네비게이션
- 스크린 리더 테스트
- 색상 대비 검사
- 포커스 표시 확인
---
요청에 맞는 접근성 검토를 수행하세요.