iBetter Books
수정

도입 스토리

"주문 내역 표를 스크린리더로 읽으니까 숫자만 나열되네요." 김개발이 말했습니다.

NVDA로 표를 탐색하면 이렇게 들렸습니다.

"12345. 20260101. 노트북. 1500000. 완료."

어느 것이 주문번호이고, 어느 것이 날짜인지 알 수 없었습니다.

"<th> 요소와 scope 속성, caption이 없어서예요." 박멘토가 설명했습니다. "제대로 된 표는 스크린리더가 셀을 읽을 때 열 제목과 행 제목을 함께 읽어줘요."

핵심 개념 설명

기본 접근성 표 구조

<table>  <!-- 표 제목 -->  <caption>2026년 1월 주문 내역</caption>  <thead>    <tr>      <th scope="col">주문번호</th>      <th scope="col">주문일</th>      <th scope="col">상품명</th>      <th scope="col">금액</th>      <th scope="col">상태</th>    </tr>  </thead>  <tbody>    <tr>      <td>12345</td>      <td>2026.01.01</td>      <td>노트북 Pro</td>      <td>1,500,000원</td>      <td>배송 완료</td>    </tr>    <tr>      <td>12346</td>      <td>2026.01.05</td>      <td>무선 키보드</td>      <td>89,000원</td>      <td>배송 중</td>    </tr>  </tbody></table>

scope="col"이 있으면 스크린리더가 각 셀을 읽을 때 "주문번호 열 12345", "주문일 열 2026.01.01" 등으로 열 제목을 함께 읽어줍니다.

scope 속성

의미 사용 위치
col 열 제목 <th>에서 해당 열 전체의 제목
row 행 제목 <th>에서 해당 행 전체의 제목
colgroup 열 그룹 제목 <colgroup>을 사용하는 경우
rowgroup 행 그룹 제목 <thead>, <tbody>, <tfoot> 구분
<!-- 행 제목이 있는 표 (시간표 같은 형태) --><table>  <caption>부서별 분기 매출</caption>  <thead>    <tr>      <th scope="col">부서</th>      <th scope="col">1분기</th>      <th scope="col">2분기</th>      <th scope="col">3분기</th>    </tr>  </thead>  <tbody>    <tr>      <th scope="row">영업팀</th>  <!-- 행 제목 -->      <td>120만원</td>      <td>145만원</td>      <td>160만원</td>    </tr>    <tr>      <th scope="row">개발팀</th>      <td>—</td>      <td>—</td>      <td>—</td>    </tr>  </tbody></table>

복잡한 표 — headers 속성

행과 열이 중첩되는 복잡한 표는 headers 속성으로 직접 연결합니다.

<table>  <caption>2026년 영업팀 지역별/분기별 매출</caption>  <thead>    <tr>      <th id="th-blank"></th>      <th id="th-q1" colspan="2">1분기</th>      <th id="th-q2" colspan="2">2분기</th>    </tr>    <tr>      <th id="th-region">지역</th>      <th id="th-q1-a">목표</th>      <th id="th-q1-b">실적</th>      <th id="th-q2-a">목표</th>      <th id="th-q2-b">실적</th>    </tr>  </thead>  <tbody>    <tr>      <th id="th-seoul" scope="row">서울</th>      <td headers="th-q1 th-q1-a th-seoul">500만</td>      <td headers="th-q1 th-q1-b th-seoul">520만</td>      <td headers="th-q2 th-q2-a th-seoul">600만</td>      <td headers="th-q2 th-q2-b th-seoul">580만</td>    </tr>  </tbody></table>

레이아웃 테이블과 데이터 테이블 구분

레이아웃 목적으로 <table>을 사용하면 스크린리더가 표로 읽어 혼란이 생깁니다. 레이아웃에는 CSS Flexbox/Grid를 사용하고, 테이블은 실제 표 데이터에만 씁니다.

<!-- 잘못된 예: 레이아웃 목적 테이블 --><table>  <tr>    <td><img src="photo.jpg" alt="사진"></td>    <td><p>설명 텍스트</p></td>  </tr></table><!-- 올바른 예: CSS Flexbox --><div style="display: flex; gap: 1rem;">  <img src="photo.jpg" alt="사진">  <p>설명 텍스트</p></div>

레이아웃 목적으로 테이블을 꼭 써야 한다면 role="presentation"으로 보조기기에서 표 역할을 제거합니다.

<table role="presentation">  <!-- 보조기기가 표로 인식하지 않음 --></table>

정렬 가능한 테이블

열 제목을 클릭해 정렬하는 테이블에 aria-sort를 사용합니다.

<thead>  <tr>    <th      scope="col"      aria-sort="ascending"  <!-- 오름차순 정렬 -->    >      <button type="button">        이름        <span aria-hidden="true">↑</span>      </button>    </th>    <th      scope="col"      aria-sort="none"  <!-- 정렬 없음 -->    >      <button type="button">날짜</button>    </th>  </tr></thead>

aria-sort 값: ascending (오름차순), descending (내림차순), none (정렬 없음), other (기타).

단계별 실습

따라하기: 테이블 접근성 확인

NVDA에서 표를 탐색합니다.

  • Ctrl+Alt+방향키: 셀 단위 이동
  • T: 다음 테이블로 이동

스크린리더가 각 셀을 읽을 때 열/행 제목이 함께 읽히는지 확인합니다.

변형하기: 반응형 테이블 접근성

작은 화면에서 표를 카드 형태로 변환할 때 접근성을 유지합니다.

/* 작은 화면에서 테이블 열을 데이터 레이블로 변환 */@media (max-width: 640px) {  table, thead, tbody, tr, th, td {    display: block;  }  thead tr {    position: absolute;    top: -9999px;    left: -9999px;  }  td::before {    content: attr(data-label);    font-weight: bold;    display: block;  }}
<!-- data-label 속성으로 레이블 제공 --><tr>  <td data-label="주문번호">12345</td>  <td data-label="주문일">2026.01.01</td>  <td data-label="금액">1,500,000원</td></tr>

정리와 확인

핵심 내용 요약

  • <caption>: 표 제목 (첫 번째 자식 요소)
  • scope="col/row": 열/행 제목 지정
  • headers 속성: 복잡한 표에서 셀-제목 직접 연결
  • role="presentation": 레이아웃 테이블에서 표 의미 제거
  • aria-sort: 정렬 가능한 테이블의 현재 정렬 상태

확인 문제

문제 1. <th> 없이 <td>만 있는 표를 스크린리더로 탐색하면 어떤 문제가 생기는가?

각 셀을 읽을 때 값만 읽히고 어느 열/행에 속하는지 알 수 없습니다.
"12345, 2026.01.01, 1500000"처럼 숫자 나열이 됩니다.

문제 2. <caption> 대신 <h2> + 일반 <p> 텍스트로 표 제목을 표시하면 안 되는 이유는?

<caption>은 표와 프로그래밍적으로 연결됩니다.
스크린리더가 표로 이동할 때 "2026년 1월 주문 내역, 표, 5열 2행"처럼
제목과 표 정보를 함께 알려줍니다.

PART 06을 마쳤습니다. 다음 PART에서는 실제 자주 쓰는 UI 컴포넌트들 — 버튼, 토글, 진행 표시줄, 알림 배지 등의 접근성 구현을 다룹니다.