Reflow와 Repaint (+ 브라우저 렌더링 과정 요약)

woolee의 기록보관소·2022년 12월 3일
0

FE개념정리

목록 보기
22/35

브라우저 렌더링 과정 요약

브라우저가 렌더링하는 과정은 다음과 같이 요약할 수 있다.

  1. 사용자가 URL을 입력하자마자, 브라우저는 서버에서 HTML 소스코드를 가져온(fetchs) 다음 해당 HTML을 구문 분석하여(parses) <head>, <body>, <div>와 같은 토큰(tokens)으로 변환한다. 그리고 이 토큰은 결과적으로 노드(nodes)로 변환된다. 그래야만 우리는 DOM Tree를 가질 수 있다.

  2. DOM Tree 다음에는 CSS 파일로부터 CSSOM Tree가 생성될 차례이다.

  3. 마지막으로 DOM 및 CSSOM Tree는 단일 RenderTree로 결합된다.

  4. RenderTree는 크게 세 단계를 밟아 구성된다.

  • DOM Tree의 root에서 시작해 어떤 요소가 표시되고 계산된 스타일인지 계산한다.
  • meta, script, link 와 같이 안 보이는 요소와 "display: none;”;인 요소들을 무시한다.
  • 각각의 visible node에 적절한 CSSOM 규칙을 일치시키고 적용한다.
  1. 이 모든 단계가 완료된 후에야 브라우저는 화면에 항목을 표시하고(Reflow), 스타일(Repainting)하는 방법을 고민하기 시작한다. (물론 정확히는 항목을 표시하고 스타일링을 한 뒤에 변경사항이 발생하면 Reflow와 Repainting 단계를 거치는 것이다)

Reflow

Reflow는 웹 브라우저가 문서에서 요소의 위치와 형상을 다시 계산하는 데 사용하는 프로세스이다. 기본적으로 무언가가 변경될 때마다(a refresh, a screen resize or a common show/hide content action) 브라우저가 화면의 모든 요소를 배치하기 위해 수행해야 하는 노력이다.

Reflow는 요소에 대한 변경이 페이지 일부 또는 전체 페이지 레이아웃에 영향을 미칠 때 발생한다. 요소의 Reflow는 DOM에 있는 자식 및 조상 요소의 subsequent reflow를 유발할 것이다.

또한 Reflow operation은 a user blocking operation이다(즉, reflow를 완료할 때까지 사용자가 페이지와 상호작용할 수 없다).

Repaint

Repaint는 브라우저가 배경색이나 글꼴 크기와 같이 렌더링된 트리에 배치된 요소에 올바른 모양을 제공하기 시작하는 프로세스에 대한 정의이다.

숨겨진 요소를 표시하거나, 노드 애니메이션 또는 텍스트 색상 변경과 같은 상황에서도 발생한다.

Reflow와 Repaint를 야기하는 작업들

Reflow와 Repaint 모두

  1. padding/margin/border와 같은 CSS 속성을 변경할 때
  2. font-size를 변경할 때
  3. 요소에 DOM을 추가하거나 제거할 때
  4. display: none;으로 DOM 요소를 숨길 때
  5. stylesheet를 추가하거나 제거할 때
  6. Window scroll 할 때 => Reflow의 경우 Affects only if a layout change is made to the page
  7. input box에 텍스트 입력할 때
  8. style attribute을 사용할 때
  9. class attribute을 조작할 때 => Repaint의 경우 Affects only if the class change some visuals to the page

Reflow만

  1. Window resize할 때
  2. JS로 getComputedStyle()를 호출할 때
  3. JS로 windows dimensions를 얻을 때
  4. JS에서 scroll() 함수 사용할 때
  5. JS로 box metrix를 얻을 때
  6. element.focus()로 요소 포커스 설정할 때

Repaint만

  1. background or border color를 변경할 때
  2. visibility: hidden;로 DOM 요소를 숨길 때

repaints와 reflows를 최소화하는 방법?

DOM depth의 깊이를 줄인다.

예를 들어, 20개의 <div><span> 요소가 동일한 부모 요소의 모든 자식을 가질 필요가 없다. reflow가 발생하면 변경된 노드의 위, 아래에 있는 DOM 트리 노드도 reflow되며 자연스럽게 트리에 노드가 많을수록 브라우저는 더 노력해야 한다.

clean CSS sheet를 가져야 한다.

repainting은 브라우저가 시간과 리소스를 소모하므로 properties와 attributes을 가능한 적게 사용해야 한다. CSS sheet에서 필요하지 않은 부분은 제거해야 한다.

또한 너무 많은 CSS 선택자를 남용하지 않는 것도 중요하다. 그래야 CPU power가 더 적게 소모된다.

개별적으로 CSS styles을 변경하지 말아야 한다.

// bad
let left = 8, top = 8;
element.style.left = left + "px";
element.style.top = top + "px";

// better 
element.classList.add += "modifier";

또한 변경사항이 많은 구성 요소가 있는 경우, wrapper 요소가 아니라 변경이 발생할 보다 구체적인 요소에 클래스를 적용하는 게 가장 좋다. 이는 브라우저가 reflow해야 하는 RenderTree의 양을 잘 관리하는 데 도움을 준다.

계산된 동적 정보를 사용해 뭔가를 변경해야 할 때,
JS의 .cssText 속성을 활용하면 좋다. 물론 남용하지 말아야 한다.

element.style.cssText += "; left: " + left + "px; top: " + top + "px;";

DOM 변경을 일괄적으로 모아서 해야 한다(Batch).

너무 많은 computed styles은 피해야 한다.

".offsetTop" 또는 ".clientHeight"의 경우 스타일을 요청할 때마다 너무 많은 계산을 해야 하므로 피하는 것만으로도 엄청난 개선을 노릴 수 있다. 기본적으로 필요한 정보는 한번만 요청한 뒤, 결과를 저장한 뒤 반복하면 된다.

let myNode = document.getElementById("element"); // get a node 
let left = myNode.offsetLeft, // storing what we need in variables 
    top = myNode.offsetTop, 
    style = myNode.style;

for(loop here) {
  style.left = left + "10px";
  style.top = top + "10px";
}

inline styles을 피해야 한다.

style 속성을 통해 스타일을 설정하면 reflow가 발생하고 결과적으로 동일한 요소에 여러 스타일을 추가하면 각 스타일이 reflow를 유발할 수 있다.

가능하면 모든 스타일을 하나의 단일 클래스로 결합하는 게 좋다.

absolute/fixed positioning을 사용하라

일반적으로 요소의 애니메이션이 느리고 결함이 있는 원인은 정확히 화면을 재배치하고 다시 칠하는 동안 브라우저가 투입해야 하는 노력의 양 때문이다.

이때 우리는 GPU를 사용해 브라우저에 더 많은 처리 능력(processing power)을 제공할 수 있다.

CSS 성능을 개선하기 위해 사용할 수 있는 트릭 중 하나는 변경될 요소에 absolute/fixed positioning을 사용하는 것이다. 이렇게 하면 요소에 애니메이션을 적용해도 레이아웃의 다른 요소에 영향을 주지 않는다. 요소가 문서 부모 요소의 자식이 되고 전체 DOM reflow가 아닌 페이지 일부에 대한 reflow만 필요하기 때문이다.

불필요한 smoothness를 줄여라

화면의 요소에 애니메이션을 적용할 때 1px씩 줄 수도 있지만 애니메이션이 길면 CPU가 reflow 양과 subsequent calculations을 처리하는 데 어려움을 겪기 시작할 수 있다. 대신 3px 또는 4px로 늘리는 것만으로도 CPU가 수행해야 하는 reflow 양과 subsequent calculations을 크게 줄일 수 있다.

이를 통해 우리는 빠른 장치에서는 부드러움을 잃지 않고, 구형 장치에서는 애니메이션을 느리게 만들어 사용자 경험을 방해하지 않을 수 있다.

개발자 도구로 문제점 파악하는 방법

브라우저에서 문제점을 파악할 수 있다.
예를 들어 크롬 브라우저라면, 개발자 도구를 켠 뒤 performance 탭에서 동그라미 버튼을 누르면 녹음이 시작되는데, 이 녹음이 진행되는 시간을 측정해 성능을 파악할 수 있다.

녹음이 완료되면 아래와 같이 요약을 확인할 수 있다.

위 그림을 확인해보면, reflow (rendering) 와 repaint (painting) 사이에 250ms 이상 경과한 것을 볼 수 있다.

event log 탭을 누르면, reflow와 repainting 관련 모든 이벤트를 확인할 수도 있다.

각각의 이벤트를 자세히 보면, 해당 요청을 수행하는 데 필요한 노드의 양과 시간을 알 수 있다. 아래 그림을 확인해보면, 레이아웃을 변경하는 데 25ms 이상 소요되었고 1119개의 노드에 영향을 미쳤다. #document란 전체 DOM 트리에 영향을 미쳤다는 걸 의미한다.

참고

Avoid Reflow & Repaint
Reflow or Repaint(or ReDraw)과정 설명 및 최적화 방법

profile
https://medium.com/@wooleejaan

0개의 댓글