CSS position: sticky로 헤더 고정하는 <table> 만드는 방법

표의 내용이 길어질 때, 엑셀에서 "틀 고정" 기능을 사용하듯 열 또는 행을 고정시키고 싶을 때가 있습니다. 이와 같은 기능을 자바스크립트 없이 CSS만으로 구현이 가능합니다. position: sticky를 이용해서 코드 작성하는 방법을 알려드리겠습니다.

 

position: sticky가 작동하기 위한 전제 조건

먼저 sticky가 작동하려면 몇 가지 전제 조건이 필요합니다.

  1. 부모 요소에 overflow가 있으면 안 됩니다.
  2. top, bottom, left, right 속성 중 한 두 가지를 반드시 가지고 있어야 합니다. 
  3. 부모 요소의 높이(height) 또는 너비(width)가 있어야 합니다. 
  4. sticky 속성을 갖는 요소는 자신의 부모 요소 안에서만 적용됩니다. 즉, 부모 요소의 범위를 벗어나면 sticky 효과가 사라집니다.

 

thead th, tbody th에 sticky 속성 적용하기

 

sticky 속성을 이용하기 위한 구조는 아래 그림과 같습니다.

sticky table을 만들기 위한 개념도

 

개념을 이해했다면 CSS를 작성해 보겠습니다. 주의해야 할 점이 있다면, td 위로 th가 덮이는 모양이므로 z-index와 background-color를 주어야 합니다.

// 부모요소를 벗어난 표의 경우, 스크롤이 생기도록 합니다.
.wrapper { 
  width: 500px; 
  height: 400px; 
  overflow-y: auto; 
}

// th, tdpaddingborder 값 부여
table { border-collapse: collapse; }
th, td { padding: 10px; border: 1px solid #ddd; }

// 첫 번째 행을 고정시킵니다. 
thead th { 
  position: sticky; 
  top: 0; 
  background: #eee; 
  border-top: 1px solid #ddd;
  z-index: 1; /* 셀이 고정될 때 td 위로 보여야 하기 때문에 z-index 값 부여 */
}

// 첫번째 칸은 좌우 스크롤 시에 왼쪽+상단에 고정되어야 하므로 left, top 속성을 모두 부여합니다.
thead th.no { 
  left: 0;
  z-index: 1; 
}

// 가로 스크롤시에 왼쪽에 고정되도록 설정합니다.
tbody th { 
  position: sticky; 
  left: 0; 
  background: #f5f5f5; 
  z-index: 1; 
}

 

기본적인 원리는 이 것이 전부입니다. 하지만 이 상태로 코드를 작성하면 border 속성을 주었을 때 오류가 발생합니다.

 

sticky table의 고정된 th 위로 td가 겹치는 현상 발생

아래 그림처럼 th의 border가 투명해지면서 td의 텍스트가 그 뒤로 1픽셀만큼 보이게 됩니다. 이 오류를 해결하기 위해서는 table이 어떻게 생겼는지부터 알아야 합니다.

sticky table에서 border가 투명해지는 오류 스크린샷
sticky table에서 고정된 th 뒤로 td가 비치는 현상

 

<table>의 기본 구조

<table>은 아래 그림과 같이 border, border-spacing의 속성을 갖습니다. CSS를 작성하지 않고 <table border="1"></table>로 코드를 작성한다면 아래 그림처럼  표가 출력되는 것을 확인할 수 있습니다.

&lt;table&gt;의 기본 구조
<table>의 기본 구조

 

sticky 테이블을 만들면서 table { border-collapse: collapse; } 속성을 부여해 1픽셀의 테두리는 만드는 것까지는 간단하게 했습니다. 그런데 sticky가 작동하면서 border가 숨어버리는 현상이 발생을 했죠. 해결하기 위해 collapse를 사용하지 않고 기본 속성인 border가 서로 분리된 상태로 만들고 대신 border-spacing을 0으로 주어 빈 공간을 없애보겠습니다.

table의 border를 각각 분리시킨 화면

border-collapse: separate;로 border를 분리시키고, border-spacing: 0;로 빈 여백을 없앴더니 각각의 border가 합쳐져서 2픽셀의 border가 생겼습니다. 하지만 우리는 1픽셀의 테두리를 갖고 싶지요. 방법은 있습니다.

 

1. th, td의 오른쪽(또는 왼쪽), 아래쪽(또는 위쪽)만 border를 1픽셀 지정

2. 마지막 tr의 th와 td에는 아래쪽 border를 0으로 지정

3. 가장 오른쪽 th, td는 오른쪽 border를 0으로 지정 

 

위와 같은 방법을 써서 분리된 border가 겹쳐지더라도 1픽셀을 유지할 수 있습니다. 다양한 상황을 코드로 확인해 보겠습니다. 

 

 

지금까지 position: sticky를 이용한 표의 header를 고정하는 방법을 알아보았습니다. 더불어 sticky table를 만들면서 생기는 border 오류에 대해서도 살펴보았는데요. 이 글의 내용을 모두 종합하여 코드를 작성한 것은 아래에서 확인할 수 있습니다.

 

완성된 코드