JavaScript는 시각적 변경의 트리거인 경우가 많습니다. 스타일 조작을 통해 직접 변경하기도 하고, 데이터 검색 또는 정렬과 같이 시각적 변경을 초래하는 계산을 통해 변경하기도 합니다. 타이밍이 좋지 않거나 오래 실행되는 JavaScript는 성능 문제의 일반적인 원인이 될 수 있으므로 가능한 한 영향을 최소화해야 합니다.
스타일 계산
요소를 추가 및 삭제하거나, 속성 또는 클래스를 변경하거나, 애니메이션을 재생하여 DOM을 변경하면 브라우저에서 요소 스타일과 대부분의 경우 페이지의 일부 또는 전체 레이아웃을 다시 계산합니다. 이 프로세스를 스타일 계산 이라고 합니다.
브라우저는 일치하는 선택기 집합을 만들어 스타일 계산을 시작하여 특정 요소에 적용되는 클래스, 가상 선택기, ID를 결정합니다. 그런 다음 일치하는 선택기의 스타일 규칙을 처리하고 요소의 최종 스타일을 파악합니다.
상호작용 지연 시간에서 스타일 재계산의 역할
다음 페인트에 대한 상호작용 (INP)은 사용자 입력을 기준으로 페이지의 전반적인 응답성을 평가하는 사용자 중심의 런타임 실적 측정항목입니다. 사용자가 페이지와 상호작용하는 시점부터 브라우저가 사용자 인터페이스에 해당하는 시각적 업데이트를 보여주는 다음 프레임을 페인트할 때까지의 상호작용 지연 시간을 측정합니다.
상호작용의 중요한 구성요소는 다음 프레임을 페인트하는 데 걸리는 시간입니다. 다음 프레임을 표시하기 위해 수행되는 렌더링 작업은 레이아웃, 페인트, 합성 작업 바로 전에 발생하는 페이지 스타일 계산을 비롯한 여러 부분으로 구성됩니다. 이 가이드에서는 스타일 계산 비용에 중점을 두지만 상호작용의 총 렌더링 기간을 줄이면 총 지연 시간도 줄어듭니다.
선택기의 복잡성 줄이기
CSS 선택기를 간소화하면 페이지의 스타일 계산 속도를 높일 수 있습니다. 가장 간단한 선택기는 클래스 이름만으로 CSS의 요소를 참조합니다.
.title {
/* styles */
}
하지만 프로젝트가 커짐에 따라 더 복잡한 CSS가 필요할 수 있으며 다음과 같은 선택기가 발생할 수 있습니다.
.box:nth-last-child(-n+1) .title {
/* styles */
}
이러한 스타일이 페이지에 적용되는 방식을 결정하기 위해 브라우저는 '이 요소가 부모 요소의 minus-nth-plus-1번째 하위 요소인 box 클래스의 부모가 있는 title 클래스의 요소인가?'라고 효과적으로 질문해야 합니다. 브라우저에서 이를 파악하는 데 시간이 걸릴 수 있습니다. 이를 간소화하려면 선택기를 더 구체적인 클래스 이름으로 변경하면 됩니다.
.final-box-title {
/* styles */
}
이러한 대체 클래스 이름은 어색해 보일 수 있지만 브라우저의 작업을 훨씬 간단하게 만듭니다. 예를 들어 이전 버전에서 브라우저가 요소가 유형의 마지막 요소임을 알기 위해서는 먼저 그 뒤에 오는 요소가 nth-last-child가 될 수 있는지 확인하기 위해 다른 모든 요소에 관한 모든 것을 알아야 합니다. 이는 클래스 이름만을 기준으로 선택기를 요소와 일치시키는 것보다 훨씬 더 많은 계산 비용이 들 수 있습니다.
스타일이 지정되는 요소 수 줄이기
또 다른 성능 고려사항(선택기 복잡성보다 더 중요한 경우가 많음)은 요소가 변경될 때 발생해야 하는 작업의 양입니다.
일반적으로 계산된 요소 스타일을 계산하는 최악의 경우 비용은 요소 수에 선택기 수를 곱한 값입니다. 브라우저에서 일치하는지 확인하기 위해 모든 스타일에 대해 각 요소를 한 번 이상 확인해야 하기 때문입니다.
스타일 계산은 전체 페이지를 무효화하는 대신 몇 개의 요소를 직접 타겟팅할 수 있습니다. 최신 브라우저에서는 변경사항이 영향을 미칠 수 있는 모든 요소를 항상 확인할 필요가 없으므로 이 문제가 덜 발생하는 경향이 있습니다. 반면 이전 브라우저는 이러한 작업에 항상 최적화되어 있지는 않습니다. 가능한 경우 무효화된 요소 수를 줄여야 합니다.
스타일 재계산 비용 측정
브라우저에서 스타일 재계산 비용을 측정하는 방법에는 두 가지가 있습니다. 각 방법은 개발 환경의 브라우저에서 측정할지 아니면 웹사이트의 실제 사용자를 대상으로 이 프로세스가 얼마나 걸리는지 측정할지에 따라 다릅니다.
Chrome DevTools에서 스타일 재계산 비용 측정
스타일 재계산 비용을 측정하는 한 가지 방법은 Chrome DevTools의 성능 패널을 사용하는 것입니다. 시작하려면 다음을 실행하세요.
- DevTools를 엽니다.
- 성능 탭으로 이동합니다.
- 선택기 통계 체크박스를 선택합니다 (선택사항).
- 녹화 를 클릭합니다.
- 페이지와 상호작용합니다.
녹화를 중지하면 다음과 같은 이미지가 표시됩니다.
상단의 스트립은 초당 프레임 수도 표시하는 미니어처 Flame Chart입니다. 활동이 스트립 하단에 가까울수록 브라우저에서 프레임을 더 빠르게 페인트합니다. Flame Chart가 상단에서 수평으로 유지되고 그 위에 빨간색 막대가 표시되면 오래 실행되는 프레임을 유발하는 작업이 있는 것입니다.
스크롤과 같은 상호작용 중에 오래 실행되는 프레임은 자세히 살펴볼 가치가 있습니다. 큰 보라색 블록이 표시되면 활동을 확대하고 스타일 재계산 이라는 라벨이 지정된 작업을 선택하여 잠재적으로 비용이 많이 드는 스타일 재계산 작업에 관한 자세한 정보를 확인합니다.
이벤트를 클릭하면 호출 스택이 표시됩니다. 렌더링 작업이 사용자 상호작용으로 인해 발생한 경우 스타일 변경을 트리거한 JavaScript를 호출합니다. 또한 변경사항이 영향을 미치는 요소 수(이 경우 900개가 약간 넘음)와 스타일 계산에 걸린 시간도 표시합니다. 이 정보를 사용하여 코드에서 수정사항을 찾기 시작할 수 있습니다.
추적을 실행하기 전에 선택기 통계 체크박스 성능 패널 설정에서 선택한 경우 추적의 하단 패널에 동일한 이름의 탭이 추가됩니다.
이 패널은 각 선택기의 상대적 비용에 관한 유용한 데이터를 제공하므로 비용이 많이 드는 CSS 선택기를 식별할 수 있습니다.
자세한 내용은 CSS 선택기 통계 문서를 참고하세요.
실제 사용자의 스타일 재계산 비용 측정
웹사이트의 실제 사용자를 대상으로 스타일 재계산이 발생하는 데 걸리는 시간이 궁금하다면 Long Animation Frames API를 사용하면 필요한 도구를 얻을 수 있습니다. 이 API의 데이터는 스타일 재계산 시간을 비롯하여 the
web-vitals JavaScript library에 추가되었습니다.
상호작용의 프레젠테이션 지연이 페이지의 INP에 주로 기여한다고 생각되면 페이지에서 스타일을 다시 계산하는 데 걸리는 시간을 파악해야 합니다. 자세한 내용은 필드에서 스타일 재계산 시간을 측정하는 방법 을 참고하세요.