웹 접근성 테스트의 필요성
웹 접근성(a11y)은 장애를 가진 사용자도 웹 콘텐츠를 이용할 수 있도록 보장하는 것입니다. WCAG 2.1 지침을 준수해야 법적 요구사항을 충족하고, 더 많은 사용자에게 서비스를 제공할 수 있습니다. 자동 테스트로 접근성 문제의 약 30-50%를 탐지할 수 있으며, 나머지는 수동 테스트가 필요합니다.
axe-core 기본 사용
// axe-core 설치
npm install axe-core @axe-core/react @axe-core/playwright
// React 개발 환경에서 실시간 감지
// src/index.tsx
if (process.env.NODE_ENV === 'development') {
import('@axe-core/react').then(axe => {
axe.default(React, ReactDOM, 1000);
// 콘솔에 접근성 위반 사항이 실시간 표시
});
}
// 프로그래밍 방식으로 검사
import axe from 'axe-core';
async function runAccessibilityCheck() {
const results = await axe.run(document, {
rules: {
'color-contrast': { enabled: true },
'image-alt': { enabled: true },
},
tags: ['wcag2a', 'wcag2aa'],
});
console.log('위반:', results.violations.length);
results.violations.forEach(v => {
console.log(`[${v.impact}] ${v.id}: ${v.description}`);
v.nodes.forEach(n => console.log(' -', n.html));
});
}
Playwright + axe-core 통합 테스트
// tests/accessibility.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('접근성 테스트', () => {
test('홈페이지 접근성', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.exclude('.third-party-widget') // 서드파티 제외
.analyze();
expect(results.violations).toEqual([]);
});
test('로그인 폼 접근성', async ({ page }) => {
await page.goto('/login');
const results = await new AxeBuilder({ page })
.include('#login-form') // 특정 영역만 검사
.analyze();
// 심각도별 필터링
const critical = results.violations.filter(
v => v.impact === 'critical' || v.impact === 'serious'
);
expect(critical).toEqual([]);
});
test('키보드 내비게이션', async ({ page }) => {
await page.goto('/');
// Tab 키로 모든 인터랙티브 요소 순회 가능한지 확인
await page.keyboard.press('Tab');
const firstFocused = await page.evaluate(() =>
document.activeElement?.tagName
);
expect(firstFocused).toBeTruthy();
// Skip to content 링크 확인
const skipLink = page.locator('a[href="#main-content"]');
await expect(skipLink).toBeFocused();
});
});
Lighthouse CI 설정
// lighthouserc.js
module.exports = {
ci: {
collect: {
url: [
'http://localhost:3000/',
'http://localhost:3000/about',
'http://localhost:3000/login',
],
numberOfRuns: 3,
settings: {
preset: 'desktop',
onlyCategories: ['accessibility'],
},
},
assert: {
assertions: {
'categories:accessibility': ['error', { minScore: 0.9 }],
'color-contrast': 'error',
'image-alt': 'error',
'link-name': 'error',
'button-name': 'error',
'html-has-lang': 'error',
'meta-viewport': 'warn',
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
GitHub Actions CI 통합
# .github/workflows/a11y.yml
name: Accessibility Tests
on: [pull_request]
jobs:
accessibility:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
# Playwright + axe-core 테스트
- run: npx playwright install --with-deps
- run: npm run test:a11y
# Lighthouse CI
- run: npm install -g @lhci/cli
- run: npm start &
- run: lhci autorun
- uses: actions/upload-artifact@v4
if: failure()
with:
name: a11y-report
path: .lighthouseci/
흔한 접근성 위반과 수정
| 위반 | 심각도 | 수정 방법 |
|---|---|---|
| color-contrast | serious | 텍스트/배경 대비 4.5:1 이상 보장 |
| image-alt | critical | 모든 img에 의미 있는 alt 속성 |
| button-name | critical | 아이콘 버튼에 aria-label 추가 |
| link-name | serious | 링크에 설명적 텍스트 제공 |
| form-field-label | critical | 모든 입력에 연결된 label |
// 수정 예시
// BAD
<button><svg>...</svg></button>
<img src="logo.png">
<a href="/profile"><img src="avatar.png"></a>
// GOOD
<button aria-label="메뉴 열기"><svg aria-hidden="true">...</svg></button>
<img src="logo.png" alt="Young Sam 로고">
<a href="/profile" aria-label="내 프로필"><img src="avatar.png" alt=""></a>
- 자동 테스트는 접근성 문제의 일부만 감지합니다 — 스크린 리더 테스트와 실제 사용자 테스트를 병행하세요
- CI에서 critical/serious 위반은 빌드를 실패시키고, minor는 경고로 처리하는 것이 현실적입니다
- 새로운 컴포넌트에 대한 접근성 테스트를 Storybook play function으로 작성하면 개발 단계에서 조기 발견할 수 있습니다
댓글 0