
이 글에서 만드는 것
caption과 th scope로 비교 관계를 표현하고, 모바일에서 표의 의미를 유지하며 키보드로 가로 이동하는 HTML 비교표를 완성합니다.

1. 표가 필요한 데이터인지 먼저 판단합니다
이 예제는 이용 유형마다 시간·요금·제공 항목을 같은 기준으로 비교합니다. 열을 따라 읽는 것이 의미 있으므로 table을 사용합니다. 문장 세 개나 카드 세 개를 배치하는 데 표를 쓰는 것은 다른 문제입니다. 먼저 종이에 행은 “이용 유형”, 열은 “비교 기준”이라고 적어 보세요. 둘의 교차점이 데이터 셀이 됩니다.
2. caption과 th는 서로 다른 역할입니다
caption은 표 전체의 이름입니다. 페이지 제목 h1이 있다고 해서 표 이름까지 자동으로 정해지는 것은 아닙니다. th는 각 데이터 셀이 무엇을 뜻하는지 알려 주는 머리글이고, scope=”col”은 같은 열을, scope=”row”는 같은 행을 가리킵니다. W3C는 단순한 행·열 머리글 표에서 이 관계를 명시하는 방법을 설명합니다. 병합 셀이 많은 복잡한 표는 이 예제의 범위를 넘어섭니다.
다음은 차이만 보여 주는 부분 코드입니다. 전체 파일에 그대로 덧붙이지 마세요.
<!-- 의미가 없는 데이터 셀 -->
<td>팀 연습</td>
<!-- 행 전체의 이름을 나타내는 머리글 -->
<th scope="row">팀 연습</th>
3. 좁은 화면에서는 표 바깥 상자만 움직입니다
table에 최소 폭 36rem을 주면 글자를 억지로 한 글자씩 꺾지 않고 비교 관계를 유지할 수 있습니다. 대신 바깥 .table-scroll에 overflow-x: auto를 지정합니다. 표가 넓어지는 것과 페이지 전체가 넓어지는 것은 다릅니다. 본문 문장은 화면 폭 안에서 줄바꿈하고, 표 영역에서만 가로 이동해야 합니다. 각 행이나 셀의 display를 block으로 바꾸지 않습니다.
WCAG 리플로 설명은 의미상 2차원 배치가 필요한 데이터 표에 국소적인 양방향 스크롤을 허용하는 예외를 설명합니다. 예외가 표 내부 글자 잘림이나 페이지 전체 가로 넘침을 정당화하는 것은 아닙니다.
공식 근거: WCAG 2.2 리플로 설명과 데이터 표 예외
4. 스크롤 영역에도 이름과 키보드 진입점을 둡니다
tabindex=”0″은 일반 Tab 순서에 표의 바깥 영역을 넣습니다. role=”region”과 aria-labelledby는 그 영역을 표 제목으로 식별하고 aria-describedby는 가로 이동 안내를 연결합니다. 양수 tabindex로 순서를 새로 만들지 않습니다. 브라우저에서 키보드 초점이 들어온 다음 방향키로 이동되는지 직접 확인하세요. 화면 읽기 프로그램이 켜져 있으면 방향키를 문서 탐색에 사용할 수 있으므로 그때의 읽기·조작 검증은 별도로 진행합니다.
5. starter에서 고치고 결과를 비교합니다
- ZIP을 풀고 starter/index.html을 브라우저로 엽니다. 두 행의 값을 먼저 읽어 봅니다.
- caption, thead, tbody를 추가한 뒤 첫 행과 첫 열의 td를 목적에 맞는 th로 바꿉니다.
- complete와 같은 스크롤 바깥 요소를 만들고 CSS를 적용합니다.
- 발표 연습 행을 추가합니다. 새 행에서도 이용 유형이 scope=”row”인지 확인합니다.
- 데스크톱과 320 CSS 픽셀 폭에서 확인합니다. 본문이 잘리지 않고 표 내부에서만 움직이는지 살펴봅니다.
6. 자주 실패하는 지점
- 모든 셀을 th로 만들면 데이터와 머리글 구분이 흐려집니다. 이름 셀만 th로 지정하세요.
- caption을 table 밖에 두면 표 제목 관계가 만들어지지 않습니다. table의 첫 자식으로 넣습니다.
- body에 overflow-x: hidden을 주면 넘침을 고치지 않고 가릴 수 있습니다. 표 바깥 상자의 폭과 overflow부터 봅니다.
- 스크롤 상자에 outline: none을 넣으면 현재 위치를 놓치기 쉽습니다. 예제의 :focus-visible 표시를 유지합니다.
7. 완료 기준과 추가 과제
현재 파일에 표 제목 한 개, 네 개의 열 머리글, 세 개의 행 머리글이 있는지 확인하세요. Tab으로 표에 들어가고 다시 Tab으로 다음 링크에 나올 수 있어야 합니다. 가능한 환경에서는 화면 읽기 프로그램의 표 탐색 명령으로 요금 셀의 이용 유형과 열 이름을 함께 확인합니다. 이 읽기 결과는 일반 브라우저 키보드 확인만으로 대신할 수 없습니다.
추가 과제는 “예약 가능 요일” 열 추가입니다. 데이터와 열 머리글을 같은 순서에 넣고 좁은 화면에서 끝 열까지 접근되는지 다시 확인하세요. 이 샘플에 없는 복잡한 rowspan·colspan부터 추가하지 말고 단순한 비교 관계를 먼저 완성해 보세요.
완성 코드: complete/index.html 전체
아래 코드는 부분 예제가 아니라 HTML 파일 전체입니다. 같은 폴더에 이어지는 style.css를 저장하세요.
실습 파일
파일 경로를 확인하고 같은 프로젝트 안에 저장하세요. 이미지 등 소스 목록에 없는 파일과 실행 안내는 실습 ZIP에 포함되어 있습니다.
표 제목과 헤더 관계를 보완하고 좁은 화면에서는 표 영역만 스크롤합니다. 수정 전은 시작 상태, 수정 후는 비교할 완성 상태입니다.
수정 전
index.html
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
HTML 표 만들기: caption·scope로 비교표를 읽기 쉽게, 모바일에서는 표만 스크롤하기
</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main>
<h1>연습 공간 요금 비교</h1>
<p>가상 정보입니다. 아래 표에 제목과 머리글의 방향을 추가하세요.</p>
<!-- TODO 1: caption을 추가하세요. -->
<!-- TODO 2: 첫 행은 열 머리글, 첫 열은 행 머리글로 바꾸세요. -->
<!-- TODO 3: 표 바깥에 이름과 tabindex가 있는 가로 스크롤 영역을 만드세요. -->
<table>
<tr>
<td>이용 유형</td>
<td>기본 시간</td>
<td>가상 요금</td>
<td>포함 항목</td>
</tr>
<tr>
<td>개인 연습</td>
<td>1시간</td>
<td>10,000원</td>
<td>책상 1개, 의자 1개</td>
</tr>
<tr>
<td>팀 연습</td>
<td>2시간</td>
<td>30,000원</td>
<td>책상 1개, 의자 4개</td>
</tr>
</table>
</main>
</body>
</html>
style.css
* {
box-sizing: border-box;
}
html {
color: #222;
background: #f1f1f1;
font-family: system-ui, sans-serif;
}
body {
margin: 0;
line-height: 1.7;
}
input {
accent-color: #555;
}
main,
.site-header,
.site-footer {
width: min(70rem, 100%);
margin-inline: auto;
padding: 1.5rem;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
a {
color: #222;
text-underline-offset: 0.2em;
}
:focus-visible {
outline: 3px solid #111;
outline-offset: 3px;
}
section {
margin-block: 2rem;
}
.notice {
padding: 1rem;
background: #e5e5e5;
border: 1px solid #666;
}
table {
width: 100%;
border-collapse: collapse;
}
td {
border: 1px solid #777;
padding: 0.75rem;
}
수정 방법
index.html
index.html에서 아래 차이가 있는 위치를 수정하세요. -는 삭제할 줄, +는 추가할 줄이며 표시는 실제 코드에 넣지 않습니다.
표 제목과 헤더 관계를 보완하고 좁은 화면에서는 표 영역만 스크롤합니다.
--- 수정 전/index.html
+++ 수정 후/index.html
@@ -11,30 +11,60 @@
<body>
<main>
<h1>연습 공간 요금 비교</h1>
- <p>가상 정보입니다. 아래 표에 제목과 머리글의 방향을 추가하세요.</p>
- <!-- TODO 1: caption을 추가하세요. -->
- <!-- TODO 2: 첫 행은 열 머리글, 첫 열은 행 머리글로 바꾸세요. -->
- <!-- TODO 3: 표 바깥에 이름과 tabindex가 있는 가로 스크롤 영역을 만드세요. -->
- <table>
- <tr>
- <td>이용 유형</td>
- <td>기본 시간</td>
- <td>가상 요금</td>
- <td>포함 항목</td>
- </tr>
- <tr>
- <td>개인 연습</td>
- <td>1시간</td>
- <td>10,000원</td>
- <td>책상 1개, 의자 1개</td>
- </tr>
- <tr>
- <td>팀 연습</td>
- <td>2시간</td>
- <td>30,000원</td>
- <td>책상 1개, 의자 4개</td>
- </tr>
- </table>
+ <p>모든 금액과 조건은 HTML 표 연습을 위한 가상 정보입니다.</p>
+ <p id="scroll-help">
+ 표가 화면보다 넓으면 표 영역을 선택한 뒤 좌우 방향키로 이동하세요. 터치로도
+ 가로로 움직일 수 있습니다.
+ </p>
+ <div
+ class="table-scroll"
+ role="region"
+ aria-labelledby="price-caption"
+ aria-describedby="scroll-help"
+ tabindex="0"
+ >
+ <table>
+ <caption id="price-caption">
+ 연습 공간 1회 이용 요금과 제공 항목
+ </caption>
+ <thead>
+ <tr>
+ <th scope="col">이용 유형</th>
+ <th scope="col">기본 시간</th>
+ <th scope="col">가상 요금</th>
+ <th scope="col">포함 항목</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th scope="row">개인 연습</th>
+ <td>1시간</td>
+ <td>10,000원</td>
+ <td>책상 1개, 의자 1개</td>
+ </tr>
+ <tr>
+ <th scope="row">팀 연습</th>
+ <td>2시간</td>
+ <td>30,000원</td>
+ <td>책상 1개, 의자 4개</td>
+ </tr>
+ <tr>
+ <th scope="row">발표 연습</th>
+ <td>2시간</td>
+ <td>40,000원</td>
+ <td>책상 1개, 의자 4개, 발표 화면</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <section id="conditions">
+ <h2>비교표 읽는 방법</h2>
+ <p>
+ 행 제목에서 이용 유형을 고르고 열 제목에서 비교할 항목을 찾습니다. 요금만 떼어
+ 읽으면 이용 시간이 다르다는 조건을 놓칠 수 있습니다.
+ </p>
+ <a href="#scroll-help">표 이동 안내로 돌아가기</a>
+ </section>
</main>
</body>
</html>
수정 후 탭의 전체 파일과 비교한 뒤 본문의 확인 절차를 실행하세요.
style.css
style.css에서 아래 차이가 있는 위치를 수정하세요. -는 삭제할 줄, +는 추가할 줄이며 표시는 실제 코드에 넣지 않습니다.
표 제목과 헤더 관계를 보완하고 좁은 화면에서는 표 영역만 스크롤합니다.
--- 수정 전/style.css
+++ 수정 후/style.css
@@ -42,11 +42,31 @@
background: #e5e5e5;
border: 1px solid #666;
}
+.table-scroll {
+ max-width: 100%;
+ overflow-x: auto;
+ border: 1px solid #666;
+ padding: 0.5rem;
+}
table {
width: 100%;
+ min-width: 36rem;
border-collapse: collapse;
+ background: #fff;
}
+caption {
+ text-align: left;
+ font-weight: 700;
+ padding: 0.75rem;
+}
+th,
td {
border: 1px solid #777;
padding: 0.75rem;
+ text-align: left;
+ vertical-align: top;
}
+thead,
+tbody th {
+ background: #e5e5e5;
+}
수정 후 탭의 전체 파일과 비교한 뒤 본문의 확인 절차를 실행하세요.
수정 후
index.html
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
HTML 표 만들기: caption·scope로 비교표를 읽기 쉽게, 모바일에서는 표만 스크롤하기
</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main>
<h1>연습 공간 요금 비교</h1>
<p>모든 금액과 조건은 HTML 표 연습을 위한 가상 정보입니다.</p>
<p id="scroll-help">
표가 화면보다 넓으면 표 영역을 선택한 뒤 좌우 방향키로 이동하세요. 터치로도
가로로 움직일 수 있습니다.
</p>
<div
class="table-scroll"
role="region"
aria-labelledby="price-caption"
aria-describedby="scroll-help"
tabindex="0"
>
<table>
<caption id="price-caption">
연습 공간 1회 이용 요금과 제공 항목
</caption>
<thead>
<tr>
<th scope="col">이용 유형</th>
<th scope="col">기본 시간</th>
<th scope="col">가상 요금</th>
<th scope="col">포함 항목</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">개인 연습</th>
<td>1시간</td>
<td>10,000원</td>
<td>책상 1개, 의자 1개</td>
</tr>
<tr>
<th scope="row">팀 연습</th>
<td>2시간</td>
<td>30,000원</td>
<td>책상 1개, 의자 4개</td>
</tr>
<tr>
<th scope="row">발표 연습</th>
<td>2시간</td>
<td>40,000원</td>
<td>책상 1개, 의자 4개, 발표 화면</td>
</tr>
</tbody>
</table>
</div>
<section id="conditions">
<h2>비교표 읽는 방법</h2>
<p>
행 제목에서 이용 유형을 고르고 열 제목에서 비교할 항목을 찾습니다. 요금만 떼어
읽으면 이용 시간이 다르다는 조건을 놓칠 수 있습니다.
</p>
<a href="#scroll-help">표 이동 안내로 돌아가기</a>
</section>
</main>
</body>
</html>
style.css
* {
box-sizing: border-box;
}
html {
color: #222;
background: #f1f1f1;
font-family: system-ui, sans-serif;
}
body {
margin: 0;
line-height: 1.7;
}
input {
accent-color: #555;
}
main,
.site-header,
.site-footer {
width: min(70rem, 100%);
margin-inline: auto;
padding: 1.5rem;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
a {
color: #222;
text-underline-offset: 0.2em;
}
:focus-visible {
outline: 3px solid #111;
outline-offset: 3px;
}
section {
margin-block: 2rem;
}
.notice {
padding: 1rem;
background: #e5e5e5;
border: 1px solid #666;
}
.table-scroll {
max-width: 100%;
overflow-x: auto;
border: 1px solid #666;
padding: 0.5rem;
}
table {
width: 100%;
min-width: 36rem;
border-collapse: collapse;
background: #fff;
}
caption {
text-align: left;
font-weight: 700;
padding: 0.75rem;
}
th,
td {
border: 1px solid #777;
padding: 0.75rem;
text-align: left;
vertical-align: top;
}
thead,
tbody th {
background: #e5e5e5;
}
완성 코드: complete/style.css 전체
이 글이 도움이 되었나요?
웹 퍼블리싱 학습 순서
필수 13개 · 전체 15개
읽음 기록 관리
전체 과정 목차 (15개)
- 필수 학습 · HTML 입문: 시맨틱 문서와 접근 가능한 폼 만들기
- 필수 학습 · CSS 입문: 박스 모델·선택자·캐스케이드 직접 확인하기
- 필수 학습 · CSS Flex와 미디어 쿼리 입문: 반응형 카드 페이지 만들기
- 필수 학습 · CSS position 완전 이해: relative·absolute·fixed·sticky 실습
- 필수 학습 · HTML details와 summary로 접근 가능한 FAQ 만들기
- 필수 학습 · CSS Grid 실습: Flex 학습 카드로 열 맞추기와 auto-fit 비교
- 필수 학습 · CSS overflow 해결: 긴 텍스트·min-width: 0·말줄임 실습
- 필수 학습 · HTML 표 만들기: caption·scope로 비교표를 읽기 쉽게, 모바일에서는 표만 스크롤하기 현재 글
- 필수 선수 · HTML CSS 이미지 문제 해결 실습: 경로·비율·잘림·흐림·CLS
- 필수 학습 · Container Query 실습: 같은 카드를 본문과 사이드바에 배치하기
- 선택 참고 · CSS Baseline 보는 법: 지원 범위와 폴백을 직접 확인하기
- 필수 학습 · CSS z-index가 안 먹힐 때: stacking context와 부모 층 디버깅
- 필수 선수 · 웹 접근성 기초 점검: 키보드 순서·초점 표시·확대 화면을 직접 확인하는 체크리스트
- 필수 학습 · HTML CSS 종합 실습: 소개·카드·이미지·요금표·문의 폼·FAQ가 있는 반응형 서비스 페이지
- 선택 참고 · 프론트엔드 SEO 기초: HTML 구조와 이미지 성능 진단하기
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.
BlogFlow AI
이 글과 함께 공부하기
무엇을 도와드릴까요?
읽다가 막힌 부분을 물어보세요. 이 글을 바탕으로 설명하고, 이어서 읽을 본문을 연결합니다.