모든 사용자가 화면을 문제없이 이용하려면 태그 선택, 포커스 이동, 보조 기술용 힌트를 세심하게 다뤄야 합니다. 이번 글은 개념 → 코드 → 이유 순서로 접근성을 정리합니다.
이번 글에서 새로 나오는 용어
- 시맨틱 HTML: 태그 이름만으로 역할이 드러나도록
header,nav,section등을 적재적소에 쓰는 마크업 방식입니다. - ARIA 속성: 시맨틱 정보가 부족할 때
aria-label,aria-live처럼 보조기기에 추가 설명을 주는 속성 세트입니다. - 포커스 트랩: 모달 안에서 Tab 이동이 밖으로 새 나가지 않게 잡아 두는 제어 기법입니다.
- role 속성: 요소의 역할을 명시적으로 지정해 스크린 리더가 의미를 정확히 전달하게 하는 속성입니다.
핵심 개념
- 시맨틱 HTML(Semantic HTML, 의미가 담긴 태그 구조): 태그 이름만으로 의미를 전달합니다.
section,nav,button,label이 여기에 속합니다. - ARIA 속성(Accessible Rich Internet Applications, 의미 확장 속성): 시맨틱 정보가 부족할 때
aria-live,aria-expanded,aria-controls같은 속성으로 추가 컨텍스트를 제공합니다. - 포커스 흐름(Focus Flow, Tab 이동 순서): 키보드 사용자가
Tab키로 이동하는 순서를 뜻합니다.tabindex와focus()로 순서를 설계합니다. - 라이브 영역(Live Region, 자동 읽기 영역):
aria-live="polite"같은 속성을 붙여 상태 변화를 보조기기가 읽도록 만듭니다. 로딩, 성공, 오류 메시지에 사용합니다.
코드로 확인하기
먼저 시맨틱 태그와 내비게이션 라벨을 붙여 화면 구조를 또렷하게 만듭니다.
<header>
<h1>학생 일정 대시보드</h1>
<nav aria-label="페이지 섹션 이동">
<a href="#tasks">작업</a>
<a href="#stats">통계</a>
</nav>
</header>
<section id="tasks" aria-labelledby="tasks-heading">
<h2 id="tasks-heading">작업 목록</h2>
<ul role="list" class="task-list"></ul>
</section>
aria-label과 aria-labelledby를 써서 제목과 역할을 명확히 지정하면 스크린 리더가 구조를 빠르게 안내합니다.
이제 모달 포커스를 제어해 사용자가 길을 잃지 않도록 합니다.
const addButton = document.querySelector(".task-add");
const modal = document.querySelector("#task-modal");
const modalClose = modal?.querySelector(".modal-close");
function openModal() {
modal?.classList.add("is-open");
modal?.setAttribute("aria-hidden", "false");
modal?.setAttribute("aria-modal", "true");
modal?.querySelector("input")?.focus();
}
function closeModal() {
modal?.classList.remove("is-open");
modal?.setAttribute("aria-hidden", "true");
addButton?.focus();
}
addButton?.addEventListener("click", openModal);
modalClose?.addEventListener("click", closeModal);
모달을 열 때 포커스를 내부로 보내고, 닫을 때 트리거 버튼으로 되돌리면 키보드 사용자가 길을 잃지 않습니다.
상태 메시지용 라이브 영역을 만들어 실시간 피드백을 제공합니다.
<div class="form-feedback" role="status" aria-live="polite"></div>
const feedback = document.querySelector(".form-feedback");
function showFeedback(message, type = "info") {
if (!feedback) return;
feedback.textContent = message;
feedback.className = `form-feedback is-${type}`;
}
form?.addEventListener("submit", (event) => {
event.preventDefault();
showFeedback("저장 중입니다...", "info");
saveTask().then(() => showFeedback("저장 완료!", "success"));
});
role="status"와 aria-live="polite"를 붙이면 보조기기가 상태 메시지를 자동으로 읽어 줍니다.
끝으로 포커스 스타일을 정의해 시각 힌트를 제공합니다.
:focus-visible {
outline: 3px solid #1ba784;
outline-offset: 4px;
}
[tabindex="-1"]:focus {
outline: none;
}
명확한 포커스 스타일을 적용하면 키보드 사용자도 현재 위치를 쉽게 확인할 수 있습니다.
왜 중요한가
- 학교 프로젝트나 공모전에서도 접근성 요구 사항을 명시하는 경우가 많습니다. 기본기를 갖추면 심사에서 좋은 평가를 받을 수 있습니다.
- 시맨틱 구조와 ARIA 속성을 챙기면 SEO(검색 노출) 품질도 함께 올라갑니다.
- 명확한 포커스 흐름은 장애가 없는 사용자에게도 생산성을 제공합니다. 키보드만으로 조작할 수 있으면 테스트 속도가 빨라집니다.
실습
- 따라 하기: 헤더, 메인, 섹션, 버튼, 폼 등에 시맨틱 태그를 적용하고
aria-label,aria-labelledby를 붙입니다. - 확장하기: 모달을 만들고 열기/닫기 시 포커스를 이동시키며
aria-hidden,aria-modal속성을 토글합니다. - 디버깅: Tab 키만으로 UI를 탐색해 보고, 포커스가 갇히거나 사라지는 구간을 찾아
tabindex와focus()로 수정합니다. - 완료 기준: 스크린 리더(예: VoiceOver 또는 NVDA)로 주요 요소의 이름과 상태가 모두 읽히고, 키보드만으로 작업 흐름을 수행할 수 있으면 실습이 끝납니다.
마무리
접근성은 “나중에 덧붙이는 기능”이 아니라 처음부터 설계에 포함해야 할 기본값입니다. 다음 글에서는 재사용 가능한 UI 패턴을 정리하며 이러한 접근성 원칙을 어떻게 유지할지 살펴보겠습니다.
💬 댓글
이 글에 대한 의견을 남겨주세요