
처음 블로그에 비교표를 올렸을 때 내 눈엔 깔끔하게 보였다. 데스크탑 화면에서 컬럼이 딱딱 맞아 떨어지고, 텍스트도 잘 들어가 있었다. 뿌듯한 마음으로 저장하고 나서 핸드폰으로 확인했는데, 표가 화면 밖으로 반쯤 튀어나와 있었다. 마치 옷장에 양복을 넣으려는데 소매가 문 밖으로 빠져나온 것 같은 기분이었다. 억지로 접어 넣어도 다음날 보면 또 삐져나와 있는 그 느낌.
문제는 HTML 표 자체가 모바일 우선 시대 이전에 설계된 요소라는 데 있다. 기본 동작이 "내 열 너비만큼 공간이 있어야 한다"는 것이라서, 화면이 좁으면 그냥 삐져나와 버린다. 내가 잘못 만든 게 아니라 애초에 그렇게 만들어진 것인데, 그걸 나중에야 알았다.
그 이후로 표를 만들 때마다 모바일 뷰를 먼저 확인하는 버릇이 생겼다. 데스크탑에서 예쁜 표가 모바일에서 깨지면 방문자 절반은 그냥 나가버린다. 요즘 블로그 방문 트래픽의 60~70%가 모바일인 걸 생각하면 표 하나 망가진 게 보기 싫은 수준이 아니라 이탈률 문제다. 모바일 환경에서 HTML 표의 오른쪽 열이 잘려 보이는 문제는 생각보다 훨씬 흔합니다. 데스크탑에서 깔끔하게 완성했다고 안심했다가, 스마트폰으로 열어보는 순간 반쪽짜리 표를 마주하는 경험, 저도 똑같이 겪었습니다. 그 황당함이 계기가 되어 반응형 표 처리 방식을 제대로 파고들었습니다.
반응형 표 구조: 일반적인 믿음과 실제 차이
일반적으로 HTML 표를 제대로 짰으면 어느 화면에서나 잘 보일 거라고 생각하는 분들도 있는데, 저는 실제로 그렇지 않다는 걸 직접 확인했습니다. 스마트폰 요금제 비교표를 처음 만들었을 때였습니다. thead, tbody, th, td를 정석대로 나눠 쓰고, 배경색도 교대로 넣어서 꽤 만족스러운 결과물이었습니다. 그런데 핸드폰으로 열어보니 오른쪽 두 열이 화면 밖으로 완전히 잘려 있었고, 가로 스크롤조차 되지 않았습니다.
여기서 thead란 표의 헤더 행을 감싸는 시맨틱 태그(semantic tag)로, 쉽게 말해 표 위쪽에 들어가는 '열 제목 묶음'입니다. 시맨틱 태그란 HTML 요소에 의미를 부여하는 태그를 의미하며, 브라우저와 스크린 리더가 콘텐츠를 올바르게 해석하는 데 쓰입니다. 이 구조를 제대로 잡지 않으면 보조 기술이 표를 인식하지 못하는 접근성 문제로 이어집니다.
제가 처음 시도한 해결책은 표를 감싸는 래퍼 div(wrapper div)에 overflow-x: auto 속성을 추가하는 방법이었습니다. 여기서 overflow-x: auto란 콘텐츠가 컨테이너 너비를 초과할 때 가로 방향으로 스크롤 가능한 영역을 자동으로 생성하는 CSS 속성입니다. 이 한 줄만 추가했는데 가로 스크롤이 생기면서 잘리던 열이 보이기 시작했습니다. 그런데 문제가 하나 남았습니다. 사용자가 옆으로 스크롤할 수 있다는 걸 모를 수 있다는 점이었습니다. 그래서 표 위에 "좌우로 스크롤하세요"라는 안내 문구를 별도로 추가했습니다. 작은 디테일이지만, 이걸 빠뜨리면 독자가 정보의 절반을 그냥 놓치고 떠납니다.
CSS 미디어 쿼리(media query)를 이용한 세로 쌓기 방식은 또 다른 선택지입니다. 미디어 쿼리란 화면 너비 등 환경 조건에 따라 다른 스타일을 적용할 수 있게 해주는 CSS 기능으로, 반응형 웹 디자인의 핵심 도구입니다(출처: MDN Web Docs). 이 방식을 쓰면 데스크탑에서는 일반 가로 표로, 모바일에서는 행 단위로 세로로 쌓이는 구조로 자동 전환됩니다.
반응형 표를 만들 때 체크해야 할 핵심 포인트는 다음과 같습니다.
- 표를 래퍼 div로 감싸고 overflow-x: auto를 적용해 가로 스크롤을 허용한다
- thead, tbody, th, td 시맨틱 구조를 반드시 갖춘다
- caption 태그와 scope 속성으로 접근성을 챙긴다
- 모바일 분기점에서 미디어 쿼리로 세로 쌓기 레이아웃을 적용한다
data-label 세로 쌓기 방식: 효과적이지만 유지 보수 비용이 있다
data-label 방식은 각 td 셀에 data-label="요금제명" 같은 HTML 속성을 직접 넣고, CSS에서 content: attr(data-label)로 그 값을 화면에 출력하는 방식입니다. 여기서 attr()란 HTML 요소의 속성값을 CSS content 속성 안에서 텍스트로 불러오는 함수입니다. 덕분에 모바일 환경에서 th(헤더 셀)가 화면에서 사라져도 각 td가 자기 라벨을 직접 표시할 수 있습니다.
솔직히 이건 처음 봤을 때 CSS가 너무 길어서 겁먹었습니다. 그런데 막상 써보니 복사해서 열 이름에 맞게 data-label 값만 수정하면 됐습니다. 완성하고 핸드폰으로 열어보는 순간, 각 행이 카드처럼 세로로 쌓여서 훨씬 읽기 편한 화면이 나왔습니다. 그때부터 표를 만들면 모바일 뷰를 먼저 확인하는 게 습관이 됐습니다.
그런데 이 방식이 항상 옳다고 말하기는 어렵습니다. data-label 세로 쌓기 방식은 비교 목적의 표, 즉 각 행이 독립적인 데이터를 담을 때 강점을 발휘합니다. 하지만 행과 열의 교차점을 봐야 하는 행렬형 표에서는 세로 쌓기가 오히려 정보 파악을 더 어렵게 만듭니다. 행렬형 표란 행과 열 양쪽에 모두 헤더가 있고, 그 교차 지점에서 의미가 완성되는 구조의 표입니다. 이런 표를 세로로 쌓으면 맥락이 끊깁니다.
유지 보수 측면에서도 한 가지 현실적인 단점이 있습니다. 열 제목이 바뀔 때마다 HTML의 모든 td에 붙은 data-label 속성을 일일이 수정해야 합니다. 열이 많고 데이터 갱신이 잦은 표라면 오히려 관리 비용이 늘어납니다. 반응형 웹 디자인(Responsive Web Design, RWD)의 원칙은 콘텐츠를 어떤 화면에서도 접근 가능하게 만드는 것이지, 특정 기법을 무조건 적용하는 게 아닙니다. RWD란 화면 크기와 기기 종류에 따라 레이아웃이 유연하게 적응하는 웹 디자인 접근법입니다(출처: web.dev).
제 경험상, 표의 성격을 먼저 파악하고 방식을 선택하는 게 맞습니다. 단순 비교표라면 data-label 세로 쌓기, 데이터 갱신이 잦거나 열이 많은 표라면 래퍼 div 가로 스크롤 방식이 더 현실적입니다.
표는 만드는 사람 기준이 아니라 읽는 사람 기준으로 만들어야 합니다. 제 화면에서 예쁘면 완성이 아니라, 독자의 화면에서 안 깨지면 완성입니다. 당연한 말 같지만, 직접 반쪽짜리 표를 마주하기 전에는 잘 와닿지 않았습니다. 표를 만들고 나면 반드시 모바일 화면으로 한 번 더 확인하는 것, 그게 지금 제가 지키는 가장 기본적인 규칙입니다.
참고
MDN Web Docs
CSS-Tricks – Responsive Data Tables
W3Schools – CSS Tables
web.dev – Responsive web design basics