본문 바로가기
FE Development/Web

리플로우와 리페인트 - 브라우저의 렌더링 과정

by 개발자 데이빗 2022. 2. 20.

렌더링 과정 (Critical Rendering Path)

DOM 트리 생성

마크업 언어로 만들어진 문자열을 메모리에서 다루기 위해 HTML 태그를 파싱해 DOM 트리를 구성한다.

DOM은 데이터의 표현식으로 모든 HTML 태그는 태그 테이터의 표현식인 DOM 요소로 1:1로 대응해 표현된다.

메모리에 만들어진 객체를 document라고 하고 구조를 DOM이라고 한다.

이 구조는 트리형태로 구성되며 이를 DOM트리 라고 한다.

 

CSSOM 트리 생성

StyleSheet을 파싱하여 CSSOM (스타일 구조체)를 생성한다.

스타일 정보는 단계적으로 처리되며 가장 마지막 단계의 스타일 정보가 이전 스타일보다 우선적으로 적용된다.

 

  • 스타일 정보의 처리 단계
    1. 브라우저 자체에 포함된 기본 스타일 정보 (User Agent 스타일시트)
    2. 사용자 정의 스타일 (외부 파일 또는 내부 정의 스타일)
    3. 인라인 스타일 정보

 

렌더 트리 생성

DOM 트리와 CSSOM 트리를 통해 렌더 트리를 생성한다.

 

렌더 트리는 DOM 트리와는 다르게 각 노드에 스타일 정보가 설정되어 있고 화면에 표현되는 노드로 구성된다.

<head>, <title>, <script> 같은 태그의 경우 화면에 표현되는 노드가 아니므로 렌더트리에 포함되지 않는다.

즉, DOM트리와 1:1로 대응되지 않는다.

 

렌더트리에서 각 노드는 frame이나 box로 불리며 css 박스 속성정보가 있다.

 

레아이웃 처리

렌더 트리의 각 노드의 크기가 계산되고 문서에서 정확한 위치에 배치되도록 위치를 계산한다.

이 과정은 CSS 비주얼 렌더링 모델(CSS Visual Rendering Model)에 의해 제어된다.

 

페인트

Layout 과정을 통해 화면에 표현하기 위한 계산이 끝나면 Paint 과정을 거친다.

페이지의 Layer가 여러개라면 각각의 Layer를 Painting 한 뒤 하나의 이미지로 합성(Composite)하는 과정을 거쳐 브라우저에 표현된다.

렌더링 엔진은 렌더 트리를 순회하면서 paint() 함수를 호출해 노드를 화면에 표현한다.

 

 

리플로우와 리페인트

리플로우와 리페인트 모두 처리 비용이 발생하지만 리페인트보다 리플로우의 비용이 훨씬 높다.

리플로우와 리페인트는 UI의 화면 표현을 느리게 해 UX에 안좋은 영향을 줄 수 있다.

 

리플로우와 리페인트가 발생하는 원인

최초 렌더링이 완료된 상태에서 사용자의 인터랙션 또는 해당 페이지 기능에 따라 화면 일부의 변경이 발생한다.

이러한 경우 구성되어 있는 렌더 트리가 변경되어야 하며 이때 리플로우 또는 리페인트가 발생한다.

 

리플로우와 리페인트

  • 리플로우 - JS / CSS > 스타일 > 레이아웃 > 페인트 > 합성 
    • 변경이 필요한 렌더 트리에 대한 유효성 확인 작업과 함께 노드의 크기와 위치를 다시 계산한다.
    • 노드의 크기 또는 위치가 바뀌어 현재 레이아웃에 영향을 미쳐 다시 배치 해야 할 때 리플로우가 발생한다.
    • 리플로우가 발생하면 요소의 DOM 구조에 따라 자식 요소와 부모 요소 또한 다시 계산될 수 있으며 경우에 따라 문서 전체에 리플로우가 팔생할 수 있다.
    • 리플로우 또는 레이아웃 또는 레이아웃팅이라고 한다.
  • 리페인트 - JS / CSS > 스타일 > 페인트 > 합성
    • 리플로우가 발생하거나 배경색 변경 등의 단순한 스타일 변경과 같은 작업이 발생하는 경우에 리페인트가 발생한다.
    • 리페인트 또는 리드로우라고 한다.
  • JS / CSS > 스타일 > 합성
    • 가장 이상적이고 비용이 가장 적게 든다.
  •  Layout과 Painting이 발생하는 속성을 확인 할 수 있는 CSS Triggers 사이트

 

주요 발생 원인

  • DOM 노드의 변경
  • DOM 노드의 노출 속성을 통한 변경 - display: none은 리플로우 발생시키지만 visibility: hidden은 요소가 차지한 영역을 유지해 리페인트만 발생시킨다.
  • 스크립트 애니메이션 - 애니메이션은 DOM 노드의 이동과 스타일 변경이 짧은 시간 내에 수차례 반복해 발생되는 작업이다.
  • 스타일 - 새로운 스타일시트의 추가를 통한 스타일 변경
  • 사용자의 액션 - 브라우저 크기 변경, 글꼴 크기 변경 등

 

브라우저의 리플로우 최적화

렌더트리 변경으로 인한 리플로우와 리페인트는 비용이 많이 든다.

브라우저는 비용을 줄이기 위해 이러한 작업을 바로 실행하지 않고 나중에 실행하는 방법으로 최적화 한다.

관련 작업을 큐에 쌓고 일정 시간 또는 일정 수의 작업이 쌓인 이후에 Batch 처리를 통해 리플로우를 한번으로 줄인다.

 

브라우저의 리플로우 최적화를 중단시키는 속성과 메소드

 

기본적으로 브라우저는 값을 반환하도록 요청받으면 요소의 최신 상태 정보를 반환하려고 한다.

그러므로 값을 반환하도록 요청 받은 시점에 요소의 특정 속성이나 메소드를 이용해 값을 요청할 시 큐에 쌓인 작업들이 요소의 스타일 정보에 영향을 줄 수 있으므로 큐의 작업을 모두 실행한 후 요청된 값을 반환한다.

이러한 작동방식은 자바스크립트의 이벤트 루프를 이해하면 더 와닿는다.

이런 이유로 특정 속성이나 메소드를 이용해 값을 요청할 시 브라우저의 최적화를 방해하고 추가 리플로우를 발생시킬 수 있다.

 

아래는 브라우저의 리플로우 최적화를 중단시키고 추가적인 리플로우를 발생하는 속성과 메소드이다.

객체 속성 및 메서드
HTMLElement clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth 
 Frame, Image height, width 
 Range getBoundingClientRect(), getClientRects() 
 SVGLocatable computeCTM(), getBBox()
SVGTextContent getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString() 
SVGUse instanceRoot
window getComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()

 

참고자료

https://velog.io/@young_pallete/Reflow-Repaint%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

 

https://cresumerjang.github.io/2019/06/24/critical-rendering-path/

 

https://12bme.tistory.com/140

'FE Development > Web' 카테고리의 다른 글

HTTP/1.1 vs HTTP/2.0  (0) 2022.03.02
Web Storage ( 브라우저 저장소 )와 쿠키  (0) 2021.10.07

댓글