content-visibility: the new CSS property that boosts your rendering performance

본 게시글은 https://web.dev/content-visibility/ 의 글을 번역 게시하였습니다.

8월 25일 Chrome 85에 추가된 content-visibility 속성에 대해 정리했습니다.

화면 밖 콘텐츠의 렌더링을 생략함으로써 초기 로드 시간을 개선합니다.

Chrome 85에 새로 적용된 content-visibility 속성은 페이지 로드 성능을 향상시키는 가장 효과적인 새로운 CSS 속성 중 하나입니다. content-visibility는 UserAgent가 layout, painting을 포함한 요소의 렌더링 작업을 필요로할 때까지 생략할 수 있도록 합니다. 콘텐츠의 대부분이 화면 밖에 있을 때, content-visibility을 활용해서 렌더링을 생략하게 되면 사용자의 초기 로드 시간이 훨씬 빨라집니다. 또한, 화면 내 콘텐츠와 더 빠르게 상호작용할 수 있습니다.

콘텐츠에 content-visibility: auto 를 적용하면 초기 로드 시 렌더링 성능이 7배 향상됩니다.

Browser support

content-visibility는 CSS Containment Spec에 의존하고 있습니다. 현재는 Chrome 85만 지원하지만(Firefox는 “worth prototyping” 으로 간주합니다.), Containment Spec은 대부분의 모던 브라우저에서 지원하고 있습니다.

CSS Containment

CSS Containment의 핵심이자 목적은 페이지 전체에서 DOM subtree의 분리를 제공하여 렌더링 성능을 향상시키는 것 입니다.

기본적으로 개발자는 브라우저에게 페이지의 어떤 부분이 캡슐화되어 있는지 알려주어, 브라우저로 하여금 해당 콘텐츠가 외부 요소의 상태를 고려하지 않아도 된다는 것을 알 수 있도록 합니다. 분리된 콘텐츠를 알면 브라우저는 페이지 렌더링을 최적화할 수 있습니다.

CSS containment의 4가지 유형이 있고, 각 유형은 contain 속성의 잠재적 값이며 공백으로 구분하여 조합할 수 있습니다.

  • size: Size containment는 자손 요소를 확인하지 않고도 요소의 크기를 계산할 수 있습니다. 이는 요소의 크기 확인만 필요할 경우 자손 요소의 레이아웃을 생략할 수 있습니다.
  • layout: Layout containment는 자손 요소가 외부 레이아웃에 영향을 주지 않음을 의미합니다. 이는 다른 요소의 레이아웃만 필요할 경우에 자손 요소의 레이아웃을 생략할 수 있습니다.
  • style: Style containment는 countersqoutes와 같이 요소에 영향을 미칠 수 있는 속성이 요소를 벗어나지 못하도록 합니다. 이는 다른 요소의 스타일 계산을 원하는 경우라면, 자손 요소의 스타일 계산을 생략할 수 있습니다.
  • paint: Paint containment는 자손 요소가 overflow될 수 없으며, 요소가 화면 밖에 있거나 표시되지 않는 경우 자손 요소도 표시되지 않습니다. 이는 요소가 화면 밖에 있는 경우 자손 요소의 painting 작업을 생략할 수 있습니다.

Skipping rendering work with content-visibility

브라우저 최적화는 적절한 값이 지정되어야 효과가 있으므로 어떤 값을 써야할지 어려울 수 있습니다. 어떤 값이 가장 효과적인지 직접 적용하면서 확인하거나 또는 다른 속성인 content-visibility을 사용하여 자동으로 적용할 수 있습니다. content-visibility는 최소한의 노력으로 최대 성능 향상을 이룰수 있게 합니다.

content-visibility 속성에는 여러 값이 있지만, auto는 즉시 성능 향상을 제공하는 값입니다. content-visibility: auto는 layoutstyle and paint Containment의 효과가 있습니다. 요소가 화면 밖이면(선택되었거나 포커스를 받는 것과 같이 사용자 액션과 관련이 없는 경우) size Containment 효과도 얻습니다.(요소의  painting과 hit-testing도 중지합니다.)

이것이 무엇을 의미할까요? 바로 요소가 화면 밖에 있는 경우 자손 요소를 렌더링 하지 않습니다. 브라우저는 요소의 내용을 고려하지 않고 크기를 결정합니다. 자손 요소는 styling, layout과 같은 대부분의 렌더링 작업을 생략합니다.

요소가 뷰포트에 접근하면 브라우저는 더 이상 size containment를 유지하지 않고 사용자가 볼 수 있도록 렌더링 작업(painting과 hit-testing)을 진행합니다.

Example: a travel blog

이 예제에서는 content-visibility: auto 적용 후 초기 페이지 로드 시 렌더링 시간이 232ms에서 30ms로 빨라진 것을 보여줍니다.

위의 여행 블로그는 일반적으로 몇 장의 사진과 글들로 구성된 문단들로 이루어져 있습니다. 다음은 페이지 로드 시에 브라우저에서 일어나는 일입니다.

  1. 페이지의 일부를 필요한 리소스와 함께 네트워크를 통해 다운로드 합니다.
  2. 브라우저는 사용자에게 보여지는지 여부와 관계없이 모든 콘텐츠의 스타일, 레이아웃 작업을 진행합니다.
  3. 페이지 전부와 모든 리소스가 다운로드 될때까지 반복합니다.

2단계에서 브라우저는 변경되었을 수 있는 부분을 찾기 위해 모든 콘텐츠를 처리합니다. 새로운 업데이트의 결과로 새 요소와 그에 따라 변경되었을 요소의 스타일, 레이아웃을 업데이트 합니다. 이것이 렌더링 작업이며 시간이 걸리는 일입니다.

여행 블로그 예제입니다. Codepen에서 확인해보세요.

이제 이 블로그의 각 문단에 content-visibility: auto를 선언하면 어떻게 될지 생각해 보세요. 일반적인 단계 진행은 똑같습니다. 브라우저가 페이지의 일부를 다운로드하고 렌더링합니다. 그러나 차이점은 2단계에서 수행하는 작업의 양에 있습니다.

content-visibility를 통해 화면 내 콘텐츠의 스타일과 레이아웃 작업을 진행합니다. 하지만 요소가 완전히 화면 밖에 있는 경우라면 브라우저는 오직 해당 요소 박스의 스타일과 레이아웃만 처리하고 다른 렌더링 작업은 생략합니다.

이 페이지의 로딩 성능은 마치 화면 내 콘텐츠 전부와 화면 밖 콘텐츠는 빈 상자만 로딩 하는 것과 같습니다. 이는 예상되는 렌더링 비용에서 50% 이상을 감소하는 효과로 훨씬 더 나은 성능을 발휘합니다. 위 예제에서는 렌더링 시간이 232ms에서 30ms로 7배 빨라졌습니다.

이런 효과를 누리기 위해 우리가 할 일은 무엇일까요? 먼저 콘텐츠를 영역으로 나눕니다.

콘텐츠 영역에 content-visibility: auto를 적용하기 위해 클래스 story를 선언합니다. Codepen에서 확인해보세요.

그리고 콘텐츠 영역에 다음과 같이 스타일 규칙을 선언합니다.

.story {
  content-visibility: auto;
  contain-intrinsic-size: 1000px; /* 다음 섹션에서 셜명합니다. */
}

콘텐츠가 화면 안팎으로 이동하게 되면 필요에 따라 렌더링이 시작되고 중지됩니다. 하지만 브라우저는 가능한 렌더링 작업을 저장하기 때문에 동일한 콘텐츠를 반복해서 다시 렌더링 해야한다는 의미는 아닙니다.

Specifying the natural size of an element with contain-intrinsic-size

content-visibility의 잠재적인 이점을 실현하기 위해 브라우저는 콘텐츠의 렌더링 결과가 요소 크기에 영향을 미치지 않도록 size containment를 적용해야 합니다. 즉, 요소는 비어있는 것처럼 배치되는데 일반적으로 block 요소의 높이가 지정되지 않은 경우 높이는 0이 됩니다.

결국 스크롤바는 각 문단 높이에 의존하게 되어 이동하므로 이상적인 상황은 아닙니다.

다행히도, CSS에는 contain-intrinsic-size 속성을 제공합니다. 이 속성은 요소가 size containment의 영향을 받더라도 요소를 자연스러운 크기대로 효과적으로 지정할 수 있습니다. 이 예에서는 문단의 크기를 1000px로 임의 지정했습니다.

즉, 크기가 지정되지 않은 div가 공간을 차지할 수 있도록 “intrinsic-size”를 가진 자식 요소가 있는 것처럼 배치됩니다. contain-intrinsic-size는 렌더링 된 콘텐츠 대신 placeholder로서 동작합니다.

Hiding content with content-visibility: hidden

cached 렌더링의 장점을 활용하면서 콘텐츠가 화면에 있는지 여부와 관계없이 렌더링하지 않으려면 어떻게해야 할까요? content-visibility: hidden을 사용하세요.

content-visibility: hidden 속성은 마치 content-visibility: auto의 화면 밖 콘텐츠처럼 unrendered와 cached 렌더링의 장점을 제공합니다. 그러나 auto와 달리 콘텐츠가 화면 안으로 들어오더라도 자동으로 렌더링하지 않습니다.

이런 제어를 통해 요소를 숨겼다가 나중에 신속하게 다시 표시할 수 있습니다.

요소를 숨기는 다른 일반적인 방법들과 비교해봅시다.

  • display: none: 요소를 숨기고 렌더링 상태를 제거합니다. 즉, 해당 요소를 표시하기 위해서는 새 요소를 렌더링 하는 것만큼 비용이 많이 듭니다.
  • visibility: hidden: 요소를 숨기면서 렌더링 상태는 유지합니다. 이것은 문서에서 요소를 실제로 제거하지 않으며, 여전히 페이지에서 공간을 차지하고 클릭할 수 있는 상태입니다. 또한 숨겨져 있더라도 필요하면 렌더링 상태를 업데이트합니다.

반면 content-visibility: hidden은 렌더링 상태를 유지하면서 요소를 숨기는데, 상태를 변경해야 할 일이 생기면 요소가 다시 화면에 표시될 때만 변경합니다.(예로 content-visibility: hidden 속성이 제거되는 경우)

content-visibility: hidden의 좋은 사례는 가상의  스크롤러가 구현된 상태에서 레이아웃을 측정할 때입니다.

Conclusion

content-visibility 및 CSS Containment Spec은 몇 가지 흥미로운 성능 향상에 대한 것을 CSS에서 바로 적용할 수 있습니다. 자세한 내용은 다음을 확인해주세요.

출처


0개의 댓글

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다