표의 숫자는 잘 보이는데 어느 상품의 어느 분기 값인지 읽어 주는 단서가 없습니다. 배경색을 진하게 칠한 첫 행만으로는 행·열의 관계가 HTML에 기록되지 않습니다. 이 글은 두 상품과 두 분기를 가진 작은 표의 실제 합성 HTML 여섯 개를 파싱해 네 데이터 셀의 머리글을 연결합니다. 단순한 scope와 id·headers 참조를 비교하며 브라우저나 화면 낭독기의 전체 처리 과정을 구현했다고 주장하지 않습니다.
숫자 한 칸을 읽을 때 필요한 문맥을 먼저 적습니다.
자료집의 1분기 수량은 12, 2분기는 15입니다. 체크리스트는 각각 8과 11입니다. “15”만 읽으면 무엇의 값인지 알기 어렵습니다. 기대하는 해석은 자료집·2분기·15의 묶음입니다. 같은 방식으로 모든 데이터 셀에 행 이름과 열 이름을 연결하면 표를 구성하는 의미 관계가 드러납니다.
이 표의 수량은 가상 값이며 실제 판매 성과가 아닙니다. 목적은 숫자를 분석하는 것이 아니라 같은 숫자에 어떤 머리글이 필요한지 설계하는 것입니다. 원시 데이터와 HTML 표시를 한 번에 고치지 말고 먼저 기대 관계를 적어야 템플릿 수정 뒤에도 표의 뜻이 유지됐는지 확인할 수 있습니다.
여섯 HTML을 파싱해 머리글 연결을 비교했습니다.
입력은 일반 td만 있는 표, 열 scope만 있는 표, 양방향 scope 표, 명시적 headers 참조 표, 없는 ID를 참조한 표, colspan이 있는 표입니다. Python HTMLParser로 실제 태그·속성·글자를 읽습니다. td 데이터 셀마다 같은 행의 scope=row와 같은 열의 scope=col을 모으거나 headers 목록에서 참조한 th id를 찾습니다.
네 기대 관계와 대조한 결과는 앞의 두 경우 불일치, 양방향 scope와 명시적 참조는 일치, 없는 ID는 불일치였습니다. colspan이 있는 표는 단순 격자 지원 범위를 넘어 보류했습니다. 이 결과는 실제 브라우저의 모든 추론 규칙이나 접근 가능한 이름을 재현한 검사가 아니라 명시한 구조 연결의 제한 검사입니다.
색이나 굵은 글자 대신 th로 역할을 표현합니다.
열 머리글에는 th와 scope=col을, 상품 이름 같은 행 머리글에는 th와 scope=row를 사용하는 구조를 생각할 수 있습니다. td에 굵은 글자 스타일만 적용하면 시각적 강조는 생기지만 같은 역할을 명시한 것은 아닙니다. CSS로 표를 바꿀 때도 HTML의 머리글 관계가 남는지 확인해야 합니다.
표의 처음 빈 칸도 무조건 비워 두지 말고 행 머리글의 종류를 설명할 수 있는지 봅니다. 이번 예제는 첫 열 이름을 상품으로 정했습니다. 사용자가 어느 차원에서 수치를 비교하는지 모르면 처음부터 잘못된 질문을 하게 됩니다. 읽는 순서와 선택 기준이 드러나도록 필요한 머리글은 짧고 일관되게 작성합니다.
caption은 표의 목적을 설명하고 셀 연결은 별도로 둡니다.
“분기별 자료 수량” 같은 caption은 표를 만났을 때 전체 목적을 알리는 데 도움이 됩니다. 그러나 caption 하나가 각 셀의 행·열 머리글을 대신하지는 않습니다. 두 번째 col-only 예제는 caption이 있어도 행 관계가 빠졌다고 표시했습니다. 서로 다른 역할을 한 요소로 해결하려고 하면 검수가 느슨해집니다.
표 위의 설명 문장과 caption이 완전히 같으면 중복 낭독이 될 수 있으므로 필요한 정보를 나눠 씁니다. 제목은 표가 답하는 질문을, 본문은 계산 기준이나 예외를, 머리글은 셀의 관계를 맡기면 됩니다. caption을 장식처럼 숨기거나 표 밖에 비슷한 문장을 적었다는 이유로 연결이 끝났다고 보지 않습니다.
입력별 결과를 편집 판단에 연결하기
다음 표의 결과는 제공 실행 파일에서 확인한 값이며, 해석과 다음 확인은 그 결과를 이용하는 편집 예시입니다. 추가 시험을 수행한 결과로 세지 않습니다.
| 입력·관찰 | 실행 결과 또는 선택 | 판단 근거 | 다음 확인 |
|---|---|---|---|
| plain-td | 네 데이터 셀에서 기대한 머리글 연결을 얻지 못했습니다. | 보기용 첫 행의 글자가 있어도 파서가 선언된 th 관계로 읽지는 않습니다. 모든 표준 브라우저의 추론 결과를 재현한 검사가 아닙니다. | 먼저 원시 표의 행·열 역할을 정한 뒤 필요한 머리글 마크업을 확인합니다. |
| column-only | 열 이름은 얻지만 상품의 행 관계가 빠집니다. | 12와 8이 모두 1분기라는 정보만으로는 어느 상품의 값인지 구분하기 어렵습니다. 이 표의 기대 관계에는 두 축이 필요합니다. | 행 머리글을 th scope=row로 연결하거나 적절한 명시적 참조 구조를 설계합니다. |
| scope-both | 행과 열의 scope로 네 셀의 관계가 일치했습니다. | 코드는 단순 격자의 같은 행과 같은 열에서 선언된 머리글을 모았습니다. 복잡한 병합 셀의 일반 연결 알고리즘은 아닙니다. | 표를 수정한 뒤에도 값과 두 머리글의 묶음이 유지되는지 대조합니다. |
| explicit-headers | headers가 가리키는 th id를 찾아 같은 네 관계를 얻었습니다. | scope 대신 명시한 ID 목록을 사용한 사례입니다. ID가 있어도 잘못된 셀을 참조하면 의미는 틀릴 수 있습니다. | 참조 ID의 존재뿐 아니라 기대한 행·열 이름인지 같이 확인합니다. |
| missing-header-id | missing이라는 참조를 찾지 못해 일치하지 않았습니다. | 자료집 2분기 셀에서 열 머리글 하나가 사라졌습니다. 표가 여전히 화면에 그려지는지와 의미 연결의 유효성은 별개입니다. | 템플릿에서 ID가 바뀐 곳과 해당 headers 속성을 함께 수정합니다. |
| complex-span | colspan이 나타나 supported=false로 보류했습니다. | 병합을 단순 격자로 억지 계산하면 잘못된 열 위치를 정상처럼 내보낼 수 있습니다. 미지원 범위를 실제 입력에서 분리했습니다. | WAI의 그룹·다층 머리글 패턴과 실제 보조기기 검증을 별도로 진행합니다. |
행을 카드 모양으로 바꿀 때 머리글을 잃지 않게 합니다.
모바일에서 표를 카드처럼 보이게 만드는 CSS를 적용할 수 있습니다. 이때 셀 앞에 data-label을 시각적으로 붙이는 방식과 HTML의 실제 머리글 관계는 다릅니다. 사용자가 화면에서 글자를 볼 수 있다는 사실만으로 모든 보조기기에서 같은 정보가 전달된다고 단정할 수 없습니다.
먼저 표로 유지한 채 가로 스크롤을 제공하는 방법과 요약 표·본문 설명으로 나누는 방법을 비교합니다. 스크롤 영역에 키보드로 들어가 끝까지 확인할 수 있는지, 확대 시 값과 머리글이 떨어지지 않는지도 봅니다. 이번 선언 모델은 모바일 CSS나 키보드 동작을 실행하지 않았으므로 실제 구현에서는 공개 화면을 별도로 확인해야 합니다.
병합 셀이나 다층 머리글은 이 작은 모델의 범위를 넘습니다.
연도 아래 여러 분기가 놓이거나 지역별로 행을 묶으면 셀 하나가 여러 단계의 머리글을 필요로 할 수 있습니다. WAI는 이런 경우 rowgroup·colgroup과 복잡한 표 연결을 별도로 설명합니다. 단순히 모든 th에 scope=col만 붙이는 일괄 치환으로 해결되지 않을 수 있습니다.
복잡한 표가 반드시 필요한지 먼저 묻는 것도 좋습니다. 비교 질문을 나눠 표 두 개로 만들면 관계가 더 단순해질 수 있습니다. 그러나 같은 값을 반복해 싣다가 서로 다르게 수정할 위험도 있으므로 원시 데이터는 하나로 관리합니다. 구조를 단순하게 만드는 목적은 검사 점수를 높이는 것이 아니라 독자가 필요한 조합을 찾기 쉽게 하는 것입니다.
헤더 관계와 숫자의 정확성은 각각 검수합니다.
자료집의 2분기 값 15가 올바른 머리글에 연결됐더라도 그 숫자가 실제 원자료와 맞는지는 다른 문제입니다. 반대로 수치가 정확해도 연결이 틀리면 다른 상품의 값처럼 읽힐 수 있습니다. 검수표에서는 원시 값 대조와 머리글 연결 대조를 다른 열로 두는 편이 낫습니다.
합계·평균이 들어 있으면 그 행이 비교값인지 요약값인지 설명하고 단위도 표시합니다. 12와 15를 단순히 오른쪽 정렬했다고 수량인지 금액인지 알 수는 없습니다. 단위를 셀마다 반복할지 머리글에 둘지 정하고 화면과 대체 접근 경로에서 모두 이해되는지 확인합니다. 이번 예제는 합계나 통계 계산을 포함하지 않습니다.
실제 구현에서는 네 셀을 직접 따라 읽어 봅니다.
기대 연관 목록은 자료집·1분기·12, 자료집·2분기·15, 체크리스트·1분기·8, 체크리스트·2분기·11입니다. HTML을 바꾼 뒤 각 조합이 유지되는지 확인합니다. 새 열을 추가하거나 행을 정렬해도 처음 적은 관계가 바뀌지 않아야 합니다. 셀 위치만 하드코딩한 테스트는 열이 이동하면 다른 뜻을 통과시킬 수 있습니다.
보조기기로 실제 시험할 때는 사용한 운영체제·브라우저·화면 낭독기와 설정을 기록합니다. 그 환경의 결과를 모든 제품의 동작으로 확대하지 않습니다. 사람이 표를 이해하는 데 걸린 시간이나 완료율을 측정하려면 별도 과업과 실험 설계가 필요합니다. 마크업 확인, 보조기기 동작, 사용자 이해는 서로 다른 증거입니다.
자료집의 2분기 15를 다른 행으로 정렬하는 편집을 했다면 “15”라는 값이 남았는지만 확인해서는 부족합니다. 원시 자료의 상품 ID와 분기 키를 기준으로 같은 관계가 유지되는지 대조해야 합니다. 제공 코드는 고정된 단순 표의 기대 관계를 순서대로 비교합니다. 정렬 가능한 운영 표를 검사할 때는 이 위치 기반 계약을 그대로 사용하지 말고 데이터 키를 포함한 기준을 준비합니다.
첨부 코드는 실제 단순 표 구조를 읽고 미지원 범위를 분리합니다.
파일의 expectedAssociations는 네 셀의 기대 관계이며 cases에는 원문 HTML과 파싱 결과가 있습니다. caption 글자, mapping, missingHeaderIds, supported를 각각 기록합니다. 이름만 바꾼 고정 성공 문구가 아니라 입력의 th·scope·headers·id를 읽어 계산했고 여섯 기대 상태를 assert로 대조했습니다.
보조기기 실행과 복잡한 병합 셀은 시험하지 않았습니다. 작은 표를 새로 만들 때 기대 관계부터 적으면 코드와 디자인이 바뀌어도 어떤 뜻을 보존해야 하는지 공유하기 쉽습니다. 접근성은 별도의 장식 문구를 덧붙이는 과정이 아니라 원래 정보를 여러 경로로 같은 목적에 맞게 전달하는 문제입니다. 눈에 보이는 선과 색을 검수할 때 관계와 이름도 같은 비중으로 살펴보세요.
누락 참조를 발견했다면 headers 값에서 없는 단어를 삭제하는 식으로 경고만 없애지 않습니다. 셀이 실제로 잃은 머리글이 무엇인지 확인한 뒤 올바른 th의 id를 연결합니다. 같은 id가 중복되는 경우도 따로 막아야 합니다. 이번 파서는 중복 ID 전수 검사나 접근 가능한 이름 계산을 구현하지 않았으므로 단순 연결 결과를 완전한 접근성 인증으로 제시하지 않습니다.
이 글의 입력·실행 자료
합성 자료의 실제 검증일은 2026-10-15입니다. 글의 예약 발행일과 구분합니다. 코드의 fixtureCreatedOn은 예제 작성일이고 executedAt은 재실행할 때의 실제 UTC 시각입니다. Python 3 표준 라이브러리를 사용합니다. 실제 고객정보나 새 Gemini 응답을 입력하지 않았습니다.
python3 accessible-table-header-mapping-lab.py확인한 공식 원문
2026-10-15에 열어 확인했습니다. 제공 예제의 규칙과 공식 문서의 일반 기능 설명을 구분해 읽습니다.



