HTML 테이블(Table)의 기본 구조

HTML 테이블 기본 구조

[ CSS 목차보기 ]

웹페이지에서 테이블을 그리는 방법으로는 HTML 테이블CSS 테이블 2가지 방법이 있다. 이번 포스트에서는 HTML 테이블에 대해서 알아볼 것이다. 보통 HTML 테이블을 그리면, 생각한 것과 달리 예쁘지 못한 표가 나오는데, 이번 포스트와 관련 포스트들에서는 테이블의 작동원리를 심도있게 다뤄서, 어떻게 하면 예쁜 테이블을 그릴 수 있는지를 알아 볼 것이다. 이번 포스트에서는 가장 기본이 될 수 있는 HTML 테이블의 기본 구조에 대해서 알아보고, tbody를 생략할 때 발생하는 문제점들에 대해서 알아 볼 것이다.

[1] 테이블 기본 구조

테이블을 제대로 사용하려면, 기본 구조부터 정확히 이해해야 한다. 아래 Figure 1의 첫 번째 그림은 테이블의 계층 구조를 보여주고, 두 번째 그림은 이런 계층 구조를 이용하여 만든 실제 테이블 코드를 보여준다. table 관련 요소들은 부모-자식의 계층 구조를 가지는데, 최외각에는 <table></table>이 위치한다. 그 밑으로는 <caption>, <thead>, <tbody>, <tfoot> 요소들이 위치하고, <thead>, <tbody>, <tfoot> 요소들 밑으로는 테이블 행에 해당하는 <tr></tr>이 위치한다. <tr></tr> 안에는 테이블 셀에 해당하는 <th></th> 또는 <td></td>가 위치하게 된다.

< table >
< caption >
 
< thead >
< tr >
< th >
 
< tbody >
< tr >
< td >
 
< tfoot >
< tr >
< td >
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<table>
    <caption>영수증</caption>
    <thead>
        <tr>
            <th>메뉴</th>
            <th>가격</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>피자</td>
            <td>12,000</td>
        </tr>
        <tr>
            <td>스파게티</td>
            <td>8,000</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td>총액</td>
            <td>20,000</td>
        </tr>
    </tfoot>
</table>
cs
Figure 1. HTML 테이블의 기본 구조

  • 구현 결과
    출력화면을 보면 아래와 같은 표가 출력된다. 표 위에 마우스를 올려보자. 각 셀들이 어느 계층에 속하는지 보여주게 설정해뒀다.
    영수증
    메뉴 가격
    피자 12,000
    스파게티 8,000
    총액 20,000
    캡션
    (1) caption
    “영수증”이라는 글자가 있는 곳이 caption에 해당하고, 테이블에 대한 설명을 넣는 곳이다.

    행 단위 (Block of Rows)
    (1) thead
    table header의 약자이다. 테이블의 열들의 제목을 넣는 부분으로, 기본적으로 굵은 글씨로 설정된다. 위에서는 “메뉴”와 “가격” 있는 행 단위에 해당한다.
    (2) tbody
    table body의 약자이다. 테이블의 실제적인 데이터가 들어가는 부분이다. 위에서는 “피자”, “12,000”, “스파게티”, “8,000”이 tbody가 들어가 있는 행 단위에 해당한다.
    (3) tfoot
    table footer의 약자이다. 데이터의 추가정보 및 요약하는 부분으로, 위와 같이 “총액” 같은 것을 넣을 수 있는 공간이다.

    테이블 행
    (1) tr
    table row의 약자로서, 테이블에서 하나의 행에 해당한다. HTML 테이블은 행으로 작성하게 되기 때문에, thead, tbody, tfoot의 행 단위 안에는 무조건 <tr></tr> 요소가 와야 한다. 그리고 <tr></tr> 안에는 그 행에 들어가는 데이터 셀들이 위치하게 된다.

    테이블 셀
    (1) th: table header cell의 약자이다. 테이블 헤더(thead)에 위치한 셀로써, 테이블의 제목이 들어가는 셀이다.
    (2) td: table data cell의 약자이다. tbody나 tfoot에서 실제적인 데이터가 들어가는 셀이다.

[2] tbody 생략

앞 섹션에서 보았듯이 <table> 요소 아래에는 <thead>, <tbody>, 또는 <tfoot> 요소들이 오게 된다. 하지만 실제 많은 경우에, 아래 코드처럼 <tbody></tbody>를 생략하고, <table> 요소 바로 밑에 <tr> 요소를 적는 것을 볼 수 있다. 특히 표를 레이아웃(Layout)으로 이용할 때, 이런 식으로 많이 사용되었다. 요즘은 다른 좋은 수단들이 생겨서 테이블로 레이아웃을 짜는 경우가 거의 없어졌다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<table>
    <tr>
        <th>메뉴</th>
        <th>가격</th>
    </tr>
    <tr>
        <td>피자</td>
        <td>12,000</td>
    </tr>
    <tr>
        <td>스파게티</td>
        <td>8,000</td>
    </tr>
    <tr>
        <td>총액</td>
        <td>20,000</td>
    </tr>
</table>
cs

A table element must not contain tr elements, even though these elements are technically allowed inside table elements according to the content models described in this specification. (If a tr element is put inside a table in the markup, it will in fact imply a tbody start tag before it.)

table 요소의 자식요소로 tr 요소가 오는 것은 기술적으로는 허용되지만, table 요소는 tr 요소를 바로 포함하면 안 된다. (만약 tr 요소가 table 요소에 바로 포함되어 있다면, 이것은 tbody 요소에 들어가 있는 것으로 간주한다.)
  • tbody를 적어줘야 하는 이유는 무엇인가요?
  1. tbody를 적지 않더라도, 브라우저들이 자동적으로 tbody를 추가한다.
    위에서 작성한 HTML을 크롬에서 열고, F12(개발자 툴)을 눌러서 Elements 탭에서 요소 구조를 확인해보자. 분명 코드에는 tbody를 적지 않았지만, 아래 Figure 2처럼 브라우저는 자동으로 tbody를 추가한다. 이렇게 되면 실제 코드와 브라우저가 처리하는 코드에 일관성의 문제가 발생할 수 있다. 즉, 브라우저마다 tbody를 추가하느냐 안하느냐의 문제도 있을 수 있고, CSS나 Javascript에서 테이블 요소를 선택할 때 문제가 발생할 수도 있다. 이 문제들은 아래에서 자세히 다룬다.
<body>
  <table>
   <tbody>
    <tr>
     <th>메뉴</th>
     <th>가격</th>
    </tr>
    <tr>
     <td>피자</td>
     <td>12,000</td>
    </tr>
    <tr>
     <td>스파게티</td>
     <td>8,000</td>
    </tr>
    <tr>
     <td>총액</td>
     <td>20,000</td>
    </tr>
    </tbody>
  </table>
</body>
Figure 2. 브라우저(크롬)는 자동으로 tbody를 추가한다.

  1. 시맨틱 웹(Semantic Web) 구현이라는 관점에서도 tbody를 적어주는 것이 좋다.
    시맨틱 웹이란, 쉽게 말해서 컴퓨터들도 이해할 수 있게 만들어진 웹이라고 할 수 있다. 사람들은 어느 부분이 테이블의 제목에 해당하고 어느 부분이 실제적인 데이터에 해당한다고 쉽게 이해할 수 있지만, 컴퓨터는 이해할 수 없다. 따라서 정확히 thead에 제목들을 넣어주고, tbody에 실제적인 데이터를 넣어주고, 추가적인 정보들을 tfoot에 넣어줘야지, 컴퓨터가 테이블 헤더(header) 부분을 제목으로, 테이블 보디(body) 부분을 실제적인 데이터로, 테이블 푸터(footer)을 요약 및 추가 정보들로 구분할 수 있다. 그럼 왜 컴퓨터가 이해할 수 있게 웹을 만들어야 하며, 우리에겐 어떤 이득이 있을까? 검색 엔진(예: 구글)은 크롤러(crawler)라고 불리는 소프트웨어를 이용하여 웹사이트를 랜덤으로 돌아다니며 해당 정보를 데이터베이스에서 정리해 두는데, 사용자가 검색을 하면 이를 바탕으로 사이트 정보를 보여주게 된다. 이 때 크롤러가 잘 이해할 수 있게 짜여진 시맨틱 웹일 수록, 정확한 정보가 사용자들에게 전달 될 수 있고, 더 많은 방문자들을 유도할 수 있다. 따라서 방문자 수를 올리고자 한다면, 시맨틱 웹을 잘 구현하는 것이 핵심이라 할 수 있다.
  2. th 요소들의 위치가 thead가 아니라 tbody 요소 밑으로 자동 분류되었다.
    <th></th> 요소는 thead 요소 아래로 들어가야 하나, 브라우저가 tbody 밑으로 자동을 넣어버리는 문제점이 있다. 결과 화면을 볼 때 사람 눈에는 제목과 데이터 값을 구분할 수 있지만, 위에서 설명한 것처럼 컴퓨터에게는 둘의 차이를 구분하기 쉽지 않다. 따라서 시맨틱 웹의 측면에서 thead, tbody, tfoot을 정확히 적어주는 것이 좋다.

[2.1] tbody 생략: 출력에는 문제 없음

  • 예제 목표
    tbody를 생략하고, table 밑에 바로 tr을 적었을 때에도, 표는 제대로 출력된다는 것을 확인한다.
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id=“wrapper”>
    <table>
        <tr>
            <th>메뉴</th>
            <th>가격</th>
        </tr>
        <tr>
            <td>피자</td>
            <td>12,000</td>
        </tr>
        <tr>
            <td>스파게티</td>
            <td>8,000</td>
        </tr>
        <tr>
            <td>총액</td>
            <td>20,000</td>
        </tr>
    </table>
</div>
cs
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
    th, td {
        background-color: rgba(51, 108, 78, 0.5);
        color: white;
        border: 1px solid rgb(81, 139, 109);
        padding: 5px 10px;
    }
    /* 아래는 기본 래퍼 설정 */
    #wrapper {
        display: flex;
        justify-content: center;
        
        margin: 0 auto;
        padding: 10px;
        max-width: 1170px;
    }
</style>
cs
RESULT
메뉴 가격
피자 12,000
스파게티 8,000
총액 20,000

  • HTML
    <div id=”wrapper”></div> 예제를 둘러싼 래퍼이다. 래퍼의 개념과 기본 CSS 설정에 대해서는 [참고1. 래퍼의 개념과 기본 설정]에 대해서 자세히 다뤘으니, 관심있으신 분들은 참고하시면 될 것 같다.
    <table></table> 테이블 요소를 만든다.
    <tr></tr> tbody요소 없이 바로 테이블 행에 관련된 tr 요소를 생성한다.
    <th></th> 테이블 헤더 셀에 해당하며, 테이블의 제목을 적는다.
    <td></td> 테이블 데이터 셀에 해당하며, 실제적인 데이터를 적는다.
  • th, td의 CSS
    background-color: rgba(51, 108, 78, 0.5); th, td 셀의 배경 색깔을 지정한다.
    color: white; 글자 색깔을 흰색으로 지정한다.
    border: 1px solid rgb(81, 139, 109);  테두리선을 두께, 모양, 색깔을 설정한다.
    padding: 5px 10px; th, td 셀의 내부 여백을 위/아래는 5px로, 왼쪽/오른쪽은 10px로 지정한다.
  • #wrapper의 CSS
    display: flex; 자식 요소 table를 정렬하기 위해 flex로 설정한다.
    justify-content: center; 자식 요소 table을 (수평) 가운데 정렬을 한다.
    * 그 외의 부분은 기본 래퍼 설정에 해당한다. 왜 이렇게 설정하는지에 대한 내용은 [참고1. 래퍼의 개념과 기본 설정]에 자세히 설명되어 있으니, 필요하신 분들은 읽어보시면 도움이 될 것이다.
  • 구현 결과
    tbody 요소를 적지 않고, table 요소 바로 아래에 tr 요소를 적었을지라도 에러 없이 잘 출력된다.

[2.2] tbody 생략: CSS에서 발생하는 문제

  • 예제 목표
    tbody를 사용하지 않을 때, CSS에서 자식 선택자(>)를 사용할 때 발생할 수 있는 문제를 알아본다.
HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id=“wrapper”>
    <table>
        <tr>
            <th>메뉴</th>
            <th>가격</th>
        </tr>
        <tr>
            <td>피자</td>
            <td>12,000</td>
        </tr>
        <tr>
            <td>스파게티</td>
            <td>8,000</td>
        </tr>
        <tr>
            <td>총액</td>
            <td>20,000</td>
        </tr>
    </table>
</div>
cs
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
    table > tr > th, table > tr > td {
        background-color: rgba(51, 108, 78, 0.5);
        color: white;
        border: 1px solid rgb(81, 139, 109);
        padding: 5px 10px;
    }
    /* 아래는 기본 래퍼 설정 */
    #wrapper {
        display: flex;
        justify-content: center;
  
        margin: 0 auto;
        padding: 10px;
        max-width: 1170px;
    }
</style>
cs
RESULT
메뉴 가격
피자 12,000
스파게티 8,000
총액 20,000

  • HTML
    앞 섹션에서 사용한 html을 그대로 사용한다.
  • table > tr > th, table > tr > td의 CSS
    앞 섹션의 예제와 다른 점은 [2.1] 예제에서는 “태그 선택자”를 사용했지만, 여기서는 “자식 선택자”를 사용했다는 점이다. 자식 선택자(>)는 바로 밑에 있는 자식 요소를 선택할 때 사용한다. 선택자만 다르게 작성하였고, 안에 들어가 있는 프로퍼티와 값들은 앞의 예제와 동일하게 작성했다.
  • 구현 결과
    [2.1] 예제 결과와 다르게 여기서는 thtd의 스타일이 전혀 적용되지 않았음을 확인할 수 있다. 이렇게 스타일이 적용되지 않은 이유는 브라우저가 tbodytable 아래에 자동으로 삽입(table > tbody > tr)을 해버리기 때문에, “table > tr“을 찾을 수가 없고, 따라서 스타일이 적용되지 않은 것이다.

[2.3] tbody 생략 시 Javascript에서 발생하는 문제

  • 예제 목표
    tbody를 사용하지 않을 때, Javascript에서 요소를 선택할 때 발생할 수 있는 문제를 알아본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
<table>
    <tr id=“row”>
        <td>피자</td>
        <td>12,000</td>
    </tr>
</table>
  
<script>
    window.onload = function() {
        let parent = document.getElementById(“row”).parentElement;
        console.log(parent);
    };
</script>
cs
<tbody>
  <tr id=”row”>
   <td>피자</td>
   <td>12,000</td>
  </tr>
</tbody>
  • HTML
    <table></table> 테이블 요소를 생성한다.
    <tr id=”row”></tr> 테이블 행요소를 생성하고, id값을 row로 설정한다.
    <td>피자</td> 테이블 데이터 셀에 “피자”를 넣는다.
    <td>12,000</td> 테이블 데이터 셀에 “12,000”을 넣는다.
  • JavaScript
    window.onload = function() { }; 모든 요소들이 로딩된 후에 우변에 있는 함수가 실행된다. 리터럴 형식으로 표현되어 있으므로 마지막에 세미콜론(;)을 넣어줘야 한다.
    let parent = document.getElementById(“row”).parentElement; id=”row”인 요소를 찾아서, 그 요소의 부모요소(.parentElement)를 선택한다. 그리고 선택한 요소를 변수 parent에 넣는다.
    console.log(parent); 콘솔 창에 변수 parent를 출력한다. 콘솔 창은 크롬에서는 F12 눌러서 개발자 툴을 열면, console 탭에서 출력값을 확인할 수 있다.
  • 구현 결과
    작성된 코드를 기준으로 판단해볼 때, id=”row”를 가지고 있는 요소는 <tr id=”row”>이므로, tr의 부모 요소는 table 이다. 따라서 변수 parent의 값은 table 요소가 되고, 콘솔 창에는 table 요소가 출력되어야 한다. 하지만 출력된 결과 값은 tbody이다. 이렇게 다른 값이 나오는 이유는, 브라우저가 자동적으로 tbodytabletr 사이에 넣기 때문에 발생한다. 따라서 Javascript로 테이블 요소들을 다룰 때는 특별히 유의해야 한다.

참고
[1] 래퍼의 개념과 기본 설정
[2] Restrictions on content models
[ CSS 목차보기 ]

Leave a Reply