reflow는 레이아웃 -> 페인트 -> 합성 이 이루어지는 것을 말하고
repaint는 페인트 -> 합성이 이루어지는 것을 말합니다.
레이아웃 단계에서 노드 들이 ViewPort(뷰포트) 내에 위치하는 위치를 계산하게 되는데 이 과정이 상대적으로 비용이 비싸서 reflow를 불필요하게 유발하지 않아야 합니다.
ViewPort(뷰포트)란 그래픽이 표시되는 브라우저의 영역, 크기를 말합니다.
reflow, repaint에 대해 알기 위해서는 먼저 브라우저의 구성을 알고 웹 페이지가 브라우저에 나오는 과정을 이해하여야 합니다.
브라우저 구성
브라우저는 아래와 같이 구성됩니다.
UIInterface, 브라우저 엔진, 렌더링 엔진, 네트워크 모듈, js 엔진, UIBackend, Data Storage로 구성되어 있습니다.
- User Interface
* 사용자가 접근할 수 있는 영역입니다.
* 주소창 표시, 뒤로가기 버튼, 새로고침 버튼등 GUI의 구성요소 입니다. - 브라우저 엔진
* 브라우저 프로그램의 중추 입니다.
* DataStorage를 참조할 수 있고 이곳에 데이터를 읽고 쓸수 있습니다.
* User Interface와 Rendering Engine의 사이에 위치하여 동작을 제어합니다. - 렌더링 엔진
* 요청한 컨텐츠를 화면에 출력하는 역할을 수행합니다.
* html, css등을 파싱하고 화면에 그려줍니다.
* safari는 웹킷, chrome은 블링크, firefox는 Gecko엔진을 사용합니다. - 네트워크 모듈
* 네트워크 요청을 운영체제에 요청하는 역할을 합니다.
* TCP/IP 요청은 OS에서 관리하는 커널 영역인 프로토콜 스택에서 요청/제어 됩니다 - JS Engine
* javascript 코드를 해석하고 실행합니다. - UI Backend
* 기본적인 위젯을 그리는 인터페이스 입니다. - Data Storage
* Local/Session Storage, Cookie등이 위치하게 됩니다. 브라우저의 메모리를 활용하는 영역입니다.
CRP(Critical Rendering Path)
HTML,CSS가 렌더링 엔진에서 파싱되고 화면에 출력된다고 하였습니다.
세부 동작은 어떻게 수행되는지 살펴보겠습니다
간략히 살펴보면 다음과 같은 순서로 진행됩니다.
- HTML을 다운로드 받고 렌더링 엔진에서 HTML을 파싱합니다.
- HTML을 파싱하던 중 외부 리소스가 필요한 부분(css, script태그 등등)을 만나면 파싱을 중단하고 외부 리소스를 요청합니다.
- HTML 파싱을 완료하고 DOM Tree를 구축합니다.
- CSS파일을 파싱하여 CSSOM Tree을 구축합니다.
- CSSOM tree와 DOM Tree를 합쳐 렌더트리를 생성합니다. 이때 렌더트리와 DOM Tree의 내용은 다를수 있습니다. display: none이 적용된 Node의 경우 DOM Tree에는 존재하지만 렌더트리에는 존재하지 않습니다.
실제로 화면에 나타나는 요소들만 렌더트리에 포함됩니다. - 렌더트리의 요소들이 뷰포트 내에 어떻게 보여질지 계산을 합니다. 이때 rem, vw, vh, %와 같은 수치들이 px로 계산됩니다. 이 과정을 layout이라 합니다.
- 계산된 결과물을 화면에 그리게 됩니다. 이 과정을 painting이라 합니다.
- 화면에 HTML이 나타납니다.
CRP(Critical Rendering Path)는 HTML,CSS 등이 화면에 렌더링 되는 경로를 의미합니다.
CRP를 최적화 하는 작업은 위의 단계들을 수행할때 걸리는 시간을 최소화하는 작업입니다.
HTML을 parsing 하다가 script tag를 만나면 parsing을 멈추는 이유는
script가 html의 요소를 변화시킬수 있기 때문입니다.
parallel하게 진행이 된다면 결과를 도출해 내기가 매우 어려울 것입니다.
Html, css가 화면에 보이는 과정은 아래와 같습니다.
- DOM 트리를 구축하는 부분입니다
- CSSOM 트리를 구축하는 부분입니다.
- 렌더트리를 구축하는 부분입니다.
- 레이아웃이 이루어지는 단계 입니다.
- 페인팅이 이루어지는 단계입니다.
이제 Reflow, Repaint를 다시 살펴보겠습니다.
Reflow는 렌더트리의 변경으로 인해 레이아웃 -> 페인트 -> 합성이 이루어지는 것을 말합니다.
Repaint는 페인트 -> 합성만 이루어지는 것을 말합니다.
Reflow는 4 -> 5, Repaint는 5 번의 과정이 다시 이루어지는 것을 뜻하게 됩니다.
4번 단계는 레이아웃 단계로서 노드들이 뷰포트 내에 실제로 위치하게 되는 영역을 계산하는 영역입니다.
불필요한 리플로우를 유발하는 동작들은 계산을 계속 발생시킴으로써 약간의 시간 소요가 발생하게 될수 있습니다.
css 요소중 일부 속성은 그래픽 레이어에 포함됩니다.
페인트 과정에서 노드들은 별도의 레이어(렌더 레이어)로 구성되게 되는데 이때 그래픽 레이어로 빠지게 되면 gpu에 의해 연산이 이루어지게 됩니다. cpu의 연산을 줄일수 있어 효율적입니다.
그래픽 레이어에 포함되는 css요소를 살펴보겠습니다.
css3d, transform, animation 과 같은 css 요소들은 gpu에 의해 연산이 이루어져 cpu연산을 줄일 수 있습니다.
Reflow, Repain
리플로우를 유발하는 css 요소, DOM api들과 예시 코드를 살펴보겠습니다.
리플로우를 유발하는 css 요소
height, weight, left, top, font-size, line-height등과 같은 요소들을 javascript로 변경하게 되면 리플로우가 일어납니다.
아래의 사이트를 방문하면 각 속성별로 렌더링 엔진에서 레이아웃, 페인트가 일어나는지 확인할 수 있습니다.
리플로우를 유발하는 DOM api
Element
clientHeight | clientLeft | clientTop | clientWidth |
focus | getBoundingClientRect | getClientRects | innerText |
offsetHeight | offsetLeft | offsetParent | offsetTop |
offsetWidth | outerText | scrollByLines | scrollByPages |
scrollHeight | scrollIntoView | scrollIntoViewIfNeeded | scrollLeft |
scrollTop | scrollWidth |
MouseEvent
layerX | layerY | offsetX | offsetY |
Window
getComputedStyle | scrollBy | scrollTo | scrollX |
scrollY |
Frame, Document & Image
height | width |
위의 요소들을 Dom Api로 호출하거나 변경한다면 리플로우가 일어납니다.
리플로우를 불필요하게 유발하는 코드와 개선 예시를 살펴보겠습니다.
두개의 예시가 있습니다.
첫 번째 예시 입니다.
elementA.className = "a-style"
var heightA = elementA.offsetHeight; // layout단계가 필요하게 됩니다. 위에서 class가 변경되었으므로 스타일의 변경이 없으리라는 보장이 없기 때문입니다.
elementB.className = "b-style"
var heightB = elementB.offsetHeight; // layout단계가 필요하게 됩니다. 위에서 class가 변경되었으므로 스타일의 변경이 없으리라는 보장이 없기 때문입니다.
위에서의 코드는 레이아웃 단계가 두번 호출됩니다.
아래와 같이 변경하면 한번의 레이아웃 과정만 수행하게 됩니다.
elementA.className = "a-style"
elementB.className = "b-style"
var heightA = elementA.offsetHeight; // layout 단계가 필요하게 됩니다. 위에서 class가 변경되었으므로 스타일의 변경이 없으리라는 보장이 없기 때문입니다.
var heightB = elementB.offsetHeight; // layout 단계가 필요하지 않습니다. 현재 상태는 style의 변경이 없는 상태라는 것을 확신할 수 있기 때문입니다.
위에서의 예시로 레이아웃을 유발하는 코드들은 몰아서 사용하는 것이 좋을것 같습니다.
두번째 예시입니다.
function resizeAllParagraphsToMatchBlockWidth() {
for(var i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
위의 코드는 for 문이 돌때마다 layout과정이 이루어 집니다.
offsetWidth이 호출될때 최신 상태의 결과를 가져오기 위해 layout을 수행하여 결과를 가져오기 때문입니다.
var width = box.offsetWidth
function resizeAllParagraphsToMatchBlockWidth() {
for(var i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = width + 'px';
}
}
위와 같이 변경하면 리플로우는 한번만 이루어 집니다.
이미 이전에 계산한 값을 계속 사용하기 때문입니다.
DOM
위에서 등장한 DOM이란 어떤 것일까요???
HTML이나 XML문서를 실체로 나타내는 API를 의미합니다.
DOM(Document Object Model)은 문서로 존재하는 HTML을 메모리 상에 구조화 하여 표현함으로써 스크립트로 제어할 수 있게 해줍니다.
DOM의 API를 사용하면 DOM의 트리에 접근할 수 있습니다. 이를 통해 문서의 구조, 스타일을 변경할 수 있습니다.
DOM의 Document는 HTML 문서를 의미합니다
js외에 python, rails에서도 dom에 접근하여 제어할 수 있습니다.
BOM이란 것도 존재합니다.
BOM은 Browser Object Model로 브라우저 자체를 다루기 위한 API를 의미합니다.
location, navigator, screen등으로 구성되어 있습니다.
가상 돔이란 DOM의 구조만 간결히 흉내낸 자바스크립트 객체입니다.
React와 같은 라이브러리에서 DOM을 효율적으로 업데이트하기 위해 등장하였습니다.
여러번의 속성, 스타일의 변화로 사용자의 화면에 나타나는 DOM의 구조가 변경된다면 매번 reflow가 발생하게 됩니다.
가상 돔은 이러한 변화를 메모리상에 존재하는 객체에 업데이트 하고 실제 DOM과 비교하여 변경이 이루어진 부분만 실제 DOM에 업데이트 할수 있게 해줍니다.
가상 돔을 사용함으로써 여러번 Reflow가 이루어지던 것을 한번의 Reflow로 같은 결과를 출력할 수 있게 해줍니다.
layout thrashing, requestAnimationFrame
layout thrashing이란 리플로우 전에 리플로우를 호출하여 성능저하가 발생하는 현상을 의미합니다.
애니메이션을 발생시켜 다음좌표로 이동하라고 호출하였는데 리페인트가 끝나기도 전에 다시 다음 좌표로 이동하라고 호출하면 애니메이션은 버벅이게 됩니다.
이러한 경우 requestAnimationFrame을 사용하여야 합니다.
requestAnimationFrame은 리페인팅을 하는것이 아닙니다.
브라우저에게 다음 리페인트가 진행되기 전에 실행해야 함수를 알려줍니다.
window.requestAnimationFrame()은 브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 호출하게 합니다. 이 메소드는 리페인트 이전에 실행할 콜백을 인자로 받습니다.
따라서 시간을 인자로 넘기지 않아도 됩니다.
아래와 같이 사용합니다.
window.requestAnimationFrame(callback)
const sayHi = () => {
console.log('Hi');
requestAnimationFrame(sayHi);
};
requestAnimationFrame(sayHi);
브라우저에서 콜백의 수는 보통 1초에 60회 입니다. 대부분의 브라우저에서는 콜백의 수가 디스플레이의 주사율과 일치하게 됩니다.
layout thrasing을 발생시키지 않고 리페인트 전에 함수를 호출할 수 있어 애니메이션을 구현할때는 requestAnimation을 사용하는 것이 좋습니다.
출처: https://all-young.tistory.com/22
https://www.youtube.com/watch?v=1ojA5mLWts8
https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/
https://darrengwon.tistory.com/641
https://kellegous.com/j/2013/01/26/layout-performance/
https://boxfoxs.tistory.com/408
'브라우저' 카테고리의 다른 글
Browser B/F cache 💰 (0) | 2022.04.22 |
---|---|
브라우저 캐싱 ❓ (0) | 2022.04.06 |
JWT/JWK (0) | 2021.12.06 |
localStorage, sessionStorage (0) | 2021.11.14 |