연령, 장애 여부에 관계없이 웹 사이트에서 제공하는 정보에 동등하게 접근하고, 이용할 수 있도록 보장하는 것
동적 컨텐츠와 인터페이스 컨트롤을 위한 웹 접근성 표준으로, 보조 기술를 사용해 웹 애플리케이션을 효과적으로 탐색하는데 설계되 있다.
HTML Tag들에 추가적으로 시멘틱을 부여해서 브라우저 보조 기술들이 각 요소의 역할, 상태, 설명 등을 참고 할 수 있도록 한다.
적용 전:
<div v-if="isOpen" class="form-modal-background" @click.self="handleClose">
<div class="form-modal">
<h3>{{ modalTitle }}</h3>
<!-- 폼 내용 -->
</div>
</div>
적용 후:
<div
v-if="isOpen"
class="form-modal-background"
@click.self="handleClose"
role="dialog"
:aria-modal="isOpen"
aria-labelledby="modal-title"
>
<div class="form-modal">
<h3 id="modal-title">{{ modalTitle }}</h3>
<!-- 폼 내용 -->
</div>
</div>
개선 사항:
role="dialog": 스크린 리더에게 이것이 대화상자임을 알림aria-modal="true": 모달이 열려있을 때 배경 컨텐츠를 무시하도록 지시aria-labelledby: 모달의 제목을 명시적으로 연결
추가한 접근성 속성은 개발자 도구 Elements → Accessibility 에서 접근성 트리와 각 요소에 적용된 접근성 속성을 확인할 수 있다.
적용 전:
<input
:value="modelValue"
:placeholder="placeholder"
:required="required"
@input="handleInput"
/>
적용 후:
<input
:id="props.id"
:name="props.name"
:value="modelValue"
:placeholder="placeholder"
:required="required"
@input="handleInput"
:aria-invalid="props.invalid"
:aria-describedby="props.invalid ? `${props.id}-error` : undefined"
/>
<p
v-if="props.invalid && props.message"
:id="`${props.id}-error`"
class="error-message"
>
{{ props.message }}
</p>
개선 사항:
id와 name: 폼 요소 식별 및 label 연결aria-invalid: 입력값이 유효하지 않음을 스크린 리더에게 알림aria-describedby: 에러 메시지를 입력 필드와 연결id 부여적용 전:
<label class="form-label">
{{ field.label }}
<span v-if="field.required">*</span>
</label>
<FormInput :model-value="getFieldValue(field.name)" />
적용 후:
<label class="form-label" :for="field.name">
{{ field.label }}
<span v-if="field.required" class="required-mark">*</span>
</label>
<FormInput
:id="field.name"
:name="field.name"
:model-value="getFieldValue(field.name)"
/>
개선 사항:
<label for="field-name">: label과 input을 명시적으로 연결<button
:type="type"
:disabled="disabled"
:aria-label="ariaLabel"
class="icon-button"
>
<slot />
</button>
개선 사항:
aria-label 추가disabled 상태를 명시적으로 관리
탭(Tab) 키 입력시 Todo 버튼에 먼저 포커스가 가는 것을 확인할 수 있다.
.form-input:focus,
.form-textarea:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.form-input.form-error:focus {
border-color: #ef4444;
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}
개선 사항:
:focus 상태에서 명확한 시각적 피드백 제공outline을 제거하는 대신 box-shadow로 포커스 표시
폼 내에서도 동일하게 설정한 속성에 따라 포커스가 이동한다.
// 모바일에서도 충분한 터치 영역 확보
.icon-button {
padding: 0.5rem; // 최소 44x44px 터치 영역
cursor: pointer;
}
// 가독성을 위한 반응형 폰트 크기
h1 {
font-size: 1.5rem;
@media (min-width: 768px) {
font-size: 2rem;
}
}
개선 사항:
적용 전:
if (!value) {
showToast({ message: '입력해주세요.', variant: 'error' });
}
적용 후:
if (!value || String(value).trim() === '') {
field.invalid = true;
field.message = `${field.label}을(를) 입력해주세요.`;
showToast({ message: '필수 항목을 입력해주세요.', variant: 'error' });
}
개선 사항:
aria-describedby를 통해 에러 메시지 읽기실제로 스크린 리더(macOS VoiceOver, Windows Narrator)로 테스트한 결과:
개선 전:
개선 후:
웹 접근성을 적용한 결과:
모바일 서비스를 이용하는 누구나 불편한 없이 기기를 이용할 수 있게 하는 개념
⇒ 웹 접근성은 특별한 기능이나, 정해진 규칙에 맞추는 것이 아닌 다양한 어려움을 예방하는 UX/UI를 제공하는 것이 핵심
개발자들이 ARIA 속성을 사용할 때 겪는 어려움:
aria-expanded에 true를 넣어야 할까, "true"를 넣어야 할까?aria-expanded가 항상 "false"로 고정됨기존 도구: eslint-plugin-jsx-a11y
✅ 해결 가능
❌ 해결 불가능
// ❌ 이런 오류를 기존 도구는 찾지 못함
<button onClick={toggle} aria-expanded="false">
{/* 클릭해도 aria-expanded가 항상 false! */}
</button>
왜 불가능한가?
eslint-plugin-aria-state-validator핵심 아이디어: AST 분석으로 상태와 ARIA 속성의 연결 관계를 추론
// 1. JSXElement 순회: role 및 aria-* 속성 식별
<button onClick={() => setOpen(!isOpen)} aria-expanded="false">
// 2. 스코프 추적: context.getScope()로 변수 추적
// → onClick 핸들러에서 'isOpen' 변수 발견
// 3. 상태 추론: useState 훅에서 파생된 상태인지 판단
// → const [isOpen, setOpen] = useState(false)
// 4. 패턴 검증 & 자동 수정
// ❌ aria-expanded="false" (정적)
// ✅ aria-expanded={isOpen ? 'true' : 'false'} (동적)
state-dependent-aria-validator (동적 상태 검증)static-aria-validator (정적 ARIA 검증)React 생태계로 범위 한정
useState 훅 또는 Props 통한 상태 전달 추론이유:
Vue: ref, reactive
Svelte: $: reactive statements
Solid: createSignal
// React
const [isOpen, setOpen] = useState(false)
// Vue (향후 지원 가능)
const isOpen = ref(false)
// Svelte (향후 지원 가능)
let isOpen = $state(false)
useState 패턴이 가장 명확하고 일관적한국 접근성 인증 평가원 / 웹 접근성이란? - https://www.wa.or.kr/m1/sub1.asp
토스의 모바일 접근성 - https://toss.im/tossfeed/article/tinyquestions-disability-5