본문 바로가기
Frontend2026년 7월 23일5분 읽기

content-visibility: auto — 긴 페이지 렌더링 비용을 줄이는 CSS 한 줄

YS
김영삼
조회 5
content-visibility: auto — 긴 페이지 렌더링 비용을 줄이는 CSS 한 줄

content-visibility: auto는 화면 밖에 있는 요소의 렌더링(레이아웃·페인트)을 브라우저가 통째로 건너뛰게 해주는 CSS 속성이다. 뷰포트에 가까워지면 그때서야 렌더링하므로, 길고 무거운 페이지의 초기 렌더 비용을 극적으로 줄일 수 있다.

말은 거창하지만 실제로는 속성 한 줄이다. 나는 처음 이걸 접했을 때 "이게 되면 왜 다들 안 쓰지?" 싶었는데, 몇 번 써보고 나서야 함정이 왜 있는지 이해했다.

왜 필요한가 — 화면에 안 보여도 브라우저는 다 그린다

브라우저는 기본적으로 문서에 존재하는 모든 요소의 레이아웃을 계산하고 페인트한다. 스크롤을 한참 내려야 보이는 카드 500개도, 초기 로드 시점에 전부 계산 대상이다. 사용자는 첫 화면만 보는데 브라우저는 페이지 전체를 그리느라 메인 스레드를 붙잡고 있는 셈이다.

긴 상품 목록, 무한 스크롤 피드, 문서형 페이지에서 이 낭비가 특히 크다. 예전엔 이걸 피하려고 가상화(virtualization) 라이브러리를 붙였는데, DOM을 직접 잘라내는 방식이라 접근성·Ctrl+F 검색·SEO에서 대가를 치른다.

동작 원리 — 렌더링을 미룬다, DOM은 그대로

핵심은 이것이다. content-visibility: auto를 준 요소는 뷰포트 밖에 있으면 자식의 레이아웃과 페인트를 스킵한다. 하지만 DOM 노드 자체는 그대로 남아 있다. 즉 스크린 리더가 읽을 수 있고, 페이지 내 검색으로도 걸리고, 앵커 링크로 점프할 수도 있다. 가상화와 결정적으로 다른 지점이다.

다만 문제가 하나 있다. 렌더링을 스킵하면 브라우저는 그 요소의 높이를 모른다. 그래서 스크롤바가 요동치거나, 스크롤 위치가 튀는 현상이 생긴다. 이걸 막으려고 짝으로 쓰는 게 contain-intrinsic-size다. "이 요소는 대략 이 정도 크기일 거야"라고 미리 알려주는 힌트다.

.card {
  content-visibility: auto;
  /* 렌더 전 자리표시용 예상 크기 */
  contain-intrinsic-size: auto 320px;
}

auto 320px에서 auto 키워드가 중요하다. 한 번이라도 실제로 렌더된 요소는 그때 측정한 진짜 크기를 브라우저가 기억해뒀다가 재사용한다. 그래서 스크롤을 위아래로 오갈 때 자리표시 값이 계속 320px로 고정되지 않고 실제 높이로 안정된다.

실제로 얼마나 빨라지나

솔직히 처음엔 반신반의했다. 그런데 카드 800개짜리 내부 대시보드에 적용했더니 초기 렌더 시간이 눈에 띄게 줄었다. 원리상 초기 페인트 대상이 뷰포트 근처로 한정되니 당연한 결과다.

구분가상화 라이브러리content-visibility
DOM 노드보이는 것만 존재전부 존재
Ctrl+F 검색안 됨
구현 비용라이브러리+로직CSS 두 줄

내가 데인 함정들

  • contain-intrinsic-size를 빼먹으면 스크롤바가 춤춘다. 특히 스크롤 위치를 저장·복원하는 페이지에서 티가 난다. 자리표시 크기를 안 주면 브라우저는 요소를 0 높이로 취급한다.
  • 레이아웃 격리가 걸린다. 이 속성은 내부적으로 contain을 활성화한다. 자식이 부모 밖으로 삐져나오는 디자인(그림자, 툴팁, sticky 등)이 잘려 보일 수 있다.
  • 스크롤 앵커·CLS 지표. 자리표시 크기가 실제와 너무 다르면 렌더 순간 레이아웃이 밀려 CLS가 나빠진다. 대략이라도 실제 높이에 맞춰라.
  • 페이지 최상단(첫 화면) 요소엔 굳이 쓰지 마라. 어차피 바로 보이는데 contain 부작용만 떠안는다.
개인적으로는 "무한 스크롤 피드처럼 아이템이 서로 독립적이고, 각자 높이를 대충 예측할 수 있을 때"가 가장 잘 맞는 자리라고 본다. 반대로 요소끼리 얽혀 있거나 넘치는 장식이 많으면 오히려 골치 아프다.

자주 묻는 질문

content-visibility: hidden과는 뭐가 다른가요?

hidden은 조건과 무관하게 항상 렌더를 건너뛰고, display:none과 달리 렌더 상태를 캐시해둬 다시 보일 때 빠릅니다. 탭 패널처럼 내가 직접 보이고 숨김을 제어할 때 씁니다. auto는 뷰포트 근접 여부를 브라우저가 알아서 판단합니다.

SEO에 영향이 있나요?

DOM에 그대로 남아 있으므로 크롤러가 내용을 읽는 데 문제 없습니다. 가상화처럼 노드를 제거하지 않는 게 이 속성의 장점입니다.

브라우저 지원은 충분한가요?

크로미움 계열과 사파리, 파이어폭스 모두 지원해 실무 적용 가능한 수준입니다. 미지원 브라우저에선 속성이 무시될 뿐 페이지가 깨지지 않으므로 점진적 향상(progressive enhancement)으로 쓰기 좋습니다.

모든 긴 목록에 그냥 다 붙이면 되나요?

아닙니다. contain 부작용과 자리표시 크기 관리 비용이 있으니, 실제로 초기 렌더가 무거운 구간에만 선택적으로 적용하는 걸 권합니다. 성능 프로파일러로 먼저 병목을 확인하세요.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록