본문으로 건너뛰기

UZU Dev LogoUZU Dev LogoUZU-DEV

표(Table) 구성하기

<table> 태그에 모든 것들을 정리하였다.

2026년 6월 25일

표(Table) 구성하기 썸네일

1. 표는 언제 써야 하나

1.1 올바른 용도: 표 형식 데이터(Tabular Data)

<table> 은 행(row)과 열(column)의 관계가 의미 있는 데이터를 표현할 때 쓴다.

  • 스프레드시트처럼 행과 열이 교차하는 데이터
  • 비교표 (기능 A vs B vs C)
  • 시간표, 일정표
  • 통계 데이터
1.2 잘못된 용도: 레이아웃

1990년대~2000년대 초반엔 페이지 전체 레이아웃을 <table> 로 짜는 것이 일반적이었다. 현재는 레이아웃 목적의 <table> 사용은 잘못된 관행이다.

  • 스크린 리더가 표 데이터로 인식해 "4개 열, 20개 행의 표"처럼 잘못 안내한다.
  • 반응형 웹을 만들기 극히 어렵다.
  • 레이아웃은 CSS Flexbox 또는 Grid로 구현한다.

2. 표의 기본 구조

2.1 전체 골격
<table>
  <caption>월별 스터디 현황</caption>

  <colgroup>
    <col>
    <col span="2" class="highlight-col">
  </colgroup>

  <thead>
    <tr>
      <th scope="col">월</th>
      <th scope="col">공부 시간</th>
      <th scope="col">완료 챕터</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <th scope="row">6월</th>
      <td>42시간</td>
      <td>9편</td>
    </tr>
    <tr>
      <th scope="row">7월</th>
      <td>38시간</td>
      <td>11편</td>
    </tr>
  </tbody>

  <tfoot>
    <tr>
      <th scope="row">합계</th>
      <td>80시간</td>
      <td>-</td>
    </tr>
  </tfoot>
</table>

3. <table> 의 구성 요소들

3.1 <caption>: 표의 제목
<table>
  <caption>2026년 상반기 스터디 현황</caption>
  ...
</table>
  • <caption> 은 <table> 안에서 가장 먼저 와야 한다. <thead> 보다 앞에 위치한다.
  • 기본적으로 표 위에 표시되며, CSS caption-side: bottom 으로 아래로 옮길 수 있다.
  • 스크린 리더는 표를 읽기 전에 <caption> 을 먼저 읽어 표의 맥락을 안내한다.
  • HTML5에서 <table> 의 summary 속성은 폐기(deprecated) 됐다. 표에 대한 설명이 필요하면 <caption> 또는 aria-describedby 를 사용한다.
3.2 <thead>: 머리글 영역
  • 표의 열 제목 행을 담는 그룹이다.
  • 긴 표를 인쇄할 때 각 페이지마다 머리글이 반복되도록 브라우저가 처리한다.
  • 안에 <tr> 과 <th> 요소를 담는다.
3.3 <tbody>: 본문 데이터 영역
  • 표의 실제 데이터 행을 담는 그룹이다.
  • 생략해도 브라우저가 자동으로 추가하지만, 명시적으로 쓰는 것이 좋다.
  • <table> 안에 여러 <tbody> 를 써서 데이터를 논리적으로 그룹화할 수 있다.
3.4 <tfoot>: 바닥글 영역
  • 합계, 평균, 총계처럼 표의 요약·결론 행을 담는 그룹이다.
  • <tfoot> 은 반드시 <tbody> 뒤에 위치해야 한다. HTML4에서는 <tbody> 앞에 두는 것이 허용됐지만, HTML5 Living Standard는 <tfoot> 이 <tbody> 다음에 와야 한다고 규정한다.
<table>
  <caption>표 제목</caption>   ← 1순위
  <colgroup>...</colgroup>     ← 2순위 (선택)
  <thead>...</thead>           ← 3순위 (선택)
  <tbody>...</tbody>           ← 4순위
  <tfoot>...</tfoot>           ← 5순위 (선택, tbody 뒤에)
</table>
3.5 <colgroup> 과 <col>: 열 스타일 지정

특정 열 전체에 CSS를 적용할 때 쓴다. <col> 요소를 열 순서대로 배치한다.

<table>
  <colgroup>
    <col>                          <!-- 1열: 스타일 없음 -->
    <col class="highlight-col">    <!-- 2열 -->
    <col span="2" class="data-cols"> <!-- 3~4열: 한 번에 지정 -->
  </colgroup>
  ...
</table>

<col> 에는 CSS의 일부 속성만 적용된다: border, background, width, visibility. 대부분의 다른 CSS 속성은 <col> 을 통해 열 전체에 적용하는 것이 지원되지 않는다.


4. <tr>, <th>, <td>: 행과 셀

4.1 <tr>: 테이블 행(Row)
<tbody>
  <tr>
    <td>1행 1열</td>
    <td>1행 2열</td>
  </tr>
  <tr>
    <td>2행 1열</td>
    <td>2행 2열</td>
  </tr>
</tbody>

<tr> 은 반드시 <thead>, <tbody>, <tfoot> 안에 위치해야 한다.

4.2 <th>: 헤더 셀 vs <td>: 데이터 셀
<th><td>
의미헤더 셀 (제목·레이블)데이터 셀
기본 스타일굵게(bold), 가운데 정렬보통 굵기, 왼쪽 정렬
스크린 리더데이터 셀의 문맥 정보로 읽힘<th> 를 참조해 읽힘

<th> 는 <thead> 안에서 열 제목으로만 쓰이는 것이 아니라, <tbody> 의 각 행에서 행 제목(row header) 으로도 쓸 수 있다.

4.3 scope 속성: 헤더 셀의 적용 범위

scope 속성은 <th> 가 어느 방향의 셀들에 대한 헤더인지 명시한다. 스크린 리더가 데이터 셀을 읽을 때 어느 헤더에 속하는지 올바르게 안내하기 위한 접근성 속성이다.

scope 값의미
col이 <th> 는 같은 열의 데이터 셀들에 대한 헤더
row이 <th> 는 같은 행의 데이터 셀들에 대한 헤더
colgroup이 <th> 는 <colgroup> 으로 묶인 열 그룹의 헤더
rowgroup이 <th> 는 <thead>·<tbody>·<tfoot> 행 그룹의 헤더
<table>
  <thead>
    <tr>
      <th scope="col">과목</th>    <!-- 열 헤더 -->
      <th scope="col">점수</th>
      <th scope="col">등급</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">HTML</th>   <!-- 행 헤더 -->
      <td>95</td>
      <td>A</td>
    </tr>
    <tr>
      <th scope="row">CSS</th>    <!-- 행 헤더 -->
      <td>88</td>
      <td>B+</td>
    </tr>
  </tbody>
</table>

스크린 리더는 "88" 셀을 읽을 때 "CSS 점수 88"처럼 행 헤더와 열 헤더를 함께 안내한다.


5. colspan 과 rowspan: 셀 병합

5.1 colspan: 열 방향 병합

colspan="n" 은 해당 셀이 가로로 n개의 열을 차지하게 한다.

<table>
  <thead>
    <tr>
      <th scope="col">이름</th>
      <th scope="col" colspan="2">연락처</th>   <!-- 2열 병합 -->
    </tr>
    <tr>
      <th scope="col">-</th>
      <th scope="col">전화</th>
      <th scope="col">이메일</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>장유주</td>
      <td>010-0000-0000</td>
      <td>uzu@example.com</td>
    </tr>
  </tbody>
</table>

렌더링 결과:

┌────────┬──────────────────────────────┐
│ 이름   │          연락처              │  ← colspan=2
├────────┼──────────────┬───────────────┤
│  -     │     전화     │    이메일     │
├────────┼──────────────┼───────────────┤
│ 장유주 │010-0000-0000 │uzu@example.com│
└────────┴──────────────┴───────────────┘
5.2 rowspan: 행 방향 병합

rowspan="n" 은 해당 셀이 세로로 n개의 행을 차지하게 한다.

<table>
  <tbody>
    <tr>
      <th scope="rowgroup" rowspan="2">오전</th>   <!-- 2행 병합 -->
      <td>09:00 HTML 기초</td>
    </tr>
    <tr>
      <!-- "오전" 셀이 이미 이 행을 차지하므로 <td>를 하나 쓰지 않는다 -->
      <td>10:00 CSS 기초</td>
    </tr>
    <tr>
      <th scope="rowgroup" rowspan="2">오후</th>
      <td>13:00 JavaScript</td>
    </tr>
    <tr>
      <td>14:00 프로젝트</td>
    </tr>
  </tbody>
</table>

rowspan·colspan 사용 시 핵심 규칙

셀이 n행 또는 n열을 차지하면, 이후 해당 위치의 <tr> 에서는 그만큼 <td>·<th> 를 하나 줄여서 써야 한다. 빠진 셀의 자리는 앞 행의 병합 셀이 차지하고 있기 때문이다. 이 규칙을 어기면 열 수가 맞지 않아 표가 틀어진다.

5.3 colspan 과 rowspan 동시 사용

두 속성을 함께 쓰면 셀이 직사각형 블록을 차지하게 할 수 있다.

<td colspan="3" rowspan="2">
  이 셀은 3열 × 2행 영역을 차지한다.
</td>

6. 표 꾸미기 기초 (CSS)

6.1 border-collapse

표 테두리 처리 방식을 결정하는 가장 기본적인 CSS 속성이다.

/* separate(기본값): 각 셀의 테두리가 분리됨 → 이중 선처럼 보임 */
table { border-collapse: separate; }

/* collapse: 인접한 테두리가 하나로 합쳐짐 → 깔끔한 단일 선 */
table { border-collapse: collapse; }

실무에서는 대부분 border-collapse: collapse 를 기본으로 쓴다.

6.2 줄무늬(Zebra Striping)

:nth-child() 가상 클래스로 홀수·짝수 행에 교대로 배경색을 넣어 가독성을 높인다.

tbody tr:nth-child(even) {
  background-color: #f9fafb;
}

tbody tr:hover {
  background-color: #eff6ff;
}
6.3 table-layout
/* auto(기본값): 셀 내용에 따라 열 너비 자동 계산 (렌더링 느림) */
table { table-layout: auto; }

/* fixed: 첫 행의 너비 또는 col 설정에 따라 고정 (렌더링 빠름) */
table {
  table-layout: fixed;
  width: 100%;  /* fixed에서는 table 너비 명시가 중요 */
}

table-layout: fixed 는 표가 크거나 데이터가 많을 때 렌더링 성능을 개선한다. 전체 내용을 다 읽지 않고도 열 너비를 바로 계산할 수 있기 때문이다.

6.4 caption-side
/* top(기본값): caption이 표 위에 */
table { caption-side: top; }

/* bottom: caption이 표 아래에 */
table { caption-side: bottom; }
6.5 실전 표 기본 스타일
table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

caption {
  caption-side: top;
  text-align: left;
  font-weight: bold;
  margin-bottom: 0.5rem;
}

th, td {
  padding: 0.75rem 1rem;
  text-align: left;
  border-bottom: 1px solid #e5e7eb;
}

thead th {
  background-color: #f3f4f6;
  font-weight: 600;
}

tbody tr:nth-child(even) {
  background-color: #f9fafb;
}

tbody tr:hover {
  background-color: #eff6ff;
}

7. [실습] 셀 병합으로 스터디 시간표 만들기

7.1 완성 목표

colspan 과 rowspan 을 모두 활용한 주간 스터디 시간표를 만든다.

┌───────────┬───────────┬───────────┬───────────┬───────────┬───────────┐
│   시간    │  월요일   │  화요일   │  수요일   │  목요일   │  금요일   │
├───────────┼───────────┴───────────┼───────────┴───────────┼───────────┤
│09:00~11:00│     HTML 기초 (×2)    │     CSS 기초 (×2)     │ JS 기초   │
│           │  colspan=2, rowspan=2 │  colspan=2, rowspan=2 │ rowspan=2 │
│10:00~11:00│           (병합됨)   │           (병합됨)    │ (병합됨)  │
├───────────┼───────────────────────────────────────────────────────────┤
│11:00~13:00│               점심 · 휴식  (colspan=5)                   │
├───────────┼───────────────────────────────┬───────────────────────────┤
│13:00~14:00│  프로젝트 실습  (colspan=3)   │   복습 & Q&A (colspan=2)  │
│           │           rowspan=2            │          rowspan=2        │
│14:00~15:00│           (병합됨)            │          (병합됨)         │
└───────────┴───────────────────────────────┴───────────────────────────┘
7.2 병합 설계 분석

각 행을 만들기 전에 어떤 셀이 어디서 시작하고 얼마나 차지하는지 미리 파악한다.

행시간 열월화수목금
1행09:00~10:00HTML 기초 colspan=2 rowspan=2← (병합)CSS 기초 colspan=2 rowspan=2← (병합)JS 기초 rowspan=2
2행10:00~11:00↑(병합)-↑(병합)-↑(병합)
3행11:00~13:00점심·휴식 colspan=5----
4행13:00~14:00프로젝트 실습 colspan=3 rowspan=2--복습 & Q&A colspan=2 rowspan=2-
5행14:00~15:00↑(병합)--↑(병합)-
7.3 완성 코드
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title>스터디 시간표</title>
  <style>
    table {
      width: 100%;
      border-collapse: collapse;
      table-layout: fixed;
    }

    caption {
      caption-side: top;
      font-size: 1.2rem;
      font-weight: bold;
      text-align: left;
      margin-bottom: 0.75rem;
    }

    th, td {
      padding: 0.75rem 1rem;
      border: 1px solid #d1d5db;
      text-align: center;
      vertical-align: middle;
    }

    thead th {
      background-color: #1e3a5f;
      color: white;
    }

    /* 시간 열 */
    tbody th {
      background-color: #f3f4f6;
      font-weight: 600;
      white-space: nowrap;
    }

    /* 과목별 색상 */
    .html    { background-color: #dbeafe; }
    .css     { background-color: #d1fae5; }
    .js      { background-color: #fef3c7; }
    .lunch   { background-color: #f5f5f5; color: #6b7280; }
    .project { background-color: #ede9fe; }
    .review  { background-color: #fce7f3; }
  </style>
</head>
<body>

<table>
  <caption>2026년 7월 스터디 시간표</caption>

  <colgroup>
    <col style="width: 14%">   <!-- 시간 열 -->
    <col style="width: 17.2%"> <!-- 월 -->
    <col style="width: 17.2%"> <!-- 화 -->
    <col style="width: 17.2%"> <!-- 수 -->
    <col style="width: 17.2%"> <!-- 목 -->
    <col style="width: 17.2%"> <!-- 금 -->
  </colgroup>

  <thead>
    <tr>
      <th scope="col">시간</th>
      <th scope="col">월요일</th>
      <th scope="col">화요일</th>
      <th scope="col">수요일</th>
      <th scope="col">목요일</th>
      <th scope="col">금요일</th>
    </tr>
  </thead>

  <tbody>

    <!-- 1행: 09:00~10:00 -->
    <!-- HTML은 월·화 2열 + 2행 병합 / CSS는 수·목 2열 + 2행 병합 / JS는 2행 병합 -->
    <tr>
      <th scope="row">09:00~10:00</th>
      <td class="html" colspan="2" rowspan="2">
        <strong>HTML 기초</strong><br>
        <small>문서 구조 · 시맨틱 태그</small>
      </td>
      <td class="css" colspan="2" rowspan="2">
        <strong>CSS 기초</strong><br>
        <small>선택자 · 박스 모델</small>
      </td>
      <td class="js" rowspan="2">
        <strong>JS 기초</strong><br>
        <small>변수 · 함수</small>
      </td>
    </tr>

    <!-- 2행: 10:00~11:00 -->
    <!-- 앞 행의 colspan=2 rowspan=2 · rowspan=2 셀들이 이 행을 차지함 -->
    <!-- th(시간)만 새로 쓰고 나머지 td는 생략 -->
    <tr>
      <th scope="row">10:00~11:00</th>
      <!-- 5개 열 모두 rowspan으로 병합됨 → td 없음 -->
    </tr>

    <!-- 3행: 점심·휴식 (5열 전체 병합) -->
    <tr>
      <th scope="row">11:00~13:00</th>
      <td class="lunch" colspan="5">🍱 점심 · 휴식</td>
    </tr>

    <!-- 4행: 13:00~14:00 -->
    <!-- 프로젝트: 월·화·수 3열 + 2행 병합 / 복습: 목·금 2열 + 2행 병합 -->
    <tr>
      <th scope="row">13:00~14:00</th>
      <td class="project" colspan="3" rowspan="2">
        <strong>팀 프로젝트 실습</strong><br>
        <small>HTML + CSS 결합</small>
      </td>
      <td class="review" colspan="2" rowspan="2">
        <strong>복습 &amp; Q&amp;A</strong><br>
        <small>오류 수정 · 질의응답</small>
      </td>
    </tr>

    <!-- 5행: 14:00~15:00 -->
    <!-- 앞 행의 colspan·rowspan 셀들이 이 행을 차지함 -->
    <tr>
      <th scope="row">14:00~15:00</th>
      <!-- 5개 열 모두 rowspan으로 병합됨 → td 없음 -->
    </tr>

  </tbody>

  <tfoot>
    <tr>
      <th scope="row">일일 합계</th>
      <td colspan="5">총 5시간 (오전 2h + 오후 2h)</td>
    </tr>
  </tfoot>

</table>

</body>
</html>
7.4 코드 핵심 포인트
  • 병합된 셀의 자리에는 <td> 를 쓰지 않는다. 2행과 5행에서 <th scope="row"> 만 쓰고 <td> 를 0개 쓴 이유다.
  • colspan + rowspan 동시 사용 시 그 셀이 차지하는 직사각형 영역 전체를 이후 행에서 비워야 한다.
  • &amp; 는 & 의 HTML 엔티티. "복습 & Q&A" 처럼 & 기호를 HTML 안에 직접 쓸 때는 인코딩한다.
  • <colgroup> + <col> 으로 각 열의 너비를 미리 지정해 table-layout: fixed 와 함께 렌더링 성능을 높였다.