웹 접근성(a11y)은 장애를 가진 사용자도 웹 콘텐츠를 인식하고 사용할 수 있게 만드는 것입니다. 법적 요구사항이기도 하고, 더 넓은 사용자에게 서비스를 제공하는 것이기도 합니다.

면접에서 "접근성을 고려해 개발한 경험이 있나요?"라고 물으면, 구체적인 ARIA 속성이나 키보드 네비게이션 구현 사례를 들 수 있으면 좋습니다.


시맨틱 HTML

VUE
<template>
  <!-- 나쁜 예: div만으로 구성 -->
  <div class="header">
    <div class="nav">...</div>
  </div>

  <!-- 좋은 예: 시맨틱 태그 사용 -->
  <header>
    <nav aria-label="메인 네비게이션">
      <ul>
        <li><RouterLink to="/">홈</RouterLink></li>
        <li><RouterLink to="/about">소개</RouterLink></li>
      </ul>
    </nav>
  </header>

  <main>
    <article>
      <h1>제목</h1>
      <section aria-labelledby="section-title">
        <h2 id="section-title">섹션 제목</h2>
        <p>본문</p>
      </section>
    </article>
  </main>

  <footer>
    <p>저작권 정보</p>
  </footer>
</template>

ARIA 속성 활용

VUE
<script setup lang="ts">
import { ref } from 'vue'

const isMenuOpen = ref(false)
const selectedTab = ref(0)
const tabs = ['개요', '상세', '리뷰']
</script>

<template>
  <!-- 토글 버튼 -->
  <button
    :aria-expanded="isMenuOpen"
    aria-controls="menu"
    @click="isMenuOpen = !isMenuOpen"
  >
    메뉴 {{ isMenuOpen ? '닫기' : '열기' }}
  </button>

  <nav v-show="isMenuOpen" id="menu" role="menu">
    <a role="menuitem" href="/">홈</a>
    <a role="menuitem" href="/about">소개</a>
  </nav>

  <!-- 탭 패널 -->
  <div role="tablist" aria-label="콘텐츠 탭">
    <button
      v-for="(tab, index) in tabs"
      :key="tab"
      role="tab"
      :aria-selected="selectedTab === index"
      :tabindex="selectedTab === index ? 0 : -1"
      @click="selectedTab = index"
    >
      {{ tab }}
    </button>
  </div>

  <div
    v-for="(tab, index) in tabs"
    :key="tab"
    role="tabpanel"
    :aria-labelledby="`tab-${index}`"
    v-show="selectedTab === index"
  >
    {{ tab }} 내용
  </div>
</template>

키보드 네비게이션

VUE
<script setup lang="ts">
import { ref } from 'vue'

const items = ref(['항목 1', '항목 2', '항목 3'])
const focusedIndex = ref(0)

const handleKeydown = (event: KeyboardEvent) => {
  switch (event.key) {
    case 'ArrowDown':
      event.preventDefault()
      focusedIndex.value = Math.min(focusedIndex.value + 1, items.value.length - 1)
      break
    case 'ArrowUp':
      event.preventDefault()
      focusedIndex.value = Math.max(focusedIndex.value - 1, 0)
      break
    case 'Enter':
    case ' ':
      event.preventDefault()
      selectItem(focusedIndex.value)
      break
  }
}
</script>

<template>
  <ul role="listbox" @keydown="handleKeydown">
    <li
      v-for="(item, index) in items"
      :key="item"
      role="option"
      :aria-selected="focusedIndex === index"
      :tabindex="focusedIndex === index ? 0 : -1"
    >
      {{ item }}
    </li>
  </ul>
</template>

라이브 리전 — 동적 컨텐츠 알림

VUE
<template>
  <!-- 스크린 리더에게 동적 변경사항을 알림 -->
  <div aria-live="polite" aria-atomic="true" class="sr-only">
    {{ statusMessage }}
  </div>

  <!-- assertive — 즉시 알림 (긴급한 경우만) -->
  <div aria-live="assertive" class="sr-only">
    {{ errorMessage }}
  </div>
</template>

<style>
/* 화면에는 보이지 않지만 스크린 리더는 읽을 수 있음 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
</style>

폼 접근성

VUE
<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <label for="email">이메일 (필수)</label>
      <input
        id="email"
        type="email"
        v-model="form.email"
        aria-required="true"
        :aria-invalid="!!errors.email"
        :aria-describedby="errors.email ? 'email-error' : undefined"
      />
      <span v-if="errors.email" id="email-error" role="alert">
        {{ errors.email }}
      </span>
    </div>
  </form>
</template>

SPA 라우트 전환 알림

TYPESCRIPT
// SPA에서 페이지 전환 시 스크린 리더에 알림
router.afterEach((to) => {
  // 포커스를 메인 컨텐츠로 이동
  nextTick(() => {
    const main = document.querySelector('main')
    main?.focus()

    // 페이지 제목 변경 알림
    document.title = (to.meta.title as string) || '앱 이름'
  })
})

면접 팁

  • 접근성은 선택이 아닌 필수 라는 인식을 보여주세요. 한국은 장애인차별금지법에 따라 웹 접근성 준수가 의무입니다
  • aria-label, aria-expanded, aria-live의 용도를 구체적으로 설명할 수 있으면 좋습니다
  • SPA에서의 접근성 과제(라우트 전환 시 포커스 관리, 동적 컨텐츠 알림)를 알고 있으면 실무 역량을 보여줄 수 있습니다

요약

Vue 앱의 접근성은 시맨틱 HTML을 기본으로, ARIA 속성으로 동적 UI의 상태를 전달하고, 키보드 네비게이션을 구현하는 것입니다. aria-live로 동적 변경을 알리고, SPA 라우트 전환 시 포커스를 관리하는 것이 핵심입니다.

댓글 로딩 중...