효율적인 백그라운드 처리

웹 기능: content-visibility

브라우저에서 구성요소가 렌더링되지 않을 때 시스템 리소스와 배터리 수명을 보존하기 위해 무거운 백그라운드 작업을 일시중지합니다.

개요

content-visibility: auto 속성을 사용하면 브라우저가 표시 영역에서 멀리 떨어진 요소의 렌더링 계산을 건너뛸 수 있습니다. 브라우저가 요소의 렌더링을 건너뛰거나 재개하기로 결정하면 해당 요소에서 contentvisibilityautostatechange 이벤트를 실행합니다.

이 이벤트를 수신 대기하면 <canvas> 애니메이션, WebGL 렌더링, 고주파 WebSocket 데이터 폴링과 같이 비용이 많이 드는 작업이 필요하지 않을 때 일시중지하고 브라우저가 콘텐츠를 표시할 준비가 되면 적시에 재개할 수 있습니다.

contentvisibilityautostatechange 대 IntersectionObserver

어떤 API를 언제 사용해야 하는지 이해하는 것이 중요합니다.

  • 뷰포트에서 요소의 정확한 시각적 공개 상태와 관련된 애플리케이션 로직에 IntersectionObserver를 사용합니다 (예: 지연 로드 데이터, 무한 스크롤 트리거).
  • 복잡한 캔버스 업데이트나 대규모 DOM 변형과 같은 렌더링이 많은 작업에는 contentvisibilityautostatechange를 사용합니다. 이 이벤트는 브라우저의 내부 렌더링 수명 주기와 직접 연결됩니다. 브라우저는 요소가 화면에 실제로 표시되기 전에 렌더링을 시작하는 경우가 많습니다 (사전 렌더링 여백). 이 이벤트는 이 시점을 알려주어 콘텐츠를 표시할 준비가 되었는지 확인합니다.

구현

1. CSS 콘텐츠 공개 상태 적용

무거운 컨테이너에 content-visibility: auto를 설정하고 자리표시자 크기를 제공하여 스크롤바 점프를 방지합니다.

.heavy-component {
  /* Defer rendering work when off-screen */
  content-visibility: auto;
  
  /* Mandatory: Provide a placeholder size to prevent layouts shifts.
    - 'auto' is optional and enables the browser to remember the actual size
      once rendered. It must be paired with a <length> value to be used for
      the first render.
    - 'none' tells the browser not to apply any intrinsic width to this element.
      It can be used for either the height or the width value.
    - '500px' is the estimated height of this element. This can be any valid
      CSS <length> value. Replace it with the expected height of your
      component.
   */
  contain-intrinsic-size: auto none auto 500px;
}

2. 상태 변경 수신 대기

contentvisibilityautostatechange의 이벤트 리스너를 추가하여 백그라운드 작업을 일시중지하거나 재개합니다.

const component = document.querySelector('.heavy-component');

// Option 1: Direct listener (recommended)
component.addEventListener('contentvisibilityautostatechange', (event) => {
  if (event.skipped) {
    // The browser skipped rendering this content.
    // DO NOT perform heavy mutations or animation loops here.
    stopSimulation();
    pauseWebSocketPolling();
  } else {
    // The browser is about to render the content.
    // Resume your work so it is ready when visible.
    startSimulation();
    resumeWebSocketPolling();
  }
});

// Option 2: Capturing listener for event delegation
document.addEventListener('contentvisibilityautostatechange', (event) => {
  if (event.target.matches('.heavy-component')) {
    if (event.skipped) {
      stopSimulation();
    } else {
      startSimulation();
    }
  }
}, { capture: true });

대체 전략

content-visibility 속성과 연결된 contentvisibilityautostatechange 이벤트는 점진적 개선입니다. 이를 지원하지 않는 브라우저의 경우:

  • CSS 속성은 무시되고 콘텐츠는 정상적으로 렌더링됩니다.
  • 이벤트가 실행되지 않으므로 백그라운드 작업은 최적화 없이 정상적으로 계속 실행됩니다.

이전 브라우저에서 작업 일시중지를 지원해야 하는 경우 IntersectionObserver를 대략적인 근사치로 대체할 수 있습니다. 이렇게 하면 이전 기기에서도 배터리와 CPU를 절약할 수 있습니다.

// Fallback using IntersectionObserver for older browsers
const target = document.getElementById('target-container');

// Check if content-visibility is supported
const isSupported = 'contentVisibility' in document.documentElement.style;

if (!isSupported) {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // The element is close to the screen. Start work!
        startSimulation();
      } else {
        // The element is far away. Pause work!
        stopSimulation();
      }
    });
  }, {
    // Use rootMargin to start rendering before it hits the screen
    rootMargin: '200px'
  });

  observer.observe(target);
}