Type something to search...

03 웹접근성

1. 웹 접근성 교안

1.1. 인식의 용이성 (Perceivability)

1.1.1. 1.1 대체 텍스트 (Alternative Text)

개념: 이미지나 비텍스트 콘텐츠를 볼 수 없는 사용자를 위해 텍스트 설명을 제공합니다.

1.1.1.1. 예제 코드

1
<!-- 1. img 태그에 alt 속성으로 이미지 설명 제공 -->
2
<img src="cat.jpg" alt="흰색 고양이가 햇빛 아래 낮잠을 자고 있습니다">
3
4
<!-- 2. 장식용 이미지는 빈 alt 사용 (스크린리더가 건너뜀) -->
5
<img src="line.png" alt="">
6
7
<!-- 3. 의미있는 배경 이미지는 aria-label로 설명 -->
8
<div style="background-image: url('logo.png')"
9
role="img"
10
aria-label="회사 로고">
11
</div>
12
13
<!-- 4. 아이콘 버튼에 텍스트 설명 제공 -->
14
<button aria-label="검색">
15
<img src="search.png" alt="">
16
</button>

설명:

  • 1번: 의미있는 이미지는 내용을 설명하는 alt 제공
  • 2번: 장식만을 위한 이미지는 alt를 비워둠
  • 3번: CSS 배경 이미지도 role과 aria-label로 설명 가능
  • 4번: 아이콘 버튼은 버튼의 기능을 aria-label로 설명

1.1.2. 1.2 색상 대비 (Color Contrast)

개념: 텍스트와 배경 사이에 충분한 명도 대비를 제공하여 저시력 사용자도 내용을 읽을 수 있게 합니다.

1.1.2.1. 기준

  • 일반 텍스트: 최소 4.5:1 대비율
  • 큰 텍스트 (18pt 이상): 최소 3:1 대비율

1.1.2.2. 예제 코드

1
<!-- 1. 좋은 예: 검정 텍스트 + 흰색 배경 (대비율 21:1) -->
2
<p style="color: #000; background: #fff">
3
읽기 쉬운 텍스트입니다
4
</p>
5
6
<!-- 2. 나쁜 예: 회색 텍스트 + 흰색 배경 (대비율 2.5:1) -->
7
<p style="color: #999; background: #fff">
8
읽기 어려운 텍스트입니다
9
</p>
10
11
<!-- 3. 좋은 예: 진한 파랑 + 흰색 배경 (대비율 8.6:1) -->
12
<p style="color: #0056b3; background: #fff">
13
충분한 대비가 있는 텍스트입니다
14
</p>
15
16
<!-- 4. 큰 텍스트는 대비 기준이 낮음 -->
17
<h1 style="font-size: 24pt; color: #666; background: #fff">
18
큰 제목 텍스트
19
</h1>

설명:

  • 1번: 검정과 흰색은 최고의 대비율 제공
  • 2번: 연한 회색은 대비가 부족하여 접근성 기준 미달
  • 3번: 진한 색상 사용으로 충분한 대비 확보
  • 4번: 큰 텍스트는 3:1 대비율만 필요

1.1.3. 1.3 자막 및 수화 (Captions and Sign Language)

개념: 청각 장애인을 위해 오디오 콘텐츠에 자막을 제공합니다.

1.1.3.1. 예제 코드

1
<!-- 1. video 태그에 track 요소로 자막 파일 연결 -->
2
<video controls>
3
<!-- 2. src 속성에 비디오 파일 경로 지정 -->
4
<source src="video.mp4" type="video/mp4">
5
6
<!-- 3. track 태그로 자막 파일 추가 (WebVTT 형식) -->
7
<track kind="captions"
8
src="captions-ko.vtt"
9
srclang="ko"
10
label="한국어">
11
12
<!-- 4. 영어 자막도 추가 가능 -->
13
<track kind="captions"
14
src="captions-en.vtt"
15
srclang="en"
16
label="English">
17
</video>
18
19
<!-- 5. 오디오에도 동일하게 적용 -->
20
<audio controls>
21
<source src="podcast.mp3" type="audio/mp3">
22
<track kind="captions"
23
src="transcript.vtt"
24
srclang="ko"
25
label="한국어 자막">
26
</audio>

설명:

  • 1번: video 태그에 controls 속성으로 재생 컨트롤 표시
  • 2번: source로 비디오 파일과 MIME 타입 지정
  • 3번: track으로 자막 파일 연결 (kind=“captions”)
  • 4번: 여러 언어 자막 제공 가능
  • 5번: 오디오도 동일한 방식으로 자막 제공

1.1.4. 1.4 콘텐츠 구조화 (Content Structure)

개념: 올바른 HTML 구조로 콘텐츠의 위계와 의미를 명확하게 전달합니다.

1.1.4.1. 예제 코드

1
<!-- 1. 페이지에는 하나의 h1 제목만 사용 -->
2
<h1>웹 접근성 가이드</h1>
3
4
<!-- 2. 제목은 순서대로 사용 (h1 → h2 → h3) -->
5
<h2>인식의 용이성</h2>
6
7
<!-- 3. h2 다음에 h3 사용 (h4로 건너뛰지 않음) -->
8
<h3>대체 텍스트</h3>
9
10
<p>이미지에 대한 설명입니다.</p>
11
12
<!-- 4. 같은 레벨의 제목은 같은 숫자 사용 -->
13
<h3>색상 대비</h3>
14
15
<p>색상 대비에 대한 설명입니다.</p>
16
17
<!-- 5. 다음 섹션으로 넘어갈 때 h2 사용 -->
18
<h2>운용의 용이성</h2>
19
20
<!-- 6. 목록은 ul, ol 태그 사용 -->
21
<ul>
22
<li>첫 번째 항목</li>
23
<li>두 번째 항목</li>
24
</ul>
25
26
<!-- 7. 순서가 중요한 목록은 ol 사용 -->
27
<ol>
28
<li>로그인하기</li>
29
<li>상품 선택하기</li>
30
<li>결제하기</li>
31
</ol>

설명:

  • 1번: 페이지당 하나의 h1으로 메인 제목 표시
  • 2번: h1 다음에 h2로 하위 섹션 시작
  • 3번: 제목 레벨을 순서대로 사용 (건너뛰지 않음)
  • 4번: 같은 중요도의 내용은 같은 제목 레벨 사용
  • 5번: 새로운 주요 섹션은 h2로 시작
  • 6번: 순서가 없는 목록은 ul 사용
  • 7번: 순서가 중요한 목록은 ol 사용

1.2. 운용의 용이성 (Operability)

1.2.1. 2.1 키보드 접근성 (Keyboard Accessibility)

개념: 마우스 없이 키보드만으로 모든 기능을 사용할 수 있어야 합니다.

1.2.1.1. 예제 코드

1
<!-- 1. button 태그는 기본적으로 키보드 접근 가능 -->
2
<button onclick="save()">저장</button>
3
4
<!-- 2. 나쁜 예: div는 키보드로 접근 불가 -->
5
<div onclick="save()">저장</div>
6
7
<!-- 3. 좋은 예: div에 tabindex와 role 추가 -->
8
<div tabindex="0"
9
role="button"
10
onclick="save()"
11
onkeypress="if(event.key==='Enter') save()">
12
저장
13
</div>
14
15
<!-- 4. 링크는 기본적으로 Tab키로 이동 가능 -->
16
<a href="page.html">다음 페이지</a>
17
18
<!-- 5. 폼 요소는 모두 키보드 접근 가능 -->
19
<input type="text" placeholder="이름 입력">
20
<select>
21
<option>옵션 1</option>
22
<option>옵션 2</option>
23
</select>
24
25
<!-- 6. tabindex로 탭 순서 제어 (양수는 피할 것) -->
26
<input tabindex="1" type="text">
27
<input tabindex="2" type="text">
28
29
<!-- 7. tabindex="-1"은 포커스는 가능하지만 탭 순서에서 제외 -->
30
<div tabindex="-1" id="msg">알림 메시지</div>
31
32
<!-- 8. 모달 창에서는 포커스 트랩 구현 -->
33
<div role="dialog" aria-modal="true">
34
<h2>알림</h2>
35
<p>내용입니다</p>
36
<button>확인</button>
37
</div>

설명:

  • 1번: button은 Tab, Enter, Space 키로 자동 작동
  • 2번: div는 기본적으로 포커스 불가능
  • 3번: tabindex=“0”으로 포커스 가능하게 만듦
  • 4번: a 태그는 Tab으로 이동, Enter로 클릭
  • 5번: 모든 input, select 등은 키보드 접근 가능
  • 6번: tabindex 양수는 탭 순서를 복잡하게 만들어 피해야 함
  • 7번: tabindex=“-1”은 JS로만 포커스 가능
  • 8번: 모달에서는 포커스가 모달 내부에만 머물러야 함

1.2.2. 2.2 충분한 시간 제공 (Enough Time)

개념: 사용자가 콘텐츠를 읽고 사용할 충분한 시간을 제공합니다.

1.2.2.1. 예제 코드

1
<!-- 1. 자동으로 넘어가는 슬라이드에 일시정지 버튼 제공 -->
2
<div id="slide">
3
<img src="slide1.jpg" alt="첫 번째 슬라이드">
4
<!-- 2. 일시정지, 재생 버튼 -->
5
<button onclick="pause()">일시정지</button>
6
<button onclick="play()">재생</button>
7
</div>
8
9
<script>
10
// 3. 자동 슬라이드 변수
11
let timer;
12
let paused = false;
13
14
// 4. 슬라이드 자동 전환 함수
15
function autoSlide() {
16
// 5. 일시정지 상태가 아닐 때만 실행
17
if (!paused) {
18
// 슬라이드 전환 코드
19
}
20
// 6. 5초마다 실행 (충분한 시간 제공)
21
timer = setTimeout(autoSlide, 5000);
22
}
23
24
// 7. 일시정지 함수
25
function pause() {
26
paused = true;
27
clearTimeout(timer);
28
}
29
30
// 8. 재생 함수
31
function play() {
32
paused = false;
33
autoSlide();
34
}
35
</script>
36
37
<!-- 9. 세션 타임아웃 경고 -->
38
<div id="warn" style="display:none">
39
<!-- 10. 남은 시간 표시 -->
40
<p>5분 후 자동 로그아웃됩니다.</p>
41
<!-- 11. 연장 버튼 제공 -->
42
<button onclick="extend()">시간 연장</button>
43
</div>
44
45
<script>
46
// 12. 타임아웃 전 경고 표시
47
setTimeout(function() {
48
document.getElementById('warn').style.display = 'block';
49
}, 25 * 60 * 1000); // 25분 후 경고
50
</script>

설명:

  • 1번: 자동 슬라이드를 포함한 div 영역
  • 2번: 사용자가 슬라이드를 제어할 수 있는 버튼
  • 3번: 타이머와 일시정지 상태를 저장하는 변수
  • 4번: 슬라이드를 자동으로 전환하는 함수
  • 5번: 일시정지 중이 아닐 때만 슬라이드 전환
  • 6번: 5초 간격으로 충분한 읽기 시간 제공
  • 7번: 사용자가 일시정지 버튼을 누르면 타이머 중지
  • 8번: 재생 버튼으로 다시 자동 전환 시작
  • 9번: 세션 만료 경고를 위한 숨겨진 div
  • 10번: 남은 시간을 알려주는 메시지
  • 11번: 세션을 연장할 수 있는 버튼
  • 12번: 타임아웃 5분 전에 경고 표시

1.2.3. 2.3 발작 예방 (Seizure Prevention)

개념: 깜빡이거나 번쩍이는 콘텐츠로 인한 발작을 예방합니다.

1.2.3.1. 예제 코드

1
<!-- 1. 나쁜 예: 빠르게 깜빡이는 애니메이션 -->
2
<div style="animation: blink 0.3s infinite">
3
깜빡임
4
</div>
5
6
<style>
7
/* 2. 초당 3회 이상 깜빡임은 위험 */
8
@keyframes blink {
9
0% { opacity: 1; }
10
50% { opacity: 0; }
11
100% { opacity: 1; }
12
}
13
</style>
14
15
<!-- 3. 좋은 예: 애니메이션 제어 옵션 제공 -->
16
<button onclick="stop()">애니메이션 중지</button>
17
18
<div id="anim">
19
움직이는 콘텐츠
20
</div>
21
22
<script>
23
// 4. 애니메이션 중지 함수
24
function stop() {
25
// 5. CSS 애니메이션 제거
26
document.getElementById('anim').style.animation = 'none';
27
}
28
</script>
29
30
<!-- 6. prefers-reduced-motion으로 사용자 설정 존중 -->
31
<style>
32
/* 7. 기본 애니메이션 */
33
.box {
34
transition: transform 0.3s;
35
}
36
37
/* 8. 사용자가 애니메이션 줄이기를 선택한 경우 */
38
@media (prefers-reduced-motion: reduce) {
39
.box {
40
/* 9. 모든 애니메이션과 전환 효과 제거 */
41
transition: none;
42
animation: none;
43
}
44
}
45
</style>

설명:

  • 1번: 0.3초마다 깜빡이는 것은 발작을 유발할 수 있음
  • 2번: 초당 3회 이상의 깜빡임은 접근성 위반
  • 3번: 사용자가 애니메이션을 멈출 수 있는 버튼 제공
  • 4번: 버튼 클릭 시 애니메이션 중지하는 함수
  • 5번: CSS의 animation 속성을 none으로 설정
  • 6번: 미디어 쿼리로 사용자 선호도 확인
  • 7번: 일반 사용자를 위한 기본 애니메이션
  • 8번: 애니메이션 줄이기 설정을 한 사용자 감지
  • 9번: 해당 사용자에게는 모든 움직임 제거

1.2.4. 2.4 네비게이션 (Navigation)

개념: 사용자가 콘텐츠를 쉽게 탐색할 수 있도록 명확한 네비게이션을 제공합니다.

1.2.4.1. 예제 코드

1
<!-- 1. 본문 바로가기 링크 (페이지 최상단) -->
2
<a href="#main" class="skip">본문 바로가기</a>
3
4
<!-- 2. 메인 네비게이션을 nav 태그로 감싸기 -->
5
<nav aria-label="주 메뉴">
6
<!-- 3. 목록으로 메뉴 구조화 -->
7
<ul>
8
<li><a href="/">홈</a></li>
9
<li><a href="/about">소개</a></li>
10
<li><a href="/contact">연락처</a></li>
11
</ul>
12
</nav>
13
14
<!-- 4. 현재 페이지 표시 -->
15
<nav aria-label="주 메뉴">
16
<ul>
17
<li><a href="/">홈</a></li>
18
<!-- 5. aria-current로 현재 페이지 표시 -->
19
<li><a href="/about" aria-current="page">소개</a></li>
20
<li><a href="/contact">연락처</a></li>
21
</ul>
22
</nav>
23
24
<!-- 6. 빵 부스러기(breadcrumb) 네비게이션 -->
25
<nav aria-label="빵부스러기">
26
<!-- 7. 순서가 있는 목록 사용 -->
27
<ol>
28
<li><a href="/">홈</a></li>
29
<li><a href="/products">상품</a></li>
30
<!-- 8. 현재 위치는 링크 없이 표시 -->
31
<li aria-current="page">노트북</li>
32
</ol>
33
</nav>
34
35
<!-- 9. 본문 영역은 main 태그로 표시 -->
36
<main id="main">
37
<h1>페이지 제목</h1>
38
<p>본문 내용입니다.</p>
39
</main>
40
41
<!-- 10. 페이지 내 점프 링크 -->
42
<nav aria-label="목차">
43
<ul>
44
<!-- 11. 각 섹션의 id로 이동 -->
45
<li><a href="#sec1">섹션 1</a></li>
46
<li><a href="#sec2">섹션 2</a></li>
47
<li><a href="#sec3">섹션 3</a></li>
48
</ul>
49
</nav>
50
51
<!-- 12. 각 섹션에 id 부여 -->
52
<section id="sec1">
53
<h2>섹션 1</h2>
54
</section>
55
56
<section id="sec2">
57
<h2>섹션 2</h2>
58
</section>

설명:

  • 1번: 페이지 최상단에 본문으로 바로 가는 링크 제공
  • 2번: nav 태그와 aria-label로 네비게이션 영역 명확히 구분
  • 3번: 메뉴는 ul, li로 구조화하여 개수 파악 용이
  • 4번: 네비게이션 영역 정의
  • 5번: aria-current=“page”로 현재 페이지 표시
  • 6번: 빵부스러기는 사용자의 현재 위치 표시
  • 7번: 순서가 중요하므로 ol 사용
  • 8번: 현재 위치는 링크가 아닌 텍스트로 표시
  • 9번: main 태그로 주요 콘텐츠 영역 표시
  • 10번: 페이지 내 섹션으로 이동하는 목차
  • 11번: href=“#id”로 같은 페이지 내 이동
  • 12번: 각 섹션에 고유한 id 값 부여

1.3. 이해의 용이성 (Understandability)

1.3.1. 3.1 읽기 쉬운 텍스트 (Readable Text)

개념: 명확하고 이해하기 쉬운 언어를 사용합니다.

1.3.1.1. 예제 코드

1
<!-- 1. 페이지 언어 지정 (한국어) -->
2
<html lang="ko">
3
4
<!-- 2. 특정 부분만 다른 언어일 경우 -->
5
<p>
6
<!-- 3. 한국어 문장 -->
7
안녕하세요.
8
<!-- 4. 영어 부분만 lang 속성 지정 -->
9
<span lang="en">Hello</span>입니다.
10
</p>
11
12
<!-- 5. 약어나 줄임말에 설명 제공 -->
13
<p>
14
<!-- 6. abbr 태그로 약어 표시 -->
15
<abbr title="하이퍼텍스트 마크업 언어">HTML</abbr>은
16
웹 페이지를 만드는 언어입니다.
17
</p>
18
19
<!-- 7. 복잡한 단어에 설명 추가 -->
20
<p>
21
<!-- 8. dfn 태그로 정의 표시 -->
22
<dfn title="웹 페이지를 모두가 사용할 수 있게 만드는 것">
23
웹 접근성
24
</dfn>은 중요합니다.
25
</p>
26
27
<!-- 9. 발음이 어려운 단어에 루비 주석 -->
28
<ruby>
29
<!-- 10. 한자 표시 -->
30
漢字
31
<!-- 11. 읽는 방법 표시 -->
32
<rt>한자</rt>
33
</ruby>

설명:

  • 1번: html 태그에 lang 속성으로 페이지 언어 지정
  • 2번: 문장 안에 다른 언어가 섞여 있는 경우
  • 3번: 기본 언어는 한국어로 설정됨
  • 4번: 영어 부분에만 lang=“en” 추가
  • 5번: 약어는 의미를 모를 수 있음
  • 6번: abbr 태그의 title로 전체 명칭 제공
  • 7번: 어려운 용어에 설명 필요
  • 8번: dfn 태그의 title로 용어 정의
  • 9번: 한자나 어려운 글자에 읽는 법 표시
  • 10번: 원래 글자 (한자)
  • 11번: rt 태그로 읽는 방법 (한글) 표시

1.3.2. 3.2 예측 가능성 (Predictability)

개념: 웹 페이지가 예측 가능한 방식으로 작동해야 합니다.

1.3.2.1. 예제 코드

1
<!-- 1. 나쁜 예: 포커스만으로 팝업 열림 -->
2
<input type="text"
3
onfocus="openPopup()">
4
5
<!-- 2. 좋은 예: 명시적 클릭으로 팝업 열림 -->
6
<input type="text" id="inp">
7
<button onclick="openPopup()">도움말 보기</button>
8
9
<!-- 3. 나쁜 예: 선택만으로 페이지 이동 -->
10
<select onchange="location.href=this.value">
11
<option value="page1.html">페이지 1</option>
12
<option value="page2.html">페이지 2</option>
13
</select>
14
15
<!-- 4. 좋은 예: 버튼으로 이동 확인 -->
16
<select id="sel">
17
<option value="page1.html">페이지 1</option>
18
<option value="page2.html">페이지 2</option>
19
</select>
20
<!-- 5. 이동 버튼 별도 제공 -->
21
<button onclick="go()">이동</button>
22
23
<script>
24
// 6. 사용자가 버튼을 눌러야 이동
25
function go() {
26
const sel = document.getElementById('sel');
27
location.href = sel.value;
28
}
29
</script>
30
31
<!-- 7. 새 창 열림을 사전에 알림 -->
32
<a href="page.html"
33
target="_blank">
34
<!-- 8. 새 창에서 열림을 명시 -->
35
외부 사이트 (새 창)
36
</a>
37
38
<!-- 9. 또는 aria-label로 설명 -->
39
<a href="page.html"
40
target="_blank"
41
aria-label="외부 사이트, 새 창에서 열림">
42
외부 사이트
43
</a>

설명:

  • 1번: 포커스만으로 팝업이 열리면 예측 불가
  • 2번: 별도 버튼으로 사용자가 의도적으로 실행
  • 3번: select 변경만으로 페이지 이동은 혼란스러움
  • 4번: select는 선택만 함
  • 5번: 이동 버튼을 따로 제공하여 명확한 의도 표현
  • 6번: 버튼 클릭 시에만 페이지 이동 실행
  • 7번: 새 창으로 열릴 때 텍스트로 명시
  • 8번: 괄호 안에 “새 창” 표시
  • 9번: 또는 aria-label로 스크린리더 사용자에게 알림

1.3.3. 3.3 입력 지원 (Input Assistance)

개념: 사용자가 오류를 방지하고 수정할 수 있도록 도와줍니다.

1.3.3.1. 예제 코드

1
<!-- 1. 폼에 명확한 레이블 제공 -->
2
<form>
3
<!-- 2. label과 input을 for-id로 연결 -->
4
<label for="name">이름</label>
5
<input type="text" id="name" required>
6
7
<!-- 3. 필수 입력 표시와 설명 -->
8
<label for="email">
9
이메일
10
<!-- 4. 필수 표시 -->
11
<span aria-label="필수">*</span>
12
</label>
13
<!-- 5. 입력 형식 안내 -->
14
<input type="email"
15
id="email"
16
placeholder="example@email.com"
17
required
18
aria-describedby="tip">
19
<!-- 6. 추가 설명 제공 -->
20
<small id="tip">이메일 형식으로 입력해주세요</small>
21
22
<!-- 7. 오류 메시지 영역 -->
23
<div id="err"
24
role="alert"
25
style="display:none; color:red">
26
</div>
27
28
<button type="submit">제출</button>
29
</form>
30
31
<script>
32
// 8. 폼 제출 시 유효성 검사
33
document.querySelector('form').addEventListener('submit', function(e) {
34
// 9. 기본 제출 동작 막기
35
e.preventDefault();
36
37
const name = document.getElementById('name').value;
38
const email = document.getElementById('email').value;
39
const err = document.getElementById('err');
40
41
// 10. 이름이 비어있는지 확인
42
if (!name) {
43
// 11. 오류 메시지 표시
44
err.textContent = '이름을 입력해주세요';
45
err.style.display = 'block';
46
// 12. 해당 필드로 포커스 이동
47
document.getElementById('name').focus();
48
return;
49
}
50
51
// 13. 이메일 형식 확인
52
if (!email.includes('@')) {
53
err.textContent = '올바른 이메일 형식이 아닙니다';
54
err.style.display = 'block';
55
document.getElementById('email').focus();
56
return;
57
}
58
59
// 14. 모두 정상이면 제출
60
alert('제출 완료');
61
});
62
</script>
63
64
<!-- 15. 비밀번호 입력 시 조건 표시 -->
65
<label for="pw">비밀번호</label>
66
<input type="password"
67
id="pw"
68
aria-describedby="pwrule">
69
<!-- 16. 비밀번호 규칙 설명 -->
70
<ul id="pwrule">
71
<li>8자 이상</li>
72
<li>영문, 숫자 포함</li>
73
</ul>

설명:

  • 1번: 폼 요소들을 form 태그로 감싸기
  • 2번: label의 for와 input의 id를 동일하게 연결
  • 3번: label 안에 필수 표시 포함
  • 4번: 별표(*)로 필수 필드 표시, aria-label로 의미 전달
  • 5번: placeholder로 입력 예시 표시
  • 6번: aria-describedby로 연결된 설명 텍스트
  • 7번: role=“alert”로 오류 메시지 영역 지정
  • 8번: submit 이벤트로 유효성 검사 실행
  • 9번: preventDefault로 바로 제출되지 않게 함
  • 10번: 입력값이 비어있는지 확인
  • 11번: 오류 메시지를 사용자에게 표시
  • 12번: 오류가 있는 필드로 포커스 이동
  • 13번: 이메일에 @가 있는지 간단히 확인
  • 14번: 모든 검사 통과 시 실제 제출 진행
  • 15번: 비밀번호 필드
  • 16번: 비밀번호 규칙을 미리 안내

1.3.4. 3.4 오류 식별 및 제안 (Error Identification)

개념: 오류를 명확하게 표시하고 수정 방법을 제안합니다.

1.3.4.1. 예제 코드

1
<form id="frm">
2
<!-- 1. 각 입력 필드에 고유 id 부여 -->
3
<div>
4
<label for="user">사용자명</label>
5
<!-- 2. aria-invalid로 오류 상태 표시 -->
6
<input type="text"
7
id="user"
8
aria-invalid="false"
9
aria-describedby="user-err">
10
<!-- 3. 오류 메시지 영역 (처음엔 숨김) -->
11
<span id="user-err"
12
role="alert"
13
style="display:none; color:red">
14
</span>
15
</div>
16
17
<div>
18
<label for="age">나이</label>
19
<input type="number"
20
id="age"
21
aria-invalid="false"
22
aria-describedby="age-err">
23
<span id="age-err"
24
role="alert"
25
style="display:none; color:red">
26
</span>
27
</div>
28
29
<button type="submit">제출</button>
30
</form>
31
32
<script>
33
// 4. 폼 제출 이벤트 처리
34
document.getElementById('frm').addEventListener('submit', function(e) {
35
// 5. 기본 제출 막기
36
e.preventDefault();
37
38
// 6. 오류 여부 추적
39
let hasError = false;
40
41
// 7. 사용자명 검증
42
const user = document.getElementById('user');
43
const userErr = document.getElementById('user-err');
44
45
// 8. 빈 값 체크
46
if (!user.value.trim()) {
47
// 9. aria-invalid를 true로 변경
48
user.setAttribute('aria-invalid', 'true');
49
// 10. 오류 메시지 표시
50
userErr.textContent = '사용자명을 입력해주세요';
51
userErr.style.display = 'block';
52
hasError = true;
53
} else {
54
// 11. 정상이면 aria-invalid를 false로
55
user.setAttribute('aria-invalid', 'false');
56
userErr.style.display = 'none';
57
}
58
59
// 12. 나이 검증
60
const age = document.getElementById('age');
61
const ageErr = document.getElementById('age-err');
62
63
// 13. 나이 범위 체크
64
if (age.value < 1 || age.value > 120) {
65
age.setAttribute('aria-invalid', 'true');
66
// 14. 구체적인 수정 방법 제시
67
ageErr.textContent = '나이는 1~120 사이로 입력해주세요';
68
ageErr.style.display = 'block';
69
hasError = true;
70
} else {
71
age.setAttribute('aria-invalid', 'false');
72
ageErr.style.display = 'none';
73
}
74
75
// 15. 오류가 없으면 제출
76
if (!hasError) {
77
alert('제출 완료');
78
} else {
79
// 16. 오류가 있으면 첫 오류 필드로 포커스
80
if (user.getAttribute('aria-invalid') === 'true') {
81
user.focus();
82
} else {
83
age.focus();
84
}
85
}
86
});
87
</script>

설명:

  • 1번: 각 필드에 고유한 id를 부여하여 구분
  • 2번: aria-invalid로 현재 입력값의 오류 여부 표시
  • 3번: role=“alert”로 오류 메시지가 즉시 읽히도록 함
  • 4번: 폼 제출 시 검증 로직 실행
  • 5번: 검증 전에 제출되지 않도록 preventDefault
  • 6번: 오류 발생 여부를 추적하는 변수
  • 7번: 사용자명 필드와 오류 메시지 요소 가져오기
  • 8번: 입력값이 비어있는지 확인 (공백 제거 후)
  • 9번: 오류가 있으면 aria-invalid를 true로 설정
  • 10번: 화면에 오류 메시지 표시
  • 11번: 정상이면 aria-invalid를 false로 돌림
  • 12번: 나이 필드 검증 시작
  • 13번: 나이가 유효한 범위인지 확인
  • 14번: 어떻게 수정해야 하는지 구체적으로 안내
  • 15번: 모든 필드가 정상이면 제출 진행
  • 16번: 오류가 있으면 첫 번째 오류 필드로 포커스 이동

1.4. 요약

웹 접근성의 세 가지 핵심 원칙:

  1. 인식의 용이성: 모든 사용자가 콘텐츠를 인식할 수 있어야 합니다

    • 대체 텍스트 제공
    • 충분한 색상 대비
    • 자막 제공
    • 명확한 구조
  2. 운용의 용이성: 모든 사용자가 인터페이스를 사용할 수 있어야 합니다

    • 키보드 접근성
    • 충분한 시간 제공
    • 발작 예방
    • 명확한 네비게이션
  3. 이해의 용이성: 콘텐츠와 인터페이스가 이해하기 쉬워야 합니다

    • 읽기 쉬운 텍스트
    • 예측 가능한 동작
    • 입력 지원
    • 오류 식별 및 제안