웹 접근성: 복잡한 데이터 테이블의 접근성 향상 기법 📊
웹 개발의 세계에서 접근성은 더 이상 선택이 아닌 필수입니다. 특히 복잡한 데이터 테이블을 다룰 때, 접근성을 고려하는 것은 매우 중요합니다. 이 글에서는 복잡한 데이터 테이블의 접근성을 향상시키는 다양한 기법과 방법들을 상세히 살펴보겠습니다.
웹 접근성이란 모든 사용자가 웹 콘텐츠를 동등하게 인식하고, 이해하며, 탐색하고 상호 작용할 수 있도록 보장하는 것을 의미합니다. 특히 시각 장애인, 청각 장애인, 운동 장애인 등 다양한 장애를 가진 사용자들도 웹 콘텐츠를 쉽게 이용할 수 있도록 하는 것이 중요합니다.
복잡한 데이터 테이블은 많은 정보를 효과적으로 전달할 수 있는 강력한 도구입니다. 하지만 접근성을 고려하지 않으면, 스크린 리더 사용자나 키보드 사용자들에게는 큰 장벽이 될 수 있습니다. 따라서 우리는 이러한 사용자들도 테이블의 정보를 쉽게 이해하고 탐색할 수 있도록 해야 합니다.
이 글에서는 HTML 구조, ARIA 속성, CSS 스타일링, JavaScript를 활용한 동적 기능 등 다양한 측면에서 테이블의 접근성을 향상시키는 방법을 다룰 것입니다. 또한 실제 사례와 코드 예제를 통해 이론을 실제로 적용하는 방법도 살펴보겠습니다.
웹 개발자로서, 우리는 모든 사용자에게 동등한 경험을 제공할 책임이 있습니다. 재능넷과 같은 플랫폼에서도 이러한 접근성 원칙을 적용하여 더 많은 사용자들이 편리하게 서비스를 이용할 수 있도록 노력하고 있습니다. 이 글을 통해 여러분도 더 나은 웹 경험을 만들어갈 수 있기를 희망합니다.
그럼 지금부터 복잡한 데이터 테이블의 접근성을 향상시키는 다양한 기법들을 자세히 알아보도록 하겠습니다. 🚀
1. HTML 구조를 활용한 테이블 접근성 향상 🏗️
복잡한 데이터 테이블의 접근성을 향상시키는 첫 번째 단계는 적절한 HTML 구조를 사용하는 것입니다. HTML은 테이블의 구조와 의미를 명확하게 전달할 수 있는 다양한 요소와 속성을 제공합니다.
1.1 기본적인 테이블 구조
먼저, 테이블의 기본 구조를 올바르게 사용하는 것이 중요합니다. 다음은 기본적인 테이블 구조의 예시입니다:
<table>
<caption>2023년 분기별 매출 현황</caption>
<thead>
<tr>
<th scope="col">분기</th>
<th scope="col">매출(백만원)</th>
<th scope="col">성장률</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1분기</th>
<td>1,000</td>
<td>10%</td>
</tr>
<tr>
<th scope="row">2분기</th>
<td>1,200</td>
<td>20%</td>
</tr>
</tbody>
</table>
이 구조에서 주목해야 할 점들은 다음과 같습니다:
- <caption>: 테이블의 제목이나 설명을 제공합니다. 스크린 리더 사용자에게 테이블의 내용을 미리 알려줍니다.
- <thead>: 테이블의 헤더 부분을 그룹화합니다.
- <tbody>: 테이블의 본문 데이터를 그룹화합니다.
- <th>: 헤더 셀을 나타냅니다.
scope
속성을 사용하여 해당 헤더가 행(row)인지 열(col)인지 명시합니다.
1.2 복잡한 테이블 구조
더 복잡한 테이블의 경우, 추가적인 HTML 요소와 속성을 사용하여 구조를 명확히 할 수 있습니다.
<table>
<caption>2023년 지역별, 분기별 매출 현황</caption>
<thead>
<tr>
<th scope="col" rowspan="2">지역</th>
<th scope="col" colspan="3">분기별 매출(백만원)</th>
</tr>
<tr>
<th scope="col">1분기</th>
<th scope="col">2분기</th>
<th scope="col">3분기</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">서울</th>
<td>1,000</td>
<td>1,200</td>
<td>1,300</td>
</tr>
<tr>
<th scope="row">부산</th>
<td>800</td>
<td>900</td>
<td>1,000</td>
</tr>
</tbody>
</table>
이 예제에서는 다음과 같은 추가적인 기법들이 사용되었습니다:
- rowspan, colspan: 셀을 병합하여 복잡한 구조를 표현합니다.
- 중첩된 헤더: 여러 레벨의 헤더를 사용하여 데이터의 계층 구조를 나타냅니다.
1.3 id와 headers 속성 활용
매우 복잡한 테이블의 경우, id
와 headers
속성을 사용하여 셀 간의 관계를 명확히 할 수 있습니다.
<table>
<caption>2023년 지역별, 분기별 매출 및 성장률</caption>
<thead>
<tr>
<th id="region" rowspan="2">지역</th>
<th id="q1" colspan="2">1분기</th>
<th id="q2" colspan="2">2분기</th>
</tr>
<tr>
<th id="q1sales">매출</th>
<th id="q1growth">성장률</th>
<th id="q2sales">매출</th>
<th id="q2growth">성장률</th>
</tr>
</thead>
<tbody>
<tr>
<th id="seoul" headers="region">서울</th>
<td headers="seoul q1 q1sales">1,000</td>
<td headers="seoul q1 q1growth">10%</td>
<td headers="seoul q2 q2sales">1,200</td>
<td headers="seoul q2 q2growth">20%</td>
</tr>
<tr>
<th id="busan" headers="region">부산</th>
<td headers="busan q1 q1sales">800</td>
<td headers="busan q1 q1growth">5%</td>
<td headers="busan q2 q2sales">900</td>
<td headers="busan q2 q2growth">12.5%</td>
</tr>
</tbody>
</table>
이 방식을 사용하면 각 데이터 셀이 어떤 헤더와 관련되어 있는지 명확하게 표시할 수 있습니다. 스크린 리더는 이 정보를 사용하여 사용자에게 각 셀의 컨텍스트를 정확하게 전달할 수 있습니다.
1.4 요약 정보 제공
복잡한 테이블의 경우, summary
속성이나 별도의 요약 텍스트를 제공하는 것이 도움이 될 수 있습니다. HTML5에서는 summary
속성이 더 이상 사용되지 않지만, 다음과 같은 방법으로 요약 정보를 제공할 수 있습니다:
<table aria-describedby="summary">
<caption>2023년 지역별, 분기별 매출 및 성장률</caption>
<!-- 테이블 내용 -->
</table>
<p id="summary">이 테이블은 2023년의 1분기와 2분기 동안 서울과 부산 지역의 매출액과 성장률을 보여줍니다. 각 지역별로 분기별 매출액과 해당 분기의 성장률이 제시되어 있습니다.</p>
이렇게 제공된 요약 정보는 스크린 리더 사용자가 테이블의 전체적인 구조와 내용을 빠르게 이해하는 데 도움을 줍니다.
1.5 반응형 테이블 구조
모바일 기기에서의 접근성도 고려해야 합니다. 복잡한 테이블은 작은 화면에서 보기 어려울 수 있으므로, 반응형 디자인을 적용하는 것이 좋습니다. 다음은 간단한 반응형 테이블 구조의 예시입니다:
<div class="table-container">
<table>
<!-- 테이블 내용 -->
</table>
</div>
<style>
.table-container {
overflow-x: auto;
}
@media (max-width: 600px) {
table, thead, tbody, th, td, tr {
display: block;
}
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
tr { border: 1px solid #ccc; }
td {
border: none;
position: relative;
padding-left: 50%;
}
td:before {
position: absolute;
top: 6px;
left: 6px;
width: 45%;
padding-right: 10px;
white-space: nowrap;
content: attr(data-label);
}
}
</style>
이 방식을 사용하면 작은 화면에서 테이블이 세로로 재구성되어 스크롤 없이 모든 데이터를 볼 수 있습니다. 단, 이 경우 각 데이터 셀에 data-label
속성을 추가하여 헤더 정보를 제공해야 합니다.
이러한 HTML 구조를 활용하면 복잡한 데이터 테이블의 기본적인 접근성을 크게 향상시킬 수 있습니다. 다음 섹션에서는 ARIA 속성을 사용하여 더욱 향상된 접근성을 제공하는 방법에 대해 알아보겠습니다.
2. ARIA를 활용한 테이블 접근성 향상 🎭
ARIA(Accessible Rich Internet Applications)는 웹 콘텐츠와 웹 애플리케이션의 접근성을 향상시키기 위한 WAI-ARIA 규격의 일부입니다. ARIA 속성을 사용하면 복잡한 데이터 테이블의 의미와 기능을 보조 기술에 더 명확하게 전달할 수 있습니다.
2.1 role 속성 사용
ARIA role
속성을 사용하여 테이블의 각 부분의 역할을 명확히 할 수 있습니다.
<table role="table">
<caption>2023년 분기별 매출 현황</caption>
<thead role="rowgroup">
<tr role="row">
<th role="columnheader" scope="col">분기</th>
<th role="columnheader" scope="col">매출(백만원)</th>
<th role="columnheader" scope="col">성장률</th>
</tr>
</thead>
<tbody role="rowgroup">
<tr role="row">
<th role="rowheader" scope="row">1분기</th>
<td role="cell">1,000</td>
<td role="cell">10%</td>
</tr>
<tr role="row">
<th role="rowheader" scope="row">2분기</th>
<td role="cell">1,200</td>
<td role="cell">20%</td>
</tr>
</tbody>
</table>
이렇게 role
속성을 사용하면 HTML 구조가 명확하지 않은 경우에도 각 요소의 역할을 명시적으로 정의할 수 있습니다.
2.2 aria-labelledby와 aria-describedby 사용
aria-labelledby
와 aria-describedby
속성을 사용하여 테이블에 레이블과 설명을 연결할 수 있습니다.
<h2 id="table-title">2023년 분기별 매출 현황</h2>
<p id="table-desc">이 테이블은 2023년의 각 분기별 매출액과 전년 대비 성장률을 보여줍니다.</p>
<table aria-labelledby="table-title" aria-describedby="table-desc">
<!-- 테이블 내용 -->
</table>
이 방식을 사용하면 테이블의 제목과 설명을 테이블 자체와 프로그래밍적으로 연결할 수 있어, 스크린 리더 사용자가 테이블의 컨텍스트를 더 쉽게 이해할 수 있습니다.
2.3 aria-sort 속성 사용
정렬 가능한 열이 있는 경우, aria-sort
속성을 사용하여 현재 정렬 상태를 나타낼 수 있습니다.
<table>
<thead>
<tr>
<th aria-sort="ascending">분기</th>
<th aria-sort="none">매출(백만원)</th>
<th aria-sort="none">성장률</th>
</tr>
</thead>
<!-- 테이블 본문 -->
</table>
aria-sort
속성의 가능한 값은 "ascending", "descending", "other", "none"입니다. 이를 통해 사용자는 현재 어떤 열을 기준으로 정렬되어 있는지 알 수 있습니다.
2.4 aria-expanded 속성 사용
확장 가능한 행이나 열이 있는 경우, aria-expanded
속성을 사용하여 현재 상태를 나타낼 수 있습니다.
<tr>
<th>
<button aria-expanded="false" onclick="toggleDetails(this)">1분기 상세 보기</button>
</th>
<td>1,000</td>
<td>10%</td>
</tr>
<tr class="details" style="display: none;">
<td colspan="3">
<!-- 상세 정보 -->
</td>
</tr>
<script>
function toggleDetails(button) {
var detailsRow = button.closest('tr').nextElementSibling;
var isExpanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', !isExpanded);
detailsRow.style.display = isExpanded ? 'none' : 'table-row';
}
</script>
이 예제에서는 버튼을 클릭하면 상세 정보 행이 토글되며, aria-expanded
속성이 업데이트됩니다.
2.5 aria-live 영역 사용
테이블의 데이터가 동적으로 업데이트되는 경우, aria-live
영역을 사용하여 변경 사항을 사용자에게 알릴 수 있습니다.
<div id="table-update" aria-live="polite"></div>
<table id="dynamic-table">
<!-- 테이블 내용 -->
</table>
<script>
function updateTable(newData) {
// 테이블 업데이트 로직
document.getElementById('table-update').textContent = '테이블이 업데이트되었습니다.';
}
</script>
이 방식을 사용하면 테이블 데이터가 변경될 때 스크린 리더 사용자에게 자동으로 알림이 제공됩니다.
2.6 aria-rowcount와 aria-rowindex 사용
대량의 데이터를 가진 테이블에서 페이지네이션을 사용하는 경우, aria-rowcount
와 aria-rowindex
속성을 사용하여 전체 행 수와 현재 행의 인덱스를 나타낼 수 있습니다.
<table aria-rowcount="100">
<tr aria-rowindex="1">
<th>항목</th>
<th>값</th>
</tr>
<tr aria-rowindex="2">
<td>항목 1</td>
<td>100</td>
</tr>
<!-- 추가 행 -->
</table>
이 방식을 사용하면 사용자가 현재 보고 있는 행이 전체 데이터에서 어느 위치에 있는지 파악할 수 있습니다.
2.7 복잡한 헤더 구조에서의 ARIA 사용
매우 복잡한 헤더 구조를 가진 테이블의 경우, aria-colcount
, aria-colspan
, aria-rowspan
속성을 사용하여 구조를 명확히 할 수 있습니다.
<table aria-colcount="4">
<tr>
<th aria-colspan="4">2023년 분기별 실적</th>
</tr>
<tr>
<th>분기</th>
<th aria-colspan="3">실적 지표</th>
</tr>
<tr>
<th></th>
<th>매출</th>
<th>비용</th>
<th>이익</th>
</tr>
<!-- 데이터 행 -->
</table>
이러한 ARIA 속성들을 사용하면 복잡한 테이블 구조를 보조 기술에 더 정확하게 전달할 수 있습니다.
ARIA를 활용하면 HTML만으로는 표현하기 어려운 복잡한 의미와 관계를 명확히 할 수 있습니다. 하지만 ARIA는 신중하게 사용해야 합니다. 불필요하거나 잘못된 ARIA 사용은 오히려 접근성을 해칠 수 있으므로, 항상 적절한 HTML 구조를 먼저 사용하고, 필요한 경우에만 ARIA를 보완적으로 사용하는 것이 좋습니다.
다음 섹션에서는 CSS를 활용하여 테이블의 시각적 접근성을 향상시키는 방법에 대해 알아보겠습니다.
3. CSS를 활용한 테이블 접근성 향상 🎨
CSS는 테이블의 시각적 표현을 개선하여 모든 사용자가 테이블 내용을 쉽게 이해할 수 있도록 도와줍니다. 적절한 CSS 스타일링은 테이블의 가독성을 높이고, 데이터 간의 관계를 명확히 하며, 다양한 디바이스와 화면 크기에서의 사용성을 개선할 수 있습니다.
3.1 기본적인 테이블 스타일링
먼저, 테이블의 기본적인 가독성을 높이는 CSS 스타일을 적용해 보겠습니다.
table {
border-collapse: collapse;
width: 100%;
max-width: 800px;
margin: 0 auto;
font- family: Arial, sans-serif;
}
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background-color: #f2f2f2;
font-weight: bold;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
caption {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 10px;
}
이러한 기본 스타일링은 다음과 같은 효과를 제공합니다:
- 테이블 셀 간의 경계를 명확히 하여 데이터 구분을 용이하게 합니다.
- 헤더 셀을 강조하여 열과 행의 의미를 쉽게 파악할 수 있게 합니다.
- 짝수 행에 배경색을 적용하여 행 간 구분을 명확히 합니다.
- 캡션 스타일을 조정하여 테이블의 제목을 강조합니다.
3.2 반응형 테이블 디자인
모바일 기기에서도 테이블을 쉽게 볼 수 있도록 반응형 디자인을 적용할 수 있습니다.
@media screen and (max-width: 600px) {
table, thead, tbody, th, td, tr {
display: block;
}
thead tr {
position: absolute;
top: -9999px;
left: -9999px;
}
tr {
margin-bottom: 10px;
border: 1px solid #ccc;
}
td {
border: none;
position: relative;
padding-left: 50%;
}
td:before {
position: absolute;
top: 6px;
left: 6px;
width: 45%;
padding-right: 10px;
white-space: nowrap;
content: attr(data-label);
font-weight: bold;
}
}
이 CSS는 작은 화면에서 테이블을 세로로 재구성하여 가로 스크롤 없이 모든 데이터를 볼 수 있게 합니다. 각 데이터 셀 앞에 헤더 정보가 표시되어 컨텍스트를 유지합니다.
3.3 호버 효과 추가
행에 호버 효과를 추가하여 사용자가 현재 보고 있는 행을 쉽게 식별할 수 있게 합니다.
tr:hover {
background-color: #f5f5f5;
transition: background-color 0.3s ease;
}
3.4 스크롤 가능한 테이블
테이블이 너무 넓어 화면을 벗어나는 경우, 가로 스크롤을 추가하여 모든 열을 볼 수 있게 합니다.
.table-container {
width: 100%;
overflow-x: auto;
}
table {
min-width: 600px; /* 테이블의 최소 너비 설정 */
}
3.5 고대비 모드 지원
시각 장애가 있는 사용자를 위해 고대비 모드를 지원할 수 있습니다.
@media (prefers-color-scheme: dark) {
table {
background-color: #000;
color: #fff;
}
th, td {
border-color: #444;
}
th {
background-color: #333;
}
tr:nth-child(even) {
background-color: #222;
}
tr:hover {
background-color: #444;
}
}
3.6 포커스 표시 개선
키보드 사용자를 위해 포커스 표시를 개선합니다.
th:focus, td:focus {
outline: 2px solid #4CAF50;
outline-offset: -2px;
}
3.7 프린트 스타일 최적화
테이블을 프린트할 때 최적화된 스타일을 적용합니다.
@media print {
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid #000;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
}
이러한 CSS 기법들을 적용하면 테이블의 시각적 접근성과 사용성을 크게 향상시킬 수 있습니다. 다음 섹션에서는 JavaScript를 사용하여 테이블의 동적 기능을 구현하고 접근성을 더욱 개선하는 방법에 대해 알아보겠습니다.
4. JavaScript를 활용한 테이블 접근성 향상 🚀
JavaScript를 사용하면 테이블에 동적 기능을 추가하고 사용자 경험을 개선할 수 있습니다. 동시에 이러한 기능들이 접근성을 해치지 않도록 주의해야 합니다. 여기서는 JavaScript를 사용하여 테이블의 접근성을 향상시키는 몇 가지 기법을 살펴보겠습니다.
4.1 정렬 기능 구현
사용자가 열을 클릭하여 테이블을 정렬할 수 있게 하는 기능을 구현해 보겠습니다.
// HTML
<table id="sortable-table">
<thead>
<tr>
<th>이름</th>
<th>나이</th>
<th>직업</th>
</tr>
</thead>
<tbody>
<!-- 테이블 데이터 -->
</tbody>
</table>
// JavaScript
document.querySelectorAll('#sortable-table th').forEach(headerCell => {
headerCell.addEventListener('click', () => {
const tableElement = headerCell.parentElement.parentElement.parentElement;
const headerIndex = Array.prototype.indexOf.call(headerCell.parentElement.children, headerCell);
const currentIsAscending = headerCell.classList.contains('th-sort-asc');
sortTableByColumn(tableElement, headerIndex, !currentIsAscending);
});
});
function sortTableByColumn(table, column, asc = true) {
const dirModifier = asc ? 1 : -1;
const tBody = table.tBodies[0];
const rows = Array.from(tBody.querySelectorAll('tr'));
const sortedRows = rows.sort((a, b) => {
const aColText = a.querySelector(`td:nth-child(${column + 1})`).textContent.trim();
const bColText = b.querySelector(`td:nth-child(${column + 1})`).textContent.trim();
return aColText > bColText ? (1 * dirModifier) : (-1 * dirModifier);
});
while (tBody.firstChild) {
tBody.removeChild(tBody.firstChild);
}
tBody.append(...sortedRows);
table.querySelectorAll('th').forEach(th => th.classList.remove('th-sort-asc', 'th-sort-desc'));
table.querySelector(`th:nth-child(${column + 1})`).classList.toggle('th-sort-asc', asc);
table.querySelector(`th:nth-child(${column + 1})`).classList.toggle('th-sort-desc', !asc);
// 접근성을 위한 ARIA 속성 업데이트
table.querySelectorAll('th').forEach(th => th.setAttribute('aria-sort', 'none'));
table.querySelector(`th:nth-child(${column + 1})`).setAttribute('aria-sort', asc ? 'ascending' : 'descending');
// 정렬 결과를 스크린 리더에 알림
const sortAnnouncement = document.getElementById('sort-announcement') || document.createElement('div');
sortAnnouncement.id = 'sort-announcement';
sortAnnouncement.setAttribute('aria-live', 'polite');
sortAnnouncement.textContent = `테이블이 ${table.querySelector(`th:nth-child(${column + 1})`).textContent} 열을 기준으로 ${asc ? '오름차순' : '내림차순'} 정렬되었습니다.`;
document.body.appendChild(sortAnnouncement);
}
이 코드는 다음과 같은 접근성 개선 사항을 포함합니다:
- ARIA 속성을 사용하여 현재 정렬 상태를 명시적으로 표시합니다.
- 정렬 결과를 스크린 리더 사용자에게 알립니다.
4.2 페이지네이션 구현
대량의 데이터를 가진 테이블의 경우, 페이지네이션을 구현하여 사용성을 개선할 수 있습니다.
// HTML
<table id="paginated-table">
<!-- 테이블 내용 -->
</table>
<div id="pagination-controls"></div>
// JavaScript
const rowsPerPage = 10;
let currentPage = 1;
function setupPagination() {
const table = document.getElementById('paginated-table');
const rows = table.querySelectorAll('tbody tr');
const pageCount = Math.ceil(rows.length / rowsPerPage);
const paginationControls = document.getElementById('pagination-controls');
paginationControls.innerHTML = '';
for (let i = 1; i <= pageCount; i++) {
const btn = document.createElement('button');
btn.textContent = i;
btn.addEventListener('click', () => {
currentPage = i;
showPage(currentPage);
updatePaginationButtons();
});
paginationControls.appendChild(btn);
}
showPage(currentPage);
updatePaginationButtons();
}
function showPage(page) {
const table = document.getElementById('paginated-table');
const rows = table.querySelectorAll('tbody tr');
const startIndex = (page - 1) * rowsPerPage;
const endIndex = startIndex + rowsPerPage;
rows.forEach((row, index) => {
if (index >= startIndex && index < endIndex) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
// 접근성을 위한 알림
const announcement = document.getElementById('page-announcement') || document.createElement('div');
announcement.id = 'page-announcement';
announcement.setAttribute('aria-live', 'polite');
announcement.textContent = `${page} 페이지를 표시합니다. 총 ${Math.ceil(rows.length / rowsPerPage)} 페이지 중 ${page} 페이지입니다.`;
document.body.appendChild(announcement);
}
function updatePaginationButtons() {
const buttons = document.querySelectorAll('#pagination-controls button');
buttons.forEach((button, index) => {
if (index + 1 === currentPage) {
button.setAttribute('aria-current', 'page');
} else {
button.removeAttribute('aria-current');
}
});
}
setupPagination();
이 구현에는 다음과 같은 접근성 고려사항이 포함되어 있습니다:
- 현재 페이지 상태를 ARIA 속성으로 표시합니다.
- 페이지 변경 시 스크린 리더 사용자에게 알림을 제공합니다.
4.3 필터링 기능 구현
사용자가 특정 조건에 맞는 데이터만 볼 수 있도록 필터링 기능을 구현할 수 있습니다.
// HTML
<input type="text" id="table-filter" placeholder="필터...">
<table id="filterable-table">
<!-- 테이블 내용 -->
</table>
// JavaScript
const filterInput = document.getElementById('table-filter');
const table = document.getElementById('filterable-table');
filterInput.addEventListener('input', function() {
const filterValue = this.value.toLowerCase();
const rows = table.querySelectorAll('tbody tr');
let visibleRowCount = 0;
rows.forEach(row => {
const text = row.textContent.toLowerCase();
if (text.includes(filterValue)) {
row.style.display = '';
visibleRowCount++;
} else {
row.style.display = 'none';
}
});
// 접근성을 위한 필터링 결과 알림
const announcement = document.getElementById('filter-announcement') || document.createElement('div');
announcement.id = 'filter-announcement';
announcement.setAttribute('aria-live', 'polite');
announcement.textContent = `필터링 결과: ${visibleRowCount}개의 행이 표시됩니다.`;
document.body.appendChild(announcement);
});
이 구현은 필터링 결과를 스크린 리더 사용자에게 알려주어 접근성을 개선합니다.
4.4 데이터 편집 기능
사용자가 테이블 데이터를 직접 편집할 수 있는 기능을 구현할 수 있습니다.
// JavaScript
function makeEditable(table) {
table.querySelectorAll('td').forEach(cell => {
cell.addEventListener('dblclick', function() {
const originalContent = this.textContent;
const input = document.createElement('input');
input.type = 'text';
input.value = originalContent;
input.addEventListener('blur', function() {
const newContent = this.value;
cell.textContent = newContent;
// 변경 사항을 스크린 리더에 알림
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
announcement.textContent = `셀 내용이 ${originalContent}에서 ${newContent}로 변경되었습니다.`;
document.body.appendChild(announcement);
});
this.textContent = '';
this.appendChild(input);
input.focus();
});
});
}
makeEditable(document.getElementById('editable-table'));
이 구현은 셀 내용 변경 시 스크린 리더 사용자에게 알림을 제공하여 접근성을 개선합니다.
4.5 키보드 네비게이션 개선
키보드 사용자를 위해 테이블 내에서의 네비게이션을 개선할 수 있습니다.
// JavaScript
function enhanceKeyboardNavigation(table) {
const cells = table.querySelectorAll('th, td');
cells.forEach((cell, index) => {
cell.setAttribute('tabindex', '0');
cell.addEventListener('keydown', function(e) {
switch(e.key) {
case 'ArrowRight':
if (cells[index + 1]) cells[index + 1].focus();
break;
case 'ArrowLeft':
if (cells[index - 1]) cells[index - 1].focus();
break;
case 'ArrowDown':
const nextRowCell = cells[index + table.rows[0].cells.length];
if (nextRowCell) nextRowCell.focus();
break;
case 'ArrowUp':
const prevRowCell = cells[index - table.rows[0].cells.length];
if (prevRowCell) prevRowCell.focus();
break;
}
});
});
}
enhanceKeyboardNavigation(document.getElementById('navigable-table'));
이 구현은 키보드 사용자가 화살표 키를 사용하여 테이블 내에서 쉽게 이동할 수 있게 합니다.
이러한 JavaScript 기법들을 사용하면 테이블의 기능성을 크게 향상시킬 수 있습니다. 하지만 항상 접근성을 고려하여 구현해야 하며, 모든 사용자가 동등하게 테이블의 기능을 이용할 수 있도록 해야 합니다.
다음 섹션에서는 이러한 모든 기법들을 종합하여 실제 사례를 통해 접근성이 뛰어난 복잡한 데이터 테이블을 구현하는 방법을 살펴보겠습니다.