스타일 계산의 범위와 복잡성 줄이기

자바스크립트는 종종 시각적 변화를 유발합니다. 어떤 경우에는 스타일 조작을 통해 직접 변경되기도 하고, 데이터 검색 또는 정렬과 같은 시각적 변화를 일으키는 계산을 통해 변경되기도 합니다. 타이밍이 나쁘거나 실행 시간이 긴 JavaScript는 성능 문제의 일반적인 원인이며 가급적 이러한 영향을 최소화할 수 있는 방법을 찾아야 합니다.

요소 추가 및 삭제, 속성 변경, 클래스 변경 또는 애니메이션 재생을 통해 DOM을 변경하면 브라우저가 요소 스타일을 재계산하고 많은 경우에 페이지의 일부 또는 전체 레이아웃을 재계산합니다. 이 프로세스를 스타일 계산이라고 합니다.

브라우저는 일련의 일치하는 선택기를 만들어 주어진 요소에 적용되는 클래스, 의사 선택기, ID를 결정하여 스타일 계산을 시작합니다. 그런 다음 일치하는 선택기에서 스타일 규칙을 처리하고 요소의 최종 스타일을 파악합니다.

상호작용 지연 시간에서 스타일 다시 계산의 역할

다음 페인트에 대한 상호작용 (INP)은 사용자 입력에 대한 페이지의 전반적인 응답성을 평가하는 사용자 중심 런타임 성능 측정항목입니다. 사용자가 페이지와 상호작용하는 시점부터 브라우저가 사용자 인터페이스의 상응하는 시각적 업데이트를 보여주는 다음 프레임을 페인트할 때까지의 상호작용 지연 시간을 측정합니다.

상호작용의 중요한 구성요소는 다음 프레임을 페인트하는 데 걸리는 시간입니다. 다음 프레임을 표시하기 위해 실행되는 렌더링 작업은 레이아웃, 페인트, 컴포지션 작업 직전에 발생하는 페이지 스타일 계산을 비롯한 여러 부분으로 구성됩니다. 이 가이드에서는 스타일 계산 비용에 중점을 두지만 상호작용의 총 렌더링 시간 중 일부를 줄이면 총 지연 시간도 줄어듭니다.

선택기의 복잡성 줄이기

CSS 선택기를 단순화하면 페이지의 스타일 계산 속도를 높일 수 있습니다. 가장 간단한 선택자는 클래스 이름만으로 CSS의 요소를 참조합니다.

.title {
 
/* styles */
}

하지만 프로젝트가 커짐에 따라 더 복잡한 CSS가 필요할 수 있으며 다음과 같은 선택기로 끝날 수 있습니다.

.box:nth-last-child(-n+1) .title {
 
/* styles */
}

이러한 스타일이 페이지에 적용되는 방식을 확인하기 위해 브라우저는 다음과 같은 질문을 해야 합니다. “이 요소는 title 클래스를 포함하고 있고, 상위 요소의 - n번째 하위 요소 + 1인 box 클래스의 상위 요소를 가진 요소인가? 이를 파악하는 데 브라우저에 다소 시간이 걸릴 수 있습니다. 이를 간소화하려면 선택기를 더 구체적인 클래스 이름으로 변경하면 됩니다.

.final-box-title {
 
/* styles */
}

이러한 대체 클래스 이름은 어색해 보일 수 있지만 브라우저의 작업을 훨씬 간단하게 만듭니다. 예를 들어 이전 버전에서 브라우저가 요소가 유형의 마지막 요소인지 알기 위해서는 먼저 다른 모든 요소에 관한 모든 것을 알아야 하며, 그다음에 그 뒤에 오는 요소가 nth-last-child일 수 있는지 확인할 수 있습니다. 이는 클래스 이름만을 기준으로 선택기를 요소에 일치시키는 것보다 훨씬 더 많은 계산 비용이 들 수 있습니다.

스타일이 지정되는 요소 수 줄이기

또 다른 성능 고려사항은 선택자 복잡성보다 종종 더 중요한 요소로, 요소가 변경될 때 실행해야 하는 작업량입니다.

일반적으로, 각 요소는 매칭 여부를 확인하기 위해 최소한 한 번은 모든 스타일에 대해 확인할 필요가 있기 때문에, 계산된 요소 스타일 계산 비용의 최악의 사례는 요소 수와 선택기 수를 곱하는 것입니다.

스타일 계산은 페이지 전체를 무효화하지 않고 몇몇 요소를 직접 대상으로 할 수 있습니다. 최신 브라우저에서는 변경사항이 영향을 줄 수 있는 모든 요소를 브라우저가 항상 확인할 필요가 없기 때문에 이 문제가 발생할 가능성이 적습니다. 하지만 이전 브라우저는 이러한 작업에 맞게 최적화되지 않았습니다. 가급적 무효화되는 요소 수를 줄여야 합니다.

스타일 재계산 비용 측정

브라우저에서 스타일 재계산 비용을 측정하는 방법에는 여러 가지가 있습니다. 각 방법은 개발 환경의 브라우저에서 측정할지 또는 웹사이트에서 실제 사용자가 이 프로세스를 실행하는 데 걸리는 시간을 측정할지에 따라 다릅니다.

Chrome DevTools에서 스타일 재계산 비용 측정

스타일 재계산 비용을 측정하는 한 가지 방법은 Chrome DevTools의 성능 패널을 사용하는 것입니다. 시작하려면 다음 단계를 따르세요.

  1. DevTools를 엽니다.
  2. 실적 탭으로 이동합니다.
  3. 선택기 통계 체크박스를 선택합니다 (선택사항).
  4. 녹음을 클릭합니다.
  5. 페이지와 상호작용합니다.

녹화를 중지하면 다음과 같은 이미지가 표시됩니다.

스타일 계산을 보여주는 DevTools
스타일 계산을 보여주는 DevTools 보고서

상단의 스트립은 초당 프레임도 표시하는 소형 플레임 차트입니다. 활동이 스트립 하단에 가까울수록 브라우저에서 프레임을 더 빠르게 페인팅합니다. Flame 그래프가 상단에서 평평해지고 그 위에 빨간색 막대가 표시되면 오래 실행되는 프레임을 유발하는 작업이 있는 것입니다.

Chrome DevTools의 채워진 성능 패널의 활동 요약에서 Chrome DevTools의 문제 영역을 확대합니다.
DevTools 활동 요약의 장기 실행 프레임

스크롤과 같은 상호작용 중에 오래 실행되는 프레임은 자세히 살펴볼 가치가 있습니다. 큰 보라색 블록이 표시되면 활동을 확대하고 스타일 다시 계산이라는 라벨이 지정된 작업을 선택하여 비용이 많이 들 수 있는 스타일 다시 계산 작업에 관한 자세한 정보를 확인합니다.

스타일 재계산 작업의 영향을 받는 요소 수와 같은 중요한 정보를 포함하여 오래 실행되는 스타일 계산에 관한 세부정보를 가져옵니다.
DevTools 요약에서 25밀리초가 조금 넘게 걸리는 장기 실행 스타일 재계산입니다.

이벤트를 클릭하면 호출 스택이 표시됩니다. 렌더링 작업이 사용자 상호작용으로 인해 발생한 경우 스타일 변경을 트리거한 JavaScript를 호출합니다. 변경사항이 영향을 미치는 요소 수(이 경우 900여 개 요소)와 스타일 계산에 걸린 시간도 표시됩니다. 이 정보를 사용하여 코드에서 수정할 부분을 찾기 시작할 수 있습니다.

트레이스를 실행하기 전에 성능 패널 설정에서 선택기 통계 체크박스를 선택한 경우 트레이스의 하단 패널에 동일한 이름의 탭이 추가로 표시됩니다.

Chrome DevTools의 성능 패널에 표시되는 CSS 선택기 통계 표 이 표에는 경과 시간, 일치 시도 횟수, 일치 횟수, 일치하지 않는 노드의 비율, 선택자, 선택자가 포함된 스타일 시트와 같은 항목의 헤더와 해당 데이터가 포함됩니다.
Chrome DevTools의 성능 패널에 표시된 선택자 통계 표입니다.

이 패널은 각 선택자의 상대적 비용에 관한 유용한 데이터를 제공하므로 비용이 많이 드는 CSS 선택자를 식별할 수 있습니다.

자세한 내용은 CSS 선택자 통계 문서를 참고하세요.

실제 사용자의 스타일 재계산 비용 측정

웹사이트의 실제 사용자에게 스타일 재계산이 발생하는 데 걸리는 시간을 알고 싶다면 Long Animation Frames API를 사용하세요. 이 API의 데이터는 스타일 재계산 시간을 포함하여 web-vitals JavaScript 라이브러리에 추가되었습니다.

상호작용의 프레젠테이션 지연이 페이지의 INP에 가장 큰 영향을 미치는 것으로 의심되는 경우 페이지의 스타일을 다시 계산하는 데 걸리는 시간을 파악해야 합니다. 자세한 내용은 필드에서 스타일 재계산 시간을 측정하는 방법을 참고하세요.

리소스