해당 글을 의역한 글입니다. https://nolanlawson.com/2018/09/25/accurately-measuring-layout-on-the-web/
Accurately measuring layout on the web
Update (August 2019): the technique described below, in particular how to schedule an event to fire after style/layout calculations are complete, is now captured in a web API proposal called reques…
nolanlawson.com
2019년에 WebKit은 Chrome 및 Firefox에 맞게 requestAnimationFrame 구현을 업데이트했습니다
(예: 다음 프레임 전에 렌더링 rendering before the next frame).
따라서 이 게시물에서는 이러한 개념 중 일부를 설명하고
웹 렌더링 진행 상황을 정확하게 측정하는 기술을 제공하고자 합니다.
The web rendering pipeline
밀리초 단위로 UI 스레드 내 컴포넌트 렌더링의 CPU 비용을 확인할 수 있습니다.
문제를 해결하기 위해 필요한 단계는 다음과 같습니다.
- Execute JavaScript – 상태 조작, "가상 DOM 디핑" 및 DOM 수정을 포함하여 JavaScript 실행(반드시 컴파일할 필요는 없음).
- Calculate Style – CSS 스타일시트를 가져오고 해당 선택기 규칙을 DOM의 요소와 일치시킵니다. 이를 "포맷팅(formatting)"이라고도 합니다.
- Calculate Layout – 2단계에서 계산한 CSS 스타일을 가져와 화면에서 상자를 배치해야 하는 위치를 파악합니다. 이를 "리플로우(reflow)"라고도 합니다.
- Render – 실제로 화면에 픽셀을 배치하는 프로세스입니다.
- 여기에는 종종 페인팅, 합성(composite), GPU 가속 및 렌더링 스레드 분리(a separate rendering thread)를 포함합니다.
이러한 모든 단계는 CPU 비용을 유발하므로 모든 단계가 사용자 경험에 영향을 미칠 수 있습니다.
그 중 하나라도 시간이 오래 걸리면 컴포넌트 로딩이 지연될 수 있습니다.
The naïve approach(단순 접근법)
사람들이 레이아웃 프로세스를 측정하려고 할 때 저지르는 가장 일반적인 실수는 2, 3, 4단계를 완전히 건너뛰는 것입니다.
즉, JavaScript를 실행하는 데 소요된 시간만 측정하고 그 이후의 모든 것은 완전히 무시합니다.

What to measure(무엇을 측정해야 하는가?)
실용적으로 우리는
JavaScript 실행을 시작하기 전에 성능 지표 마킹(performance mark)을 하고
모든 추가 작업이 완료된 후에 또 다른 성능 지표를 마킹하려고 합니다.

이전에 웹의 다양한 JavaScript 타이머에 대해 기사를 쓴 적(various JavaScript timers on the web)이 있습니다.
이들 중 어떤 것이 우리를 도울 수 있습니까?
결과적으로 requestAnimationFrame이 우리의 주요 선택 도구가 될 것이지만 문제가 있습니다.
Jake Archibald가 이벤트 루프에 대한 훌륭한 강연(his excellent talk on the event loop)에서 설명했듯이
각 브라우저는 이 콜백을 실행하는 위치가 다릅니다.
(주 : 이젠 아님)

Cross-browser “after frame” callback
스타일과 레이아웃 바로 뒤에 콜백을 배치하기 위한 완벽한 솔루션은 없지만
Todd Reifsteck(Todd Reifsteck)의 조언에 따르면 이것이 가장 가깝다고 생각합니다.
requestAnimationFrame(() => {
setTimeout(() => {
performance.mark('end')
})
})
이 코드가 수행하는 작업을 분석해 보겠습니다.
Chrome과 같은 사양 준수 브라우저의 경우 다음과 같이 표시됩니다.
rAF는 스타일 및 레이아웃 전에 실행되지만 다음 setTimeout은 해당 단계(이 경우 "페인트" 포함) 직후에 실행됩니다.
Edge 17과 같이 사양을 준수하지 않는 브라우저에서 작동하는 방식은 다음과 같습니다.
스타일 및 레이아웃 후에 rAF가 실행되고 다음 setTimeout이 너무 빨리 발생하여
Edge F12 도구가 실제로 두 마크를 겹처 렌더링합니다.
트릭은 rAF 내부에 setTimeout 콜백을 대기시키는 것입니다.
이렇게 하면 브라우저가 사양을 준수하는지 여부에 관계없이 두 번째 콜백이 스타일과 레이아웃 후에 발생합니다.
단점과 대안
이 기술에는 많은 문제가 있습니다.
- setTimeout은 4ms(또는 경우에 따라 그 이상)로 클램프 될 수 있다는 점에서 다소 예측할 수 없습니다.(주 : 4ms 단위로 반올림)
- 코드의 다른 곳에 대기 중인 다른 setTimeout 콜백이 있는 경우, 우리의 측정을 위한 콜백이 항상 마지막에 실행될 것이라는 보장이 없습니다.
- 사양을 준수하지 않는 브라우저에서 setTimeout을 수행하는 것은 실제로 낭비입니다.
- 마크를 설정하기에 완벽하게 좋은 장소인 rAF 내부가 있기 때문입니다.
하지만 그나마 rAF + setTimeout이 우리가 원하는 솔루션과 가장 가깝습니다.
몇 가지 대체 접근 방식과 이것들이 잘 작동하지 않는 이유를 고려해 보겠습니다.
rAF + microtask
requestAnimationFrame(() => {
Promise.resolve().then(() => {
performance.mark('after')
})
})
자바스크립트 실행이 완료된 직후 마이크로태스크(예: Promise)가 실행되기 때문에 이것은 전혀 작동하지 않습니다.
따라서 스타일과 레이아웃을 전혀 기다리지 않습니다.
(주 : 브라우저는 콜 스택이 빌 때마다 마이크로태스크를 실행할 기회를 얻음)
rAF + requestIdleCallback
requestAnimationFrame(() => {
requestIdleCallback(() => {
performance.mark('after')
})
})
rAF + rAF
requestAnimationFrame(() => {
requestAnimationFrame(() => {
performance.mark('after')
})
})

따라서 결함에도 불구하고 rAF+setTimeout이 여전히 rAF+rAF보다 더 나을 것입니다.
스스로를 속이지 말자

결론
'FrontEnd' 카테고리의 다른 글
[Vue3] defineProps를 컴포저블에서 사용할 수 없는 이유 (0) | 2023.01.05 |
---|---|
[Vue3] Vue3로 접근성 고려한 Form(양식) 개발하기 (0) | 2023.01.04 |
Vue3 컴포넌트 디자인 패턴 : Renderess Component / Compound Component (0) | 2022.12.31 |
[객체지향설계] 객체지향 설계 및 분석 : UML과 Use Case 다이어그램 (0) | 2022.12.30 |
[객체지향 설계] 객체지향 설계의 기초와 핵심개념 (0) | 2022.12.30 |