4P by GN⁺ | ★ favorite | 댓글 1개
  • 행이 많은 테이블은 스크롤할수록 열의 의미를 잊기 쉬워져 데이터 읽기가 불편해짐
  • theadposition: sticky;top: 0;만 추가하면 헤더 고정을 구현할 수 있음
  • sticky는 전 세계 기준 약 96% 브라우저 지원을 갖고 있어 실험적 기능에 가깝지 않음
  • 300번째 항목처럼 아래쪽 행을 볼 때도 열 제목이 남아 있어 표 탐색 경험이 좋아짐
  • 구현은 작지만 효과가 커서, 긴 테이블을 제공하는 웹페이지라면 기본 사용성 개선으로 적용할 만함

긴 테이블에서 헤더가 사라지는 문제

  • 큰 데이터셋이나 수백 행짜리 테이블에서는 스크롤 후 테이블 헤더가 화면 밖으로 사라짐
  • 300번째 항목까지 내려가면 각 열의 데이터가 무엇을 뜻하는지 다시 확인하기 어려울 수 있음
  • 처음 보는 테이블일수록 열의 의미를 기억하며 읽어야 해 탐색 부담이 커짐

thead에 sticky 적용하기

  • 해결 방법은 thead에 두 가지 CSS 속성을 추가하는 것임
position: sticky;
top: 0;
  • position: stickytop: 0을 함께 사용하면 스크롤 중에도 헤더를 화면 상단에 고정할 수 있음
  • sticky는 약 96% 전 세계 지원율을 갖고 있어 많은 브라우저에서 사용할 수 있음
  • 실제 동작은 CodePen 예제에서 확인할 수 있음

댓글과 토론

Hacker News 의견들
  • 한 축의 고정 헤더는 인정하겠지만, CSS가 양쪽 축 모두에서 지원하는 날을 아직 기다리는 중: https://github.com/w3c/csswg-drafts/issues/8286

    • 열과 헤더를 둘 다 고정해야 했던 계약 작업이 가장 어려운 일 중 하나였음
      표준 테이블이면 쉽지만, 이건 생성된 CSS-as-JS를 쓰는 복잡한 VueJS 서드파티 동적 테이블이라 CSS와 JS가 어디서 오는지 파악하는 데만 오래 걸렸고, 결국 JS 클래스 함수와 생성 CSS를 덮어써야 했음
      클라이언트에게 시간이 너무 걸려 미안했고, Vue/React 코드베이스에서 라이브러리 선택을 훨씬 조심하게 됨. 테이블은 HTML 기본 요소인데, 왜 단순한 작업 하나에 JS 클래스 세 겹과 JSON으로 임포트한 CSS까지 필요한지 모르겠음
    • 이 이슈를 낳은 원래 이슈를 아마 8년째 지켜보는 중인데, CSS 명세에서 가장 짜증 나는 부분임
      가끔은 거대한 데이터 테이블이 필요하고, 위쪽은 고정하면서 overflow-x: auto도 쓰고 싶은데 둘 중 하나만 고르라고 함
  • 고백하자면 CSS 지식이 거의 없어도 그럭저럭 동작하는 웹 앱은 만들고 있음. 최근 약 2만 행짜리 괴물 테이블에서 헤더를 고정하려고 했음: https://www.datasciencemeta.com/rpackages
    처음엔 Bootstrap 테이블 클래스에 class="table table-header-sticky" 같은 게 있길 기대했지만 없었고, Stack Overflow, GPT, CodePen 제안을 20분 정도 만져도 이상한 부작용이 생김
    그런데 이 글의 CodePen으로는 2분 만에 동작했고, 지금 운영에 배포하는 중임

  • 테이블 헤더 고정은 position: sticky; 하나면 쉬워 보이지만, 테이블 내용에 뭔가를 더하기 시작하면 바로 복잡해짐
    고정 헤더가 있는 테이블에서 사용자가 본문을 스크롤하고 페이지를 떠나면 첫 번째 보이는 행의 인덱스를 저장했다가 돌아왔을 때 복원하는 기능을 붙였는데, 항상 한 행쯤 아래로 복원됐음. 알고 보니 5행은 실제로 테이블 맨 위에 있었지만 thead가 겹쳐서 6행이 첫 행처럼 보였던 것임
    그래서 thead 높이를 고려해 스크롤 위치를 조정했지만 가끔 몇 픽셀씩 어긋났고, 큰 테이블이라 스크롤 이벤트에서 지연 로딩을 하며 기본 자동 테이블 레이아웃을 쓰는 탓에 헤더 셀이 줄바꿈되어 전체 헤더 높이가 변했음. 최종적으로는 theadResizeObserver를 붙여 높이가 바뀔 때 스크롤 위치를 동적으로 조정함
    [1] https://developer.mozilla.org/en-US/docs/Web/API/ResizeObser...

  • 이걸 구현하면서 헤더의 테두리도 같이 고정되길 원한다면 table thead:beforetable thead:afterposition: absolute, width: 100%, top: 0/bottom: 0, border-top/border-bottom을 주는 식으로 처리할 수 있음

  • 테이블 헤더는 자동으로 이렇게 동작했으면 좋겠음. 개발자가 굳이 관련 태그를 써서 추가 작업을 했다면, 대개 이미 원하는 동작이 이쪽일 가능성이 큼

    • 모든 페이지에 사용자 정의 스타일시트를 로드할 수 있는 브라우저 플러그인을 설치하면 됨
      다만 요즘은 <table> 대신 <div> 500개와 각 <div> 안에 45겹의 중첩 <span>으로 테이블을 만드는 사이트가 늘어서, 그런 곳에서는 안 먹힐 것 같음
    • 이런 변경은 예상 못 한 방식으로 무언가를 깨뜨릴 가능성이 커 보임
      주된 이유는 CSS Grid가 나오기 전 많은 웹사이트가 표를 격자 레이아웃용으로 썼기 때문일 것임. 헤더가 고정되면 그런 레이아웃이 깨질 수 있음
      HTML 의미론의 큰 실패는 테이블이 “표 형식 데이터를 표시하는 방법”이 아니라 “임의의 요소 격자”처럼 받아들여졌다는 데 있음
  • 명령줄에서는 헤더는 stderr, 본문은 stdout으로 보내면 됨
    그렇게 하지 않으면 heights | sort -k2 -nr 결과가 George 186, Wally 173, NAME HEIGHT 순서가 되지만, 헤더가 stdout을 우회하면 NAME HEIGHT, George 186, Wally 173 순서가 됨

    • heights | (sed -u 1q; sort -k2 -nr)가 제대로 동작함
      출처: https://stackoverflow.com/a/56151840
    • 파일에 쓰는 경우라면 몇 번 더 시도해야 하고 셸 이해도도 더 필요해짐
      이건 인밴드 텍스트 전용 모델의 지속적인 한계지만, STDERR를 이렇게 오용하면 도움이 되기보다 더 헷갈릴 것 같음
      heights가 파일이면 % head -1 heights ; sed 1,1d heights | sort -k2 -nr처럼 할 수 있고, heights가 두 번 실행하기 비싼 실행 파일이면 % heights > /tmp/heights ; head -1 /tmp/heights ; sed 1,1d /tmp/heights | sort -k2 -nr처럼 할 수 있음. awk로 파이프해도 됨
      다른 선택지는 덜 편하지만 덜 놀랍고, 실행 파일 작성자가 다른 생각을 갖고 있어도 동작함
    • 여기서는 뭐가 더 나을까? 헤더를 stderr로 보내는 것, 아니면 stdout이 TTY일 때만 헤더를 출력하는 것?
  • 이건 오랫동안 동작하지 않았음. Chrome은 예전에 theadtr 요소에서 position: sticky를 비활성화했었음: https://issues.chromium.org/issues/41303743
    브라우저 지원 표는 거의 전부 초록색으로 보였는데, 정작 Chrome의 테이블에서는 안 됐던 기억이 있음

    • 지금은 이렇게 간단해졌다는 게 놀라움. 예전에 필요했을 땐 브라우저마다 동작하게 하려면 HTML과 CSS에 온갖 잡동사니를 넣어야 했음
      그래도 놀랍진 않았는데, 웹 개발이 대체로 그런 식이기 때문임
  • 차라리 “제발 하지 말아 달라”고 말하고 싶은 쪽도 있음: https://news.ycombinator.com/item?id=32998091

    • 일반 웹페이지라면 전적으로 동의함. Stylus 스타일을 작성하는 것보다 빠르기 때문에 uBlock Origin으로 고정 요소를 삭제하는 일이 많고, 사이트 전반에 적용하는 범용 스타일도 꽤 잘 먹혔음
      하지만 원문은 고정 헤더 열이 있는 데이터 테이블 이야기라서, 이 제안은 실제로 꽤 유용함
    • 그건 웹페이지 전체의 고정 헤더 이야기임. 테이블에서는 완전히 다름
      숫자가 있는 열이 열두 개쯤 되는 테이블을 보다가 조금만 아래로 스크롤해도 뭐가 뭔지 모르게 되는 상황은 정말 싫음
    • sticky 속성의 뭐가 나쁜가?
  • CodePen은 top을 테두리만큼 오프셋하면 더 좋아 보임. 예를 들어 0 대신 -1px 로 두면 테이블 본문이 헤더 위로 비치는 것을 피할 수 있음
    비교: https://codepen.io/bradleytaunt/pen/bGZyJBj
    수정본: https://codepen.io/alokmenghrajani/pen/ExMBQow

  • 고정 헤더 대신 제한된 뷰포트 높이를 써서 헤더가 항상 보이게 하는 방식을 제안함
    DataGridXL(https://datagridxl.com)처럼 하면 됨. 참고로 내가 만든 것임

    • 그냥 떠오른 생각인데, 이 방식은 포인팅 장치 없이, 예를 들어 키보드만으로 탐색하기가 매우 어려움
    • 비판하려는 건 아니지만, 모바일에서는 이런 방식을 좋아한 적이 없음. 콘텐츠는 페이지와 함께 스크롤되어야 한다고 생각함