FEInterview Prep

browser

웹 접근성(a11y) — 모든 사용자를 위한 웹

웹 접근성은 장애인만을 위한 것이 아닙니다. 스크린리더 사용자, 키보드 전용 사용자, 저시력, 색각 이상, 일시적 장애(팔 골절), 열악한 네트워크 환경 모두를 포함합니다. 접근성이 좋은 코드는 SEO도 좋고, 유지보수도 쉽습니다. 토스, 카카오, 네이버 같은 대기업은 접근성 법적 요건(장애인차별금지법)을 준수해야 하며 면접에서 "aria-label을 언제 사용하나요?" 같은 질문이 실제로 나옵니다.

시작 전
이해도
매우 낮음

학습 개요

탄생 배경

해결하려 했던 문제

시각 장애인은 스크린리더로 웹을 사용합니다. div로만 만든 버튼은 스크린리더가 "클릭 가능한 요소"로 인식하지 못합니다. 비시맨틱 HTML(div soup)은 스크린리더 사용자에게 아무런 정보도 전달하지 못합니다. 키보드로만 탐색하는 사용자는 포커스가 없는 커스텀 드롭다운을 사용할 수 없습니다. 전 세계 약 15%(10억 명)가 어떤 형태로든 장애를 가지고 있습니다.

역사적 맥락

W3C의 WAI(Web Accessibility Initiative)가 1997년 설립됐습니다. WCAG 1.0(1999), WCAG 2.0(2008), WCAG 2.1(2018), WCAG 2.2(2023)으로 발전했습니다. ARIA(Accessible Rich Internet Applications) 표준이 2008년 발표되어 동적 콘텐츠의 접근성을 보강했습니다. 한국은 2008년 장애인차별금지법 시행 후 공공 웹사이트 접근성 준수가 법적 의무가 됐습니다.

이전에는 어떻게 했나

시맨틱 HTML만으로도 많은 접근성 문제를 해결할 수 있습니다 — button, nav, main, header, footer, h1-h6, label 등을 올바르게 사용하면 ARIA 없이도 스크린리더가 이해합니다. ARIA는 네이티브 HTML로 표현할 수 없는 복잡한 위젯(탭, 아코디언, 슬라이더)에 보완적으로 사용합니다. "첫 번째 규칙: ARIA를 사용하지 않는 것이 최선"이라는 W3C 지침도 있습니다.

멘탈 모델

동작 원리

WCAG 2.1 원칙 (POUR): 1. 인식 가능(Perceivable): 사용자가 콘텐츠를 인식 가능해야 함 - 이미지에 alt 텍스트 - 색상 대비 비율 준수 (4.5:1 일반, 3:1 큰 텍스트) - 자막, 트랜스크립트 2. 운용 가능(Operable): 모든 기능이 키보드로 접근 가능 - 탭 포커스 순서가 논리적 - 포커스 스타일 가시화 - 키보드 트랩 방지 3. 이해 가능(Understandable): 콘텐츠와 UI가 이해 가능 - 명확한 에러 메시지 - 일관된 내비게이션 4. 견고함(Robust): 다양한 보조 기술과 호환 - 유효한 HTML - 올바른 ARIA 사용 ARIA 속성: - role: 요소의 역할 명시 (button, dialog, navigation, list) - aria-label: 보이지 않는 레이블 (아이콘 버튼) - aria-labelledby: 다른 요소를 레이블로 연결 - aria-describedby: 추가 설명 요소 연결 - aria-hidden="true": 스크린리더에서 숨김 - aria-expanded: 열림/닫힘 상태 - aria-live: 동적으로 변하는 콘텐츠 알림

핵심 구성 요소

시맨틱 HTML

의미 있는 태그 사용. 기본 접근성 보장의 가장 효과적인 방법

ARIA 속성

네이티브 HTML만으로 부족한 복잡한 위젯의 접근성 보강

키보드 내비게이션

Tab/Shift+Tab/Enter/Space/화살표키로 모든 기능 접근 가능

포커스 관리

모달 열릴 때 포커스 이동, 닫힐 때 원래 위치로 복귀

색상 대비

WCAG 기준 4.5:1 (일반 텍스트), 3:1 (18px+ 또는 14px+ 굵은 텍스트)

aria-live

스크린리더에게 동적 콘텐츠 변경 알림. polite(비방해) / assertive(즉시)

흐름 설명


[잘못된 vs 올바른 접근성 코드]

❌ 접근성 없는 코드:
<div onclick="handleClick()">삭제</div>
<div class="modal">...</div>
<img src="logo.png" />
<span style="color: red">오류 메시지</span>

✅ 접근성 있는 코드:
<button type="button" onclick="handleClick()">삭제</button>
// 네이티브 button: 키보드 접근, 클릭/엔터 지원, 스크린리더 인식

<div role="dialog" aria-modal="true" aria-labelledby="title">
  <h2 id="title">삭제 확인</h2>
</div>

<img src="logo.png" alt="우리회사 로고" />
// 장식용: alt=""

<p id="error-msg" role="alert">이메일 형식이 올바르지 않습니다</p>
// role="alert": 스크린리더가 즉시 읽음

[포커스 트랩 (모달)]
모달 열릴 때: 첫 번째 포커스 가능 요소로 포커스 이동
탭으로 순환: 모달 내부에서만 순환
Escape 키: 모달 닫고 원래 요소로 포커스 복귀
    

코드 예제


// ✅ React에서 접근성 있는 모달 컴포넌트
import { useEffect, useRef } from 'react';
import FocusTrap from 'focus-trap-react';

function Modal({ isOpen, onClose, title, children }) {
  const closeButtonRef = useRef(null);

  useEffect(() => {
    if (isOpen) {
      closeButtonRef.current?.focus(); // 모달 열릴 때 포커스 이동
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <FocusTrap>
      <div
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
        onKeyDown={(e) => e.key === 'Escape' && onClose()}
      >
        <h2 id="modal-title">{title}</h2>
        {children}
        <button ref={closeButtonRef} onClick={onClose}>
          닫기 <span aria-hidden="true">×</span>
        </button>
      </div>
    </FocusTrap>
  );
}

// ✅ 아이콘 버튼 접근성
<button aria-label="검색" type="button">
  <SearchIcon aria-hidden="true" /> {/* 아이콘 자체는 숨김 */}
</button>

// ✅ 동적 알림 (로딩 → 완료)
<div aria-live="polite" aria-atomic="true">
  {isLoading ? '저장 중...' : '저장 완료!'}
</div>

// ✅ 폼 레이블 연결
<label htmlFor="email">이메일 *</label>
<input
  id="email"
  type="email"
  aria-required="true"
  aria-invalid={!!errors.email}
  aria-describedby={errors.email ? 'email-error' : undefined}
/>
{errors.email && (
  <p id="email-error" role="alert">{errors.email}</p>
)}
    

비교 분석

vs

시맨틱 HTML vs ARIA

비교 관점
이 방식
시맨틱 HTML vs ARIA
우선순위
시맨틱 HTML: 항상 먼저 고려
ARIA: 네이티브로 불가능할 때 보완
브라우저 지원
시맨틱 HTML: 모든 브라우저/보조기술에서 신뢰성 높음
ARIA: 일부 스크린리더에서 불일치 가능
사용 사례
버튼, 폼, 내비게이션, 헤딩
탭패널, 아코디언, 슬라이더, 트리뷰
오용 위험
낮음
높음 — 잘못된 ARIA가 없는 것보다 나쁨

vs

수동 테스트 vs 자동화 도구

비교 관점
이 방식
수동 테스트 vs 자동화 도구
커버리지
수동: 실제 사용 흐름 검증
자동(axe, Lighthouse): 규칙 기반 검사
발견 비율
수동+스크린리더: 더 많은 문제 발견
자동: 접근성 문제의 ~30-40%만 발견
비용
시간 비용 큼
빠르고 CI에 통합 가능
권장
핵심 사용자 흐름에 대해 실제 스크린리더로 테스트
CI/CD에 axe-core 통합으로 기본 검사 자동화

트레이드오프

이상적인 사용 사례

면접 질문