들어가며

ChatGPT, Claude, Gemini… 생성형 AI가 우리 일상 깊숙이 들어온 시대입니다. 웹사이트마다 AI 챗봇이 자리를 잡았고, 콘텐츠 자동 생성도 이제 낯설지 않죠. 이렇게 빠르게 변화하는 기술 환경 속에서, 한 가지 중요한 질문을 던져보고 싶습니다.

“AI가 만든 웹, 과연 모든 사람이 사용할 수 있을까요?”

웹접근성(Web Accessibility)이란 장애를 가진 분들, 어르신들, 느린 인터넷 환경의 사용자 등 모든 사람이 웹을 동등하게 이용할 수 있도록 보장하는 것입니다. AI 시대에도, 아니 AI 시대이기에 더욱 웹접근성은 중요해지고 있습니다.

이 글에서는 AI 기술의 발전이 웹접근성에 어떤 영향을 미치는지, 그리고 개발자로서 우리가 꼭 알아야 할 핵심 원칙들을 실전 예제와 함께 살펴보겠습니다.

AI 시대, 웹접근성이 더 중요해진 이유

1. AI 인터페이스가 점점 복잡해지고 있어요

AI 챗봇, 음성 인터페이스, 제스처 기반 UI… 새로운 방식의 사용자 경험이 계속 등장하면서 웹은 점점 더 복잡해지고 있습니다.

스마트폰 화면에 표시된 AI 채팅 비서 인터페이스 - 새로운 형태의 사용자 경험을 보여주는 예시
스마트폰 화면에 표시된 AI 채팅 비서 인터페이스 - 새로운 형태의 사용자 경험을 보여주는 예시
사진: Zulfugar Karimov / Unsplash
html
<!-- 잘못된 예: AI 챗봇 버튼 -->
<div class="chat-button" onclick="openChat()">
  <img src="chat-icon.png">
</div>

<!-- 올바른 예: 접근 가능한 AI 챗봇 버튼 -->
<button
  type="button"
  aria-label="AI 채팅 도우미 열기"
  aria-expanded="false"
  aria-controls="chat-widget"
  onclick="openChat()">
  <img src="chat-icon.png" alt="채팅 아이콘">
</button>

스크린 리더를 사용하는 분들은 <div>로 만든 버튼을 버튼으로 인식하지 못합니다. 시맨틱 HTML과 ARIA 속성을 함께 사용해야 모두가 사용할 수 있는 인터페이스가 됩니다.

2. AI가 만든 코드, 겉만 번지르르할 수 있어요

AI가 생성한 HTML은 시각적으로는 완벽해 보일 수 있습니다. 하지만 접근성 측면에서는 허점이 많은 경우가 많아요. AI는 이런 것들을 자주 놓칩니다:

  • 대체 텍스트(alt) 누락
  • 엉성한 헤딩 구조
  • 키보드 포커스 순서 무시
  • 색상 대비 부족
  • ARIA 속성 잘못 사용
html
<!-- AI가 만든 코드 (문제가 있어요) -->
<div class="card">
  <img src="product.jpg">
  <div class="title">제품명</div>
  <div class="price">29,900원</div>
  <div class="button" onclick="buy()">구매하기</div>
</div>

<!-- 접근성을 고려한 코드 -->
<article class="card">
  <img src="product.jpg" alt="무선 이어폰 블랙 색상">
  <h3>무선 이어폰</h3>
  <p><strong>가격:</strong> 29,900원</p>
  <button type="button" onclick="buy()">구매하기</button>
</article>

3. 자동화의 역설

AI는 웹 개발을 자동화할 수 있지만, 접근성 테스트는 완전히 자동화하기 어렵습니다.

자동화 도구(Lighthouse, axe 등)는 기술적인 오류는 잘 찾아내지만, 이런 문제들은 발견하지 못합니다:

  • 맥락에 맞지 않는 alt 텍스트
  • 혼란스러운 네비게이션 구조
  • 모호한 에러 메시지
  • 시각적으로만 전달되는 중요한 정보

결국 사람의 판단이 꼭 필요합니다.

웹접근성의 핵심 원칙: WCAG 2.2

W3C의 WCAG(Web Content Accessibility Guidelines) 2.2는 4가지 핵심 원칙을 제시합니다. 2023년 10월에 발표된 WCAG 2.2는 이전 버전(2.1)에 모바일 접근성과 저시력 사용자를 위한 기준을 추가로 강화했습니다.

1. 인식 가능성 (Perceivable)

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

대체 텍스트 제공하기

html
<!-- 나쁜 예 -->
<img src="chart.png">

<!-- 좋은 예 -->
<img
  src="chart.png"
  alt="2024년 월별 매출 추이: 1월 100만원부터 12월 300만원까지 지속 상승">

적절한 색상 대비 유지하기

css
/* 나쁜 예: 대비율 2.5:1 (WCAG AA 기준 미달) */
.text {
  color: #999999;
  background: #ffffff;
}

/* 좋은 예: 대비율 4.5:1 이상 (WCAG AA 통과) */
.text {
  color: #595959;
  background: #ffffff;
}

색상 대비율은 WebAIM Contrast Checker에서 확인할 수 있습니다.

멀티미디어에 자막 제공하기

html
<video controls>
  <source src="tutorial.mp4" type="video/mp4">
  <track
    kind="captions"
    src="captions-ko.vtt"
    srclang="ko"
    label="한국어 자막">
  <track
    kind="captions"
    src="captions-en.vtt"
    srclang="en"
    label="English Captions">
</video>

2. 운용 가능성 (Operable)

모든 기능을 마우스 없이 키보드만으로 조작할 수 있어야 합니다.

키보드 클로즈업 - 웹 접근성에서 키보드 네비게이션은 필수적인 요소입니다
키보드 클로즈업 - 웹 접근성에서 키보드 네비게이션은 필수적인 요소입니다
사진: BoliviaInteligente / Unsplash

키보드 포커스 잘 관리하기

javascript
// AI 모달 열기
function openModal() {
  const modal = document.getElementById('modal');
  const closeButton = modal.querySelector('.close-button');

  modal.style.display = 'block';
  modal.setAttribute('aria-hidden', 'false');

  // 모달이 열리면 첫 번째 포커스 가능한 요소로 포커스 이동
  closeButton.focus();

  // 모달 내부에서만 탭 이동 (포커스 트랩)
  trapFocus(modal);
}

// 포커스 트랩 함수
function trapFocus(element) {
  const focusableElements = element.querySelectorAll(
    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];

  element.addEventListener('keydown', function(e) {
    if (e.key !== 'Tab') return;

    if (e.shiftKey) {
      // Shift + Tab: 역방향 이동
      if (document.activeElement === firstElement) {
        lastElement.focus();
        e.preventDefault();
      }
    } else {
      // Tab: 정방향 이동
      if (document.activeElement === lastElement) {
        firstElement.focus();
        e.preventDefault();
      }
    }
  });
}

본문 바로가기 링크 만들기

html
<!-- 페이지 최상단에 배치 -->
<a href="#main-content" class="skip-link">
  본문으로 바로가기
</a>

<style>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  text-decoration: none;
  z-index: 9999;
}

.skip-link:focus {
  top: 0; /* 포커스 시 나타남 */
}
</style>

WCAG 2.2 신규: 포커스 표시 강화 (2.4.11, 2.4.13)

WCAG 2.2에서는 포커스 인디케이터의 최소 크기와 명확성을 규정하는 기준이 추가되었습니다.

css
/* 포커스 표시 최소 기준 충족 */
button:focus,
a:focus,
input:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
  /* 최소 2px 두께, 요소 경계에서 2px 떨어진 위치 */
}

/* 대비율 3:1 이상 확보 */
.dark-theme button:focus {
  outline-color: #4d94ff; /* 어두운 배경에서도 충분한 대비 */
}

3. 이해 가능성 (Understandable)

콘텐츠와 인터페이스가 명확하고 예측 가능해야 합니다.

명확한 에러 메시지 제공하기

html
<!-- 나쁜 예 -->
<form>
  <input type="email" required>
  <span class="error">잘못된 입력</span>
</form>

<!-- 좋은 예 -->
<form>
  <label for="email">이메일 주소</label>
  <input
    type="email"
    id="email"
    aria-describedby="email-error"
    aria-invalid="true"
    required>
  <span id="email-error" class="error" role="alert">
    올바른 이메일 형식이 아닙니다. 예: [email protected]
  </span>
</form>

WCAG 2.2 신규: 일관된 도움말 (3.2.6)

웹사이트 전반에 걸쳐 도움말이나 연락 방법을 일관된 위치에 제공해야 합니다.

html
<!-- 모든 페이지에서 동일한 위치에 도움말 배치 -->
<nav aria-label="주요 메뉴">
  <ul>
    <li><a href="/"></a></li>
    <li><a href="/about">소개</a></li>
    <li><a href="/services">서비스</a></li>
    <li><a href="/help">도움말</a></li> <!-- 항상 동일한 순서 -->
  </ul>
</nav>

4. 견고성 (Robust)

다양한 보조 기술과 호환되어야 합니다.

시맨틱 HTML 사용하기

html
<!-- 나쁜 예: div 남발 -->
<div class="header">
  <div class="nav">...</div>
</div>
<div class="main">
  <div class="article">...</div>
</div>
<div class="footer">...</div>

<!-- 좋은 예: 시맨틱 HTML -->
<header>
  <nav aria-label="주요 메뉴">...</nav>
</header>
<main>
  <article>...</article>
</main>
<footer>...</footer>

ARIA를 올바르게 사용하기

html
<!-- ARIA 사용 원칙: 시맨틱 HTML이 우선 -->

<!-- 나쁜 예: 불필요한 ARIA -->
<button role="button">클릭</button>

<!-- 좋은 예: 시맨틱 HTML만으로 충분 -->
<button>클릭</button>

<!-- ARIA가 필요한 경우: 커스텀 위젯 -->
<div
  role="tablist"
  aria-label="계정 설정">
  <button
    role="tab"
    aria-selected="true"
    aria-controls="panel-1"
    id="tab-1">
    프로필
  </button>
  <button
    role="tab"
    aria-selected="false"
    aria-controls="panel-2"
    id="tab-2">
    보안
  </button>
</div>

AI 시대 웹접근성 체크리스트

AI와 함께 웹을 개발할 때 이 체크리스트를 확인해보세요:

태블릿에 체크리스트를 작성하는 모습 - 웹접근성도 체계적인 점검이 필요합니다
태블릿에 체크리스트를 작성하는 모습 - 웹접근성도 체계적인 점검이 필요합니다
사진: Jakub Żerdzicki / Unsplash

HTML 구조

  • 모든 이미지에 의미있는 alt 텍스트 제공
  • 헤딩(<h1> ~ <h6>)이 논리적 순서로 구성됨
  • 시맨틱 HTML 사용 (<header>, <nav>, <main>, <article>, <footer>)
  • 폼 요소에 <label> 연결 (for/id 또는 중첩)
  • 링크 텍스트가 명확함 (“여기 클릭” 같은 모호한 표현 지양)

키보드 접근성

  • 모든 인터랙티브 요소가 키보드로 접근 가능
  • 포커스 순서가 논리적
  • 포커스 표시가 명확 (최소 2px, 대비율 3:1 이상 - WCAG 2.2)
  • 모달/드롭다운에서 포커스 트랩 구현
  • Esc 키로 닫기 기능 구현

ARIA 사용

  • role, aria-label, aria-labelledby 적절히 사용
  • 동적 콘텐츠 변경 시 aria-live 사용
  • 확장/축소 상태를 aria-expanded로 표시
  • 에러 메시지에 role="alert" 사용

시각적 디자인

  • 색상 대비율 WCAG AA 기준 충족 (4.5:1 이상)
  • 색상만으로 정보 전달하지 않음
  • 텍스트 크기 조정 가능 (최소 200%까지)
  • 포커스 인디케이터가 시각적으로 명확 (WCAG 2.2 강화)

멀티미디어

  • 비디오에 자막 제공
  • 오디오 콘텐츠에 대본 제공
  • 자동 재생 금지 또는 제어 옵션 제공

모바일 접근성 (WCAG 2.2 강화)

  • 터치 타겟 크기 최소 24x24px (2.5.8)
  • 드래그 동작에 대한 대체 방법 제공 (2.5.7)

테스트

  • 키보드만으로 전체 사이트 탐색 테스트
  • 스크린 리더(NVDA, JAWS, VoiceOver)로 테스트
  • 자동화 도구(Lighthouse, axe DevTools) 실행
  • 가능하다면 실제 사용자 테스트

AI를 활용한 웹접근성 개선

AI는 양날의 검입니다. 잘못 사용하면 접근성을 해치지만, 올바르게 활용하면 접근성을 크게 향상시킬 수 있어요.

1. AI로 대체 텍스트 생성하기

javascript
// OpenAI Vision API를 활용한 alt 텍스트 생성
async function generateAltText(imageUrl) {
  const response = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: [
      {
        role: "user",
        content: [
          {
            type: "text",
            text: "이 이미지를 시각장애인이 이해할 수 있도록 상세히 설명해주세요. 100자 이내로 작성하세요."
          },
          {
            type: "image_url",
            image_url: { url: imageUrl }
          }
        ]
      }
    ]
  });

  return response.choices[0].message.content;
}

// 사용 예시
const altText = await generateAltText("https://example.com/chart.png");
// 결과: "2024년 매출 추이를 보여주는 선 그래프. 1월 100만원에서 시작하여 12월 300만원까지 꾸준히 증가하는 추세를 나타냄"

주의할 점: AI가 생성한 alt 텍스트는 반드시 사람이 검토해야 합니다. 맥락을 놓치거나 중요한 정보를 빠뜨릴 수 있거든요.

2. 접근 가능한 AI 챗봇 만들기

html
<!-- 접근 가능한 AI 챗봇 위젯 -->
<div
  id="chat-widget"
  role="region"
  aria-label="AI 고객 지원 채팅"
  aria-live="polite">

  <div
    id="chat-messages"
    role="log"
    aria-live="polite"
    aria-atomic="false">
    <!-- 메시지가 여기에 추가됨 -->
  </div>

  <form onsubmit="sendMessage(event)">
    <label for="chat-input" class="visually-hidden">
      메시지 입력
    </label>
    <input
      type="text"
      id="chat-input"
      aria-describedby="chat-help"
      placeholder="메시지를 입력하세요">
    <span id="chat-help" class="visually-hidden">
      AI 봇과 대화할 수 있습니다. 엔터키를 눌러 전송하세요.
    </span>
    <button type="submit">전송</button>
  </form>
</div>

<style>
/* 시각적으로 숨기지만 스크린 리더는 읽을 수 있는 텍스트 */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
</style>

3. 스크린 리더를 위한 음성 안내

javascript
// Web Speech API를 활용한 음성 안내
function announceToScreenReader(message) {
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', 'polite');
  announcement.classList.add('visually-hidden');
  announcement.textContent = message;

  document.body.appendChild(announcement);

  // 1초 후 제거 (스크린 리더가 읽을 시간 확보)
  setTimeout(() => {
    document.body.removeChild(announcement);
  }, 1000);
}

// 사용 예시
announceToScreenReader('새 메시지가 도착했습니다.');

법적 요구사항과 비즈니스 가치

법적 의무

한국에서는 장애인차별금지법에 따라 다음 웹사이트는 웹접근성 준수가 의무입니다:

  • 공공기관 웹사이트
  • 일정 규모 이상의 법인 (매출액 또는 사업체 규모 기준)
  • 특수학교, 장애인 복지시설

위반 시 과태료 및 시정 명령을 받을 수 있습니다.

비즈니스 가치

웹접근성은 단순히 법적 의무만이 아닙니다. 실제 비즈니스 가치도 제공해요:

  1. 더 많은 사용자 확보: 전 세계 인구의 약 15%(약 10억 명)가 어떤 형태로든 장애를 가지고 있습니다.
  2. SEO 향상: 시맨틱 HTML과 명확한 구조는 검색 엔진 최적화에도 도움이 됩니다.
  3. 모바일 UX 개선: 터치 타겟 크기, 명확한 라벨 등은 모바일 사용성도 높여줍니다.
  4. 유지보수 용이: 잘 구조화된 코드는 수정과 확장이 훨씬 쉽습니다.
  5. 브랜드 이미지: 포용적인 브랜드 이미지를 구축할 수 있습니다.

실전 예제: AI 검색 인터페이스 개선하기

실제 사례로 AI 검색 기능의 접근성을 개선해볼게요.

Before: 접근성 문제가 있는 코드

html
<div class="search-container">
  <div class="search-icon">🔍</div>
  <div class="search-input" contenteditable="true">검색어 입력</div>
  <div class="ai-suggestions">
    <div onclick="selectSuggestion('React')">React</div>
    <div onclick="selectSuggestion('Vue')">Vue</div>
    <div onclick="selectSuggestion('Angular')">Angular</div>
  </div>
</div>

문제점:

  • 시맨틱 HTML을 사용하지 않음
  • 키보드로 조작할 수 없음
  • 스크린 리더가 상태 변화를 인지하지 못함
  • ARIA 속성이 없음

After: 접근성 개선 코드

html
<div class="search-container" role="search">
  <label for="search-input" class="visually-hidden">
    AI 검색
  </label>

  <div class="search-wrapper">
    <span aria-hidden="true">🔍</span>
    <input
      type="text"
      id="search-input"
      role="combobox"
      aria-expanded="true"
      aria-autocomplete="list"
      aria-controls="suggestions-list"
      aria-activedescendant="suggestion-0"
      placeholder="검색어를 입력하세요"
      onkeydown="handleKeyNavigation(event)"
      oninput="updateSuggestions()">
  </div>

  <ul
    id="suggestions-list"
    role="listbox"
    aria-label="AI 추천 검색어">
    <li
      id="suggestion-0"
      role="option"
      aria-selected="true"
      onclick="selectSuggestion('React')"
      onkeydown="handleSelection(event)">
      React
    </li>
    <li
      id="suggestion-1"
      role="option"
      aria-selected="false"
      onclick="selectSuggestion('Vue')">
      Vue
    </li>
    <li
      id="suggestion-2"
      role="option"
      aria-selected="false"
      onclick="selectSuggestion('Angular')">
      Angular
    </li>
  </ul>
</div>

<script>
function handleKeyNavigation(event) {
  const list = document.getElementById('suggestions-list');
  const options = list.querySelectorAll('[role="option"]');
  const currentIndex = Array.from(options).findIndex(
    opt => opt.getAttribute('aria-selected') === 'true'
  );

  let newIndex = currentIndex;

  switch(event.key) {
    case 'ArrowDown':
      newIndex = Math.min(currentIndex + 1, options.length - 1);
      event.preventDefault();
      break;
    case 'ArrowUp':
      newIndex = Math.max(currentIndex - 1, 0);
      event.preventDefault();
      break;
    case 'Enter':
      selectSuggestion(options[currentIndex].textContent);
      event.preventDefault();
      return;
    case 'Escape':
      document.getElementById('search-input').setAttribute('aria-expanded', 'false');
      list.style.display = 'none';
      return;
  }

  // 선택 상태 업데이트
  options.forEach((opt, idx) => {
    opt.setAttribute('aria-selected', idx === newIndex ? 'true' : 'false');
  });

  // aria-activedescendant 업데이트
  document.getElementById('search-input').setAttribute(
    'aria-activedescendant',
    options[newIndex].id
  );

  // 스크린 리더 안내
  announceToScreenReader(options[newIndex].textContent);
}

function selectSuggestion(value) {
  document.getElementById('search-input').value = value;
  document.getElementById('search-input').setAttribute('aria-expanded', 'false');
  document.getElementById('suggestions-list').style.display = 'none';

  announceToScreenReader(`${value} 선택됨`);
}
</script>

개선 사항:

  • ✅ 시맨틱 HTML 사용 (<input>, <ul>, <li>)
  • ✅ 키보드 내비게이션 (화살표, Enter, Esc)
  • ✅ ARIA 속성으로 상태 전달
  • ✅ 스크린 리더 안내 (aria-live)
  • ✅ 명확한 레이블

마치며: 기술과 인간성의 균형

AI 기술은 정말 빠르게 발전하고 있습니다. 하지만 웹의 본질은 변하지 않았어요. 웹은 정보와 서비스를 모든 사람에게 평등하게 제공하기 위해 만들어졌습니다.

흰색 로봇 팔 클로즈업 - AI 시대에도 웹의 본질은 모든 사람을 위한 것이어야 합니다
흰색 로봇 팔 클로즈업 - AI 시대에도 웹의 본질은 모든 사람을 위한 것이어야 합니다
사진: Possessed Photography / Unsplash

웹접근성은 선택이 아닌 필수입니다. 몇 가지만 기억해주세요:

  1. 시맨틱 HTML이 기본: AI가 만든 코드를 맹신하지 말고 항상 시맨틱 HTML을 우선하세요.
  2. 키보드 접근성: 모든 기능이 마우스 없이도 작동해야 합니다.
  3. 명확한 피드백: 사용자 행동에 대한 피드백을 시각적, 청각적으로 제공하세요.
  4. 테스트는 필수: 자동화 도구와 실제 사용자 테스트를 병행하세요.
  5. 지속적인 개선: 웹접근성은 한 번 구현하고 끝이 아니라 계속 개선해나가는 과정입니다.

AI 시대에도, 아니 AI 시대이기에 사람 중심의 웹을 만들어야 합니다. 우리가 만드는 웹이 누군가의 세상을 열어줄 수 있다는 것, 잊지 말았으면 좋겠습니다.

참고 자료