[JavaScript 시리즈 13편] UI 코드에서 접근성과 시맨틱 HTML 의식하기

English version

모든 사용자가 화면을 문제없이 이용하려면 태그 선택, 포커스 이동, 보조 기술용 힌트를 세심하게 다뤄야 합니다. 이번 글은 개념 → 코드 → 이유 순서로 접근성을 정리합니다.

이번 글에서 새로 나오는 용어

  1. 시맨틱 HTML: 태그 이름만으로 역할이 드러나도록 header, nav, section 등을 적재적소에 쓰는 마크업 방식입니다.
  2. ARIA 속성: 시맨틱 정보가 부족할 때 aria-label, aria-live처럼 보조기기에 추가 설명을 주는 속성 세트입니다.
  3. 포커스 트랩: 모달 안에서 Tab 이동이 밖으로 새 나가지 않게 잡아 두는 제어 기법입니다.
  4. role 속성: 요소의 역할을 명시적으로 지정해 스크린 리더가 의미를 정확히 전달하게 하는 속성입니다.

핵심 개념

  • 시맨틱 HTML(Semantic HTML, 의미가 담긴 태그 구조): 태그 이름만으로 의미를 전달합니다. section, nav, button, label이 여기에 속합니다.
  • ARIA 속성(Accessible Rich Internet Applications, 의미 확장 속성): 시맨틱 정보가 부족할 때 aria-live, aria-expanded, aria-controls 같은 속성으로 추가 컨텍스트를 제공합니다.
  • 포커스 흐름(Focus Flow, Tab 이동 순서): 키보드 사용자가 Tab 키로 이동하는 순서를 뜻합니다. tabindexfocus()로 순서를 설계합니다.
  • 라이브 영역(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-labelaria-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를 탐색해 보고, 포커스가 갇히거나 사라지는 구간을 찾아 tabindexfocus()로 수정합니다.
  • 완료 기준: 스크린 리더(예: VoiceOver 또는 NVDA)로 주요 요소의 이름과 상태가 모두 읽히고, 키보드만으로 작업 흐름을 수행할 수 있으면 실습이 끝납니다.

마무리

접근성은 “나중에 덧붙이는 기능”이 아니라 처음부터 설계에 포함해야 할 기본값입니다. 다음 글에서는 재사용 가능한 UI 패턴을 정리하며 이러한 접근성 원칙을 어떻게 유지할지 살펴보겠습니다.

💬 댓글

이 글에 대한 의견을 남겨주세요