웹접근성
1. 웹 접근성 교안
섹션 제목: “1. 웹 접근성 교안”1.1. 인식의 용이성 (Perceivability)
섹션 제목: “1.1. 인식의 용이성 (Perceivability)”1.1.1. 1.1 대체 텍스트 (Alternative Text)
섹션 제목: “1.1.1. 1.1 대체 텍스트 (Alternative Text)”개념: 이미지나 비텍스트 콘텐츠를 볼 수 없는 사용자를 위해 텍스트 설명을 제공합니다.
1.1.1.1. 예제 코드
섹션 제목: “1.1.1.1. 예제 코드”<!-- 1. img 태그에 alt 속성으로 이미지 설명 제공 --><img src="cat.jpg" alt="흰색 고양이가 햇빛 아래 낮잠을 자고 있습니다">
<!-- 2. 장식용 이미지는 빈 alt 사용 (스크린리더가 건너뜀) --><img src="line.png" alt="">
<!-- 3. 의미있는 배경 이미지는 aria-label로 설명 --><div style="background-image: url('logo.png')" role="img" aria-label="회사 로고"></div>
<!-- 4. 아이콘 버튼에 텍스트 설명 제공 --><button aria-label="검색"> <img src="search.png" alt=""></button>설명:
- 1번: 의미있는 이미지는 내용을 설명하는 alt 제공
- 2번: 장식만을 위한 이미지는 alt를 비워둠
- 3번: CSS 배경 이미지도 role과 aria-label로 설명 가능
- 4번: 아이콘 버튼은 버튼의 기능을 aria-label로 설명
1.1.2. 1.2 색상 대비 (Color Contrast)
섹션 제목: “1.1.2. 1.2 색상 대비 (Color Contrast)”개념: 텍스트와 배경 사이에 충분한 명도 대비를 제공하여 저시력 사용자도 내용을 읽을 수 있게 합니다.
1.1.2.1. 기준
섹션 제목: “1.1.2.1. 기준”- 일반 텍스트: 최소 4.5:1 대비율
- 큰 텍스트 (18pt 이상): 최소 3:1 대비율
1.1.2.2. 예제 코드
섹션 제목: “1.1.2.2. 예제 코드”<!-- 1. 좋은 예: 검정 텍스트 + 흰색 배경 (대비율 21:1) --><p style="color: #000; background: #fff"> 읽기 쉬운 텍스트입니다</p>
<!-- 2. 나쁜 예: 회색 텍스트 + 흰색 배경 (대비율 2.5:1) --><p style="color: #999; background: #fff"> 읽기 어려운 텍스트입니다</p>
<!-- 3. 좋은 예: 진한 파랑 + 흰색 배경 (대비율 8.6:1) --><p style="color: #0056b3; background: #fff"> 충분한 대비가 있는 텍스트입니다</p>
<!-- 4. 큰 텍스트는 대비 기준이 낮음 --><h1 style="font-size: 24pt; color: #666; background: #fff"> 큰 제목 텍스트</h1>설명:
- 1번: 검정과 흰색은 최고의 대비율 제공
- 2번: 연한 회색은 대비가 부족하여 접근성 기준 미달
- 3번: 진한 색상 사용으로 충분한 대비 확보
- 4번: 큰 텍스트는 3:1 대비율만 필요
1.1.3. 1.3 자막 및 수화 (Captions and Sign Language)
섹션 제목: “1.1.3. 1.3 자막 및 수화 (Captions and Sign Language)”개념: 청각 장애인을 위해 오디오 콘텐츠에 자막을 제공합니다.
1.1.3.1. 예제 코드
섹션 제목: “1.1.3.1. 예제 코드”<!-- 1. video 태그에 track 요소로 자막 파일 연결 --><video controls> <!-- 2. src 속성에 비디오 파일 경로 지정 --> <source src="video.mp4" type="video/mp4">
<!-- 3. track 태그로 자막 파일 추가 (WebVTT 형식) --> <track kind="captions" src="captions-ko.vtt" srclang="ko" label="한국어">
<!-- 4. 영어 자막도 추가 가능 --> <track kind="captions" src="captions-en.vtt" srclang="en" label="English"></video>
<!-- 5. 오디오에도 동일하게 적용 --><audio controls> <source src="podcast.mp3" type="audio/mp3"> <track kind="captions" src="transcript.vtt" srclang="ko" label="한국어 자막"></audio>설명:
- 1번: video 태그에 controls 속성으로 재생 컨트롤 표시
- 2번: source로 비디오 파일과 MIME 타입 지정
- 3번: track으로 자막 파일 연결 (kind=“captions”)
- 4번: 여러 언어 자막 제공 가능
- 5번: 오디오도 동일한 방식으로 자막 제공
1.1.4. 1.4 콘텐츠 구조화 (Content Structure)
섹션 제목: “1.1.4. 1.4 콘텐츠 구조화 (Content Structure)”개념: 올바른 HTML 구조로 콘텐츠의 위계와 의미를 명확하게 전달합니다.
1.1.4.1. 예제 코드
섹션 제목: “1.1.4.1. 예제 코드”<!-- 1. 페이지에는 하나의 h1 제목만 사용 --><h1>웹 접근성 가이드</h1>
<!-- 2. 제목은 순서대로 사용 (h1 → h2 → h3) --><h2>인식의 용이성</h2>
<!-- 3. h2 다음에 h3 사용 (h4로 건너뛰지 않음) --><h3>대체 텍스트</h3>
<p>이미지에 대한 설명입니다.</p>
<!-- 4. 같은 레벨의 제목은 같은 숫자 사용 --><h3>색상 대비</h3>
<p>색상 대비에 대한 설명입니다.</p>
<!-- 5. 다음 섹션으로 넘어갈 때 h2 사용 --><h2>운용의 용이성</h2>
<!-- 6. 목록은 ul, ol 태그 사용 --><ul> <li>첫 번째 항목</li> <li>두 번째 항목</li></ul>
<!-- 7. 순서가 중요한 목록은 ol 사용 --><ol> <li>로그인하기</li> <li>상품 선택하기</li> <li>결제하기</li></ol>설명:
- 1번: 페이지당 하나의 h1으로 메인 제목 표시
- 2번: h1 다음에 h2로 하위 섹션 시작
- 3번: 제목 레벨을 순서대로 사용 (건너뛰지 않음)
- 4번: 같은 중요도의 내용은 같은 제목 레벨 사용
- 5번: 새로운 주요 섹션은 h2로 시작
- 6번: 순서가 없는 목록은 ul 사용
- 7번: 순서가 중요한 목록은 ol 사용
1.2. 운용의 용이성 (Operability)
섹션 제목: “1.2. 운용의 용이성 (Operability)”1.2.1. 2.1 키보드 접근성 (Keyboard Accessibility)
섹션 제목: “1.2.1. 2.1 키보드 접근성 (Keyboard Accessibility)”개념: 마우스 없이 키보드만으로 모든 기능을 사용할 수 있어야 합니다.
1.2.1.1. 예제 코드
섹션 제목: “1.2.1.1. 예제 코드”<!-- 1. button 태그는 기본적으로 키보드 접근 가능 --><button onclick="save()">저장</button>
<!-- 2. 나쁜 예: div는 키보드로 접근 불가 --><div onclick="save()">저장</div>
<!-- 3. 좋은 예: div에 tabindex와 role 추가 --><div tabindex="0" role="button" onclick="save()" onkeypress="if(event.key==='Enter') save()"> 저장</div>
<!-- 4. 링크는 기본적으로 Tab키로 이동 가능 --><a href="page.html">다음 페이지</a>
<!-- 5. 폼 요소는 모두 키보드 접근 가능 --><input type="text" placeholder="이름 입력"><select> <option>옵션 1</option> <option>옵션 2</option></select>
<!-- 6. tabindex로 탭 순서 제어 (양수는 피할 것) --><input tabindex="1" type="text"><input tabindex="2" type="text">
<!-- 7. tabindex="-1"은 포커스는 가능하지만 탭 순서에서 제외 --><div tabindex="-1" id="msg">알림 메시지</div>
<!-- 8. 모달 창에서는 포커스 트랩 구현 --><div role="dialog" aria-modal="true"> <h2>알림</h2> <p>내용입니다</p> <button>확인</button></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. 2.2 충분한 시간 제공 (Enough Time)”개념: 사용자가 콘텐츠를 읽고 사용할 충분한 시간을 제공합니다.
1.2.2.1. 예제 코드
섹션 제목: “1.2.2.1. 예제 코드”<!-- 1. 자동으로 넘어가는 슬라이드에 일시정지 버튼 제공 --><div id="slide"> <img src="slide1.jpg" alt="첫 번째 슬라이드"> <!-- 2. 일시정지, 재생 버튼 --> <button onclick="pause()">일시정지</button> <button onclick="play()">재생</button></div>
<script>// 3. 자동 슬라이드 변수let timer;let paused = false;
// 4. 슬라이드 자동 전환 함수function autoSlide() { // 5. 일시정지 상태가 아닐 때만 실행 if (!paused) { // 슬라이드 전환 코드 } // 6. 5초마다 실행 (충분한 시간 제공) timer = setTimeout(autoSlide, 5000);}
// 7. 일시정지 함수function pause() { paused = true; clearTimeout(timer);}
// 8. 재생 함수function play() { paused = false; autoSlide();}</script>
<!-- 9. 세션 타임아웃 경고 --><div id="warn" style="display:none"> <!-- 10. 남은 시간 표시 --> <p>5분 후 자동 로그아웃됩니다.</p> <!-- 11. 연장 버튼 제공 --> <button onclick="extend()">시간 연장</button></div>
<script>// 12. 타임아웃 전 경고 표시setTimeout(function() { document.getElementById('warn').style.display = 'block';}, 25 * 60 * 1000); // 25분 후 경고</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. 2.3 발작 예방 (Seizure Prevention)”개념: 깜빡이거나 번쩍이는 콘텐츠로 인한 발작을 예방합니다.
1.2.3.1. 예제 코드
섹션 제목: “1.2.3.1. 예제 코드”<!-- 1. 나쁜 예: 빠르게 깜빡이는 애니메이션 --><div style="animation: blink 0.3s infinite"> 깜빡임</div>
<style>/* 2. 초당 3회 이상 깜빡임은 위험 */@keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; }}</style>
<!-- 3. 좋은 예: 애니메이션 제어 옵션 제공 --><button onclick="stop()">애니메이션 중지</button>
<div id="anim"> 움직이는 콘텐츠</div>
<script>// 4. 애니메이션 중지 함수function stop() { // 5. CSS 애니메이션 제거 document.getElementById('anim').style.animation = 'none';}</script>
<!-- 6. prefers-reduced-motion으로 사용자 설정 존중 --><style>/* 7. 기본 애니메이션 */.box { transition: transform 0.3s;}
/* 8. 사용자가 애니메이션 줄이기를 선택한 경우 */@media (prefers-reduced-motion: reduce) { .box { /* 9. 모든 애니메이션과 전환 효과 제거 */ transition: none; animation: none; }}</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. 2.4 네비게이션 (Navigation)”개념: 사용자가 콘텐츠를 쉽게 탐색할 수 있도록 명확한 네비게이션을 제공합니다.
1.2.4.1. 예제 코드
섹션 제목: “1.2.4.1. 예제 코드”<!-- 1. 본문 바로가기 링크 (페이지 최상단) --><a href="#main" class="skip">본문 바로가기</a>
<!-- 2. 메인 네비게이션을 nav 태그로 감싸기 --><nav aria-label="주 메뉴"> <!-- 3. 목록으로 메뉴 구조화 --> <ul> <li><a href="/">홈</a></li> <li><a href="/about">소개</a></li> <li><a href="/contact">연락처</a></li> </ul></nav>
<!-- 4. 현재 페이지 표시 --><nav aria-label="주 메뉴"> <ul> <li><a href="/">홈</a></li> <!-- 5. aria-current로 현재 페이지 표시 --> <li><a href="/about" aria-current="page">소개</a></li> <li><a href="/contact">연락처</a></li> </ul></nav>
<!-- 6. 빵 부스러기(breadcrumb) 네비게이션 --><nav aria-label="빵부스러기"> <!-- 7. 순서가 있는 목록 사용 --> <ol> <li><a href="/">홈</a></li> <li><a href="/products">상품</a></li> <!-- 8. 현재 위치는 링크 없이 표시 --> <li aria-current="page">노트북</li> </ol></nav>
<!-- 9. 본문 영역은 main 태그로 표시 --><main id="main"> <h1>페이지 제목</h1> <p>본문 내용입니다.</p></main>
<!-- 10. 페이지 내 점프 링크 --><nav aria-label="목차"> <ul> <!-- 11. 각 섹션의 id로 이동 --> <li><a href="#sec1">섹션 1</a></li> <li><a href="#sec2">섹션 2</a></li> <li><a href="#sec3">섹션 3</a></li> </ul></nav>
<!-- 12. 각 섹션에 id 부여 --><section id="sec1"> <h2>섹션 1</h2></section>
<section id="sec2"> <h2>섹션 2</h2></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. 이해의 용이성 (Understandability)”1.3.1. 3.1 읽기 쉬운 텍스트 (Readable Text)
섹션 제목: “1.3.1. 3.1 읽기 쉬운 텍스트 (Readable Text)”개념: 명확하고 이해하기 쉬운 언어를 사용합니다.
1.3.1.1. 예제 코드
섹션 제목: “1.3.1.1. 예제 코드”<!-- 1. 페이지 언어 지정 (한국어) --><html lang="ko">
<!-- 2. 특정 부분만 다른 언어일 경우 --><p> <!-- 3. 한국어 문장 --> 안녕하세요. <!-- 4. 영어 부분만 lang 속성 지정 --> <span lang="en">Hello</span>입니다.</p>
<!-- 5. 약어나 줄임말에 설명 제공 --><p> <!-- 6. abbr 태그로 약어 표시 --> <abbr title="하이퍼텍스트 마크업 언어">HTML</abbr>은 웹 페이지를 만드는 언어입니다.</p>
<!-- 7. 복잡한 단어에 설명 추가 --><p> <!-- 8. dfn 태그로 정의 표시 --> <dfn title="웹 페이지를 모두가 사용할 수 있게 만드는 것"> 웹 접근성 </dfn>은 중요합니다.</p>
<!-- 9. 발음이 어려운 단어에 루비 주석 --><ruby> <!-- 10. 한자 표시 --> 漢字 <!-- 11. 읽는 방법 표시 --> <rt>한자</rt></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. 3.2 예측 가능성 (Predictability)”개념: 웹 페이지가 예측 가능한 방식으로 작동해야 합니다.
1.3.2.1. 예제 코드
섹션 제목: “1.3.2.1. 예제 코드”<!-- 1. 나쁜 예: 포커스만으로 팝업 열림 --><input type="text" onfocus="openPopup()">
<!-- 2. 좋은 예: 명시적 클릭으로 팝업 열림 --><input type="text" id="inp"><button onclick="openPopup()">도움말 보기</button>
<!-- 3. 나쁜 예: 선택만으로 페이지 이동 --><select onchange="location.href=this.value"> <option value="page1.html">페이지 1</option> <option value="page2.html">페이지 2</option></select>
<!-- 4. 좋은 예: 버튼으로 이동 확인 --><select id="sel"> <option value="page1.html">페이지 1</option> <option value="page2.html">페이지 2</option></select><!-- 5. 이동 버튼 별도 제공 --><button onclick="go()">이동</button>
<script>// 6. 사용자가 버튼을 눌러야 이동function go() { const sel = document.getElementById('sel'); location.href = sel.value;}</script>
<!-- 7. 새 창 열림을 사전에 알림 --><a href="page.html" target="_blank"> <!-- 8. 새 창에서 열림을 명시 --> 외부 사이트 (새 창)</a>
<!-- 9. 또는 aria-label로 설명 --><a href="page.html" target="_blank" aria-label="외부 사이트, 새 창에서 열림"> 외부 사이트</a>설명:
- 1번: 포커스만으로 팝업이 열리면 예측 불가
- 2번: 별도 버튼으로 사용자가 의도적으로 실행
- 3번: select 변경만으로 페이지 이동은 혼란스러움
- 4번: select는 선택만 함
- 5번: 이동 버튼을 따로 제공하여 명확한 의도 표현
- 6번: 버튼 클릭 시에만 페이지 이동 실행
- 7번: 새 창으로 열릴 때 텍스트로 명시
- 8번: 괄호 안에 “새 창” 표시
- 9번: 또는 aria-label로 스크린리더 사용자에게 알림
1.3.3. 3.3 입력 지원 (Input Assistance)
섹션 제목: “1.3.3. 3.3 입력 지원 (Input Assistance)”개념: 사용자가 오류를 방지하고 수정할 수 있도록 도와줍니다.
1.3.3.1. 예제 코드
섹션 제목: “1.3.3.1. 예제 코드”<!-- 1. 폼에 명확한 레이블 제공 --><form> <!-- 2. label과 input을 for-id로 연결 --> <label for="name">이름</label> <input type="text" id="name" required>
<!-- 3. 필수 입력 표시와 설명 --> <label for="email"> 이메일 <!-- 4. 필수 표시 --> <span aria-label="필수">*</span> </label> <!-- 5. 입력 형식 안내 --> <input type="email" id="email" placeholder="example@email.com" required aria-describedby="tip"> <!-- 6. 추가 설명 제공 --> <small id="tip">이메일 형식으로 입력해주세요</small>
<!-- 7. 오류 메시지 영역 --> <div id="err" role="alert" style="display:none; color:red"> </div>
<button type="submit">제출</button></form>
<script>// 8. 폼 제출 시 유효성 검사document.querySelector('form').addEventListener('submit', function(e) { // 9. 기본 제출 동작 막기 e.preventDefault();
const name = document.getElementById('name').value; const email = document.getElementById('email').value; const err = document.getElementById('err');
// 10. 이름이 비어있는지 확인 if (!name) { // 11. 오류 메시지 표시 err.textContent = '이름을 입력해주세요'; err.style.display = 'block'; // 12. 해당 필드로 포커스 이동 document.getElementById('name').focus(); return; }
// 13. 이메일 형식 확인 if (!email.includes('@')) { err.textContent = '올바른 이메일 형식이 아닙니다'; err.style.display = 'block'; document.getElementById('email').focus(); return; }
// 14. 모두 정상이면 제출 alert('제출 완료');});</script>
<!-- 15. 비밀번호 입력 시 조건 표시 --><label for="pw">비밀번호</label><input type="password" id="pw" aria-describedby="pwrule"><!-- 16. 비밀번호 규칙 설명 --><ul id="pwrule"> <li>8자 이상</li> <li>영문, 숫자 포함</li></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. 3.4 오류 식별 및 제안 (Error Identification)”개념: 오류를 명확하게 표시하고 수정 방법을 제안합니다.
1.3.4.1. 예제 코드
섹션 제목: “1.3.4.1. 예제 코드”<form id="frm"> <!-- 1. 각 입력 필드에 고유 id 부여 --> <div> <label for="user">사용자명</label> <!-- 2. aria-invalid로 오류 상태 표시 --> <input type="text" id="user" aria-invalid="false" aria-describedby="user-err"> <!-- 3. 오류 메시지 영역 (처음엔 숨김) --> <span id="user-err" role="alert" style="display:none; color:red"> </span> </div>
<div> <label for="age">나이</label> <input type="number" id="age" aria-invalid="false" aria-describedby="age-err"> <span id="age-err" role="alert" style="display:none; color:red"> </span> </div>
<button type="submit">제출</button></form>
<script>// 4. 폼 제출 이벤트 처리document.getElementById('frm').addEventListener('submit', function(e) { // 5. 기본 제출 막기 e.preventDefault();
// 6. 오류 여부 추적 let hasError = false;
// 7. 사용자명 검증 const user = document.getElementById('user'); const userErr = document.getElementById('user-err');
// 8. 빈 값 체크 if (!user.value.trim()) { // 9. aria-invalid를 true로 변경 user.setAttribute('aria-invalid', 'true'); // 10. 오류 메시지 표시 userErr.textContent = '사용자명을 입력해주세요'; userErr.style.display = 'block'; hasError = true; } else { // 11. 정상이면 aria-invalid를 false로 user.setAttribute('aria-invalid', 'false'); userErr.style.display = 'none'; }
// 12. 나이 검증 const age = document.getElementById('age'); const ageErr = document.getElementById('age-err');
// 13. 나이 범위 체크 if (age.value < 1 || age.value > 120) { age.setAttribute('aria-invalid', 'true'); // 14. 구체적인 수정 방법 제시 ageErr.textContent = '나이는 1~120 사이로 입력해주세요'; ageErr.style.display = 'block'; hasError = true; } else { age.setAttribute('aria-invalid', 'false'); ageErr.style.display = 'none'; }
// 15. 오류가 없으면 제출 if (!hasError) { alert('제출 완료'); } else { // 16. 오류가 있으면 첫 오류 필드로 포커스 if (user.getAttribute('aria-invalid') === 'true') { user.focus(); } else { age.focus(); } }});</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.4. 요약”웹 접근성의 세 가지 핵심 원칙:
-
인식의 용이성: 모든 사용자가 콘텐츠를 인식할 수 있어야 합니다
- 대체 텍스트 제공
- 충분한 색상 대비
- 자막 제공
- 명확한 구조
-
운용의 용이성: 모든 사용자가 인터페이스를 사용할 수 있어야 합니다
- 키보드 접근성
- 충분한 시간 제공
- 발작 예방
- 명확한 네비게이션
-
이해의 용이성: 콘텐츠와 인터페이스가 이해하기 쉬워야 합니다
- 읽기 쉬운 텍스트
- 예측 가능한 동작
- 입력 지원
- 오류 식별 및 제안