CI가 잡아낼 수 없는 접근성 실패 사례와 이를 해결하는 미디어 쿼리
요약
AI 생성 앱 196개를 분석한 결과, 66.3%가 WCAG 2.2.2를 위반하는 무한 애니메이션을 포함하고 있었습니다. 기존 CI 도구들이 놓치는 모션 접근성 문제를 지적하며, 미디어 쿼리를 활용한 해결책을 제시합니다.
핵심 포인트
- 기존 CI 도구(Lighthouse 등)는 모션 접근성 검사에 한계가 있음
- AI 생성 앱의 대다수가 'prefers-reduced-motion' 가드를 누락함
- 모션 접근성 실패는 기술적 한계가 아닌 디자인 시스템의 기본값 문제임
- 미디어 쿼리와 일시 중지 제어 장치를 통한 해결이 필요함
요약 (TL;DR)
- 196개의 AI 생성 프로덕션 앱을 대상으로 정적 모션 접근성 (motion-accessibility) 스캔을 수행했습니다.
- **66.3%**가 일시 중지할 수 없는 최소 하나 이상의 무한 애니메이션을 포함하여 출시되었습니다. 이는 WCAG 2.2.2 (일시 중지, 정지, 숨기기), 레벨 A를 위반합니다.
- **96.9%**가
prefers-reduced-motion가드(guard) 없이 모션을 제공합니다 (이는 엄격한 A/AA 실패는 아니지만, 베스트 프랙티스 측면에서의 공백입니다). 단 **3.1%**만이 깨끗한 상태였습니다. - 대부분의 문제에 대한 해결책은 미디어 쿼리 (media query)와 일시 중지 제어 장치입니다. 데이터, 방법론 및 공개 데이터셋은 하단에 있습니다.
공지: 저는 이 문제를 정확히 해결하기 위한 도구인 MotionSpec을 개발하고 있으며, 이 포스트의 초안을 작성하는 데 AI의 도움을 받았습니다. 따라서 방법론은 완전히 공개되어 있으며 모든 수치는 발표된 규칙을 통해 재현 가능합니다. 저를 믿지 말고 직접 확인하십시오. 플랫폼은 코호트 A~E로 익명 처리되었으며, 이는 특정 업체의 성적표가 아닙니다.
당신의 파이프라인이 보지 못하는 공백
당신의 CI (Continuous Integration)는 아마도 axe, Lighthouse, 또는 WAVE와 같은 접근성 체크를 실행하고 있을 것입니다. 이들은 좋은 도구들입니다. 하지만 이 도구들은 기본적으로 **모션 (motion)**을 살펴보지 않습니다. 이들은 구조와 콘텐츠, 즉 대비 (contrast), 대체 텍스트 (alt text), 레이블 (labels), ARIA 등을 감사합니다. 애니메이션 동작 — 즉, prefers-reduced-motion 지원, 일시 중지 가능한 루프, 예산 외 모션 (off-budget motion) — 등은 자동화된 스캐너가 이를 신뢰성 있게 평가하지 못하고, 시각적 회귀 (visual-regression) 도구들이 스크린샷 비교를 위해 의도적으로 애니메이션을 정지시키기 때문에 검사에서 누락됩니다.
이것이 바로 업계 최대 규모의 조사인 WebAIM Million 2026 (n = 1,000,000개의 홈페이지, 95.9%가 감지 가능한 WCAG 실패 포함)에서 대비/대체 텍스트/레이블을 반복적인 주요 실패 항목으로 나열하면서도 모션에 대해서는 전혀 언급하지 않는 이유입니다. 모션이 드물기 때문이 아니라, 측정되지 않기 때문입니다. 그래서 저는 가장 빠르게 성장하고 있는 결과물인 AI 앱 빌더(AI app-builders)가 만든 앱을 대상으로 이를 측정했습니다.
발견한 내용 (196개 앱)
| 코호트 (Cohort) | 앱 수 (Apps) | 1개 이상의 보호되지 않은 움직임 (≥1 unguarded motion) | 1개 이상의 루프, 일시정지 없음 (≥1 loop, no pause) | 중앙값 점수 (Median score) |
|---|---|---|---|---|
| A | 45 | 100% | 71.1% | 15 |
| ... |
5개 코호트 중 4개가 100% 보호되지 않은 상태를 기록했습니다. 이는 부주의가 아니라 기본값 (default) 설정입니다. 코호트 E가 예외적인 사례(outlier)입니다. 100% 미만을 기록한 유일한 그룹이며, 루프 실패율은 4분의 1 수준이고, 깨끗한 앱을 보유한 유일한 그룹입니다. 즉, 이 격차는 기술적 한계가 아니라 디자인 시스템의 기본값 문제라는 뜻입니다. 이미 한 플랫폼은 수치를 변화시킬 수 있을 만큼 충분히 자주 보호 기능을 제공하고 있습니다.
(0–100 사이의 "점수"는 저의 자체적인 휴리스틱 (heuristic)이며, WCAG 준수율이 아닙니다. 재현 가능하기 때문에 보고하지만, 실질적인 근거가 되는 수치는 WCAG에 매핑된 백분율입니다.)
66.3%가 중요한 수치인 이유
두 가지 규칙이 작용하고 있습니다:
- WCAG 2.2.2 일시정지, 중지, 숨기기 (Pause, Stop, Hide) — 레벨 A (Level A). 자동으로 움직이거나, 5초 이상 실행되거나, 다른 콘텐츠와 함께 재생되는 모든 요소는 일시정지/중지/숨기기 메커니즘이 필요합니다. 일시정지가 없는 무한 루프는 최소 기준인 레벨 A를 충족하지 못합니다 (fails Level A).
- WCAG 2.3.3 상호작용에 의한 애니메이션 (Animation from Interactions) — 레벨 AAA (Level AAA). "
prefers-reduced-motion보호 장치 없음"이 여기에 해당합니다. 이는 모범 사례(best practice)이며 상호작용 범위 내의 문제로, 엄격한 A/AA 실패 사항은 아닙니다. 제가 96.9%가 아닌 66.3%를 먼저 제시한 이유가 바로 이것입니다.
전정 기관 장애 (vestibular disorder)가 있는 사람에게 이것은 미적인 문제가 아닙니다. 급격한 패럴랙스 (parallax) 효과와 무한 루프는 실제 어지러움, 메스꺼움, 편두통을 유발합니다. OS 수준의 동작 줄이기 (Reduce Motion) 스위치는 페이지가 이를 감지하고 반응할 때만 도움이 됩니다.
해결 방법
1. 비필수적인 움직임에 보호 장치를 적용하세요.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important;
...
투박한 안전망이지만, 실제 구현에서는 특정 애니메이션을 보호해야 합니다. 2. 루프에 오프 스위치를 제공하세요. animation-play-state: paused와 연결된 일시정지 버튼을 만들거나, 유한한 animation-iteration-count를 설정하세요. 이것이 앱을 66.3%의 범주에서 벗어나게 만드는 방법입니다.
만약 AI를 통해 UI를 생성한다면 — 앱 빌더(app-builder), 디자인-투-코드(design-to-code) 도구, 또는 자체적인 LLM 파이프라인 — 시스템 프롬프트(system prompt)나 컴포넌트 기본값(component defaults)에 "prefers-reduced-motion을 준수하고, 일시 정지 없이 무한 루프를 배포하지 마세요"라는 문구를 추가하세요. Cohort E는 기본값(defaults)이 핵심적인 레버(lever)임을 증명합니다.
방법론 (인용 전 읽어주세요)
각 페이지에 연결된 CSS와 인라인 스타일(inline styles)에 대한 정적 스캔만 수행되었습니다 → 이는 하한선 (lower bound) 입니다 (런타임 JS/GSAP/WAAPI 모션은 측정되지 않았으며, 앱당 하나의 페이지를 조사했습니다). 196개의 앱을 대상으로 했으며, 코호트(cohort)당 21~45개를 조사하였고, 2026-07-16에 수집되었습니다. 출처(provenance)를 기록하고, robots.txt를 준수하였으며, 로그인 벽(login-walls)이 있는 페이지는 제외했습니다. 두 기준 모두 완전히 기계적으로 확인할 수 있는 것은 아니기에 ("필수적인 (essential)" 모션은 인간의 판단 영역입니다), 이는 준수 여부에 대한 판결이 아니라 자동화 가능한 실패 _패턴 (patterns)_입니다.
- 전체 보고서 + 코호트별 상세 내용: https://motionspec.dev/blog/state-of-motion-ai-generated-uis
- 오픈 데이터셋 (CC-BY-4.0, 196행, 익명화됨): https://motionspec.dev/blog/data/state-of-motion-2026.csv
- 본인의 URL 스캔하기 (무료, 정적, 데이터 저장 안 함): https://motionspec.dev/motion-check?utm_source=article&utm_medium=devto
여러분의 차례
현재 여러분의 스택은 prefers-reduced-motion을 어떻게 처리하고 있나요? 글로벌 리셋(global reset), 컴포넌트별 가드(per-component guards), 아니면 아직 아무것도 적용되지 않았나요? 그리고 만약 UI를 생성한다면, 여러분의 파이프라인(pipeline)은 이 규칙을 알고 있나요? 사람들이 어떻게 하고 있는지 궁금합니다 — 아래에 댓글을 남겨주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기