HTML/DOM 구조
복잡한 데이터 테이블에서 헤더와 데이터 셀을 연결하는 법: scope, id, headers 속성
통계 및 금융 데이터 테이블을 스크린리더가 낭독할 때 행·열 머리글 맥락이 끊기는 원인과 scope 및 id-headers 정밀 매핑 표준을 분석합니다.
실무 검증 기록
실무 판단 기준 및 판정 조건
본 기록은 공개 fixture와 재현 조건을 기준으로 정리한 기술 설명입니다. 실제 브라우저·스크린리더 조합의 동작은 별도 수동 검토가 필요합니다.
| 검증 상태 | 공개 fixture 기반 재현 완료 |
|---|---|
| 검증 기준 | 공개 fixture 및 재현 조건 |
| 검증 범위 | 2차원 복합 데이터 테이블 헤더 매핑 검증 및 명시된 입력값에 한정 |
| 수동 확인 | 실제 브라우저·키보드·스크린리더 환경에서 별도 확인 필요 |
| fixture에서 확인한 기대 결과 | [실패 상태] td 나열로 행·열 머리글 맥락 상실 ➔ [수정 목표] scope 및 id-headers 매핑으로 스크린리더 계층 안내 |
검증에 사용된 실제 픽스처 / 재현 조건
table > caption + th[scope='col/row'] + td[headers='h1 h2']- 스크린리더별 행/열 우선 낭독 사용자 설정 차이 존재
- 모바일 반응형 가로 스크롤 시 role='region'과 aria-label 래퍼 제공 필요
금융 상품 비교표, 연간 매출 통계표, 항공권 예약 일정표 등 대량의 2차원 데이터를 구조화하여 전달할 때 HTML <table> 요소는 대체 불가능한 핵심 역할을 수행합니다. 시각 중심의 일반 사용자는 표의 가로 행(Row)과 세로 열(Column)을 눈으로 교차 대조하면서, 특정 셀의 숫자(예: '1,500,000원')가 '2026년 2분기(열 헤더)'의 '마케팅 비용(행 헤더)'에 해당한다는 사실을 수 밀리초 만에 직관적으로 파악합니다. 그러나 화면을 볼 수 없어 스크린리더를 사용하는 시각장애인이 표의 각 셀을 방향키로 탐색할 때, 브라우저가 행과 열의 제목 정보를 제때 알려주지 않는다면 사용자는 단지 '1,500,000', '2,300,000', '850,000'이라는 무의미한 숫자들의 나열만 듣게 되는 참담한 경험을 하게 됩니다. WCAG 2.2 성공 기준 1.3.1(Info and Relationships, Level A)은 표의 시각적 관계가 프로그램적으로 완전히 식별 가능해야 한다고 규정합니다. 이 글에서는 단순 표부터 복합 다중 헤더 표까지 완벽하게 대응하는 데이터 테이블 접근성 표준을 정리합니다.
단순 <td> 나열과 CSS 그리드 표가 초래하는 보조공학 단절
웹 접근성 평가에서 가장 흔히 지적되는 데이터 테이블 오류는 두 가지 형태로 나타납니다.
첫 번째는 <table> 마크업을 사용했음에도 불구하고 모든 제목 셀을 <th>(Table Header) 대신 <td>(Table Data)로 마크업하고 CSS font-weight: bold로만 시각적인 굵기를 준 경우입니다. <th> 태그가 없으면 브라우저 접근성 엔진은 표의 헤더가 존재하지 않는 것으로 처리하여 스크린리더 사용자에게 어떤 머리글 맥락도 낭독해주지 않습니다.
두 번째는 반응형 웹 디자인을 핑계로 네이티브 <table>을 버리고 <div>와 CSS Grid/Flexbox로 표 모양만 흉내 낸 경우입니다. role='table', role='row', role='cell'을 정교하게 부여하지 않는 한, 스크린리더는 이를 단순한 문단 덩어리로 인식하여 표 전용 탐색 단축키(Ctrl+Alt+방향키)를 완전히 비활성화시킵니다.
단순 테이블의 기본 공식: <th>와 scope 속성의 올바른 결합
행과 열이 복잡하게 병합(colspan, rowspan)되지 않은 표준적인 단순 표에서는 <th> 요소에 scope 속성을 명시하는 것만으로 완벽한 접근성을 확보할 수 있습니다.
- 1. <caption> 태그를 통한 표의 전체 요약 제공: <table> 태그 바로 첫머리에 <caption>2026년 상반기 부서별 예산 집행 현황</caption>을 선언하여 표의 주제를 스크린리더에 먼저 알립니다.
- 2. 열 머리글에 scope='col' 부여: <thead> 블록 내부의 상단 제목 셀들에 <th scope='col'>부서명</th>, <th scope='col'>배정 예산</th>, <th scope='col'>집행액</th>을 지정합니다.
- 3. 행 머리글에 scope='row' 부여: 각 데이터 행(<tr>)의 첫 번째 셀에 <th scope='row'>기획팀</th>을 선언하여 해당 행 전체의 식별자임을 명시합니다.
다중 병합 헤더(Complex Table)에서의 id와 headers 1:1 정밀 매핑
공공 통계나 재무제표처럼 헤더가 2개 층 이상으로 병합된 복합 표에서는 단순한 scope='col'만으로는 계층 관계를 스크린리더에 정확히 전달할 수 없습니다.
예를 들어 최상단 헤더에 '2025년 실적'과 '2026년 실적'이 2칸씩 병합(colspan='2')되어 있고, 그 아래 하위 헤더로 '목표치'와 '달성치'가 각각 위치하는 경우입니다.
이때는 W3C의 id와 headers 속성을 결합한 정밀 매핑 방식을 적용해야 합니다. 상위 헤더에 id='y2026', 하위 헤더에 id='target'을 부여하고, 실제 데이터 셀에는 <td headers='y2026 target'>85%</td> 형태로 관련 헤더들의 ID를 공백으로 구분하여 나열합니다. 이렇게 마크업하면 시각장애인이 해당 셀에 도착했을 때 스크린리더가 '2026년 실적, 목표치, 85%'라고 완벽한 계층적 문맥을 이어서 낭독해 줍니다.
실무 모바일 반응형 테이블 제작 시 주의점
좁은 모바일 화면에서 표가 깨지는 것을 막기 위해 CSS display: block이나 display: flex를 테이블 요소에 거는 경우가 많습니다. 이때 크롬과 사파리 브라우저는 네이티브 테이블 시맨틱을 접근성 트리에서 자동으로 지워버리는 오랜 렌더링 부작용이 있습니다.
따라서 모바일 반응형을 구현할 때는 <table>의 display 속성을 건드리지 말고, 표를 감싸는 래퍼 컨테이너에 <div class='table-scroll-wrapper' tabindex='0' role='region' aria-label='부서별 예산 현황 표 (가로 스크롤 가능)'> 스타일을 부여하여 가로 스크롤(overflow-x: auto)을 제공하는 방식이 가장 안전하고 접근성을 완벽히 보존하는 모범 사례입니다.
예외 조건 및 수동 검토 범위
- 스크린리더마다 다중 헤더를 읽어주는 순서(행 우선 vs 열 우선)에 사용자 환경 설정 차이가 존재합니다.
- 데이터가 방대한 경우 표 상단에 엑셀 다운로드나 대체 텍스트 요약본을 함께 제공하는 것이 인지적 접근성에 유익합니다.