프론트엔드/JavaScript

[JavaScript] 렌더링 최적화 (Reflow와 Repaint)

렌더링을 최적화하는 여러 방법 중 하나는 Reflow와 Repaint 과정을 줄이는 것이다.

먼저 Reflow와 Repaint에 대해 알아본 뒤, 다양한 최적화 방법을 소개하겠다.

 


📐 Reflow

html 요소의 크기나 위치가 변하면 그에 영향을 받는 노드들의 크기와 위치를 다시 계산하게 된다.

이렇게 요소의 변화에 따라 Layout 과정을 다시 수행하는 것을 Reflow라고 한다.

 

Reflow (Layout 단계)

 

Layout 계산 비용만큼 렌더링 시간도 늘어난다. (Chrome DevTools > Perfomance)

 

Reflow가 발생하는 사례

  • 페이지 초기 렌더링 (최초 Layout 과정)
  • 윈도우 리사이징 (Viewport 크기 변경)
  • DOM 노드 추가, 제거
  • DOM 노드 위치, 크기 변경
  • 폰트 변경, 텍스트 내용 변경
  • 이미지 크기 변경
  • 애니메이션, 트랜지션

 

Reflow가 발생하는 속성

position width height left top
right bottom margin padding border
border-width clear display float font-family
font-size font-weight line-height min-height overflow
text-align vertical-align white-space ....  

 


🎨 Repaint

Reflow가 발생한 후 새로 계산된 Render Tree를 다시 화면에 그려주는 과정이 필요하다.

이렇게 Paint 단계를 다시 수행하는 것을 Repaint 라고 한다.

 

무조건 Reflow가 발생해야 Repaint가 발생하는 것은 아니다.

background-color, visibility와 같이 레이아웃에 영향을 주지 않는 경우는 Repaint만 발생한다.

전체 픽셀을 다시 계산하는 Reflow와 달리, Repaint는 이미 계산된 값을 활용하기 때문에 비용이 적다.

 

Repaint (Paint 단계)

 

Reflow와 달리 Layout 계산 과정이 없다. 그만큼 렌더링 시간도 줄어든다.

 

Repaint가 발생하는 속성

background background-image background-position background-repeat background-size
border-radius border-style box-shadow color line-style
outline outline-color outline-style outline-width text-decoration
visibility ....      

 


💡 Reflow 최적화 방법

1️⃣ 불필요한 노드는 display: none

visibility: invisible은 Layout 공간을 차지하기 때문에 Reflow의 대상이 된다.

하지만 display: none은 Layout 공간을 차지하지 않아 Render Tree에서 아예 제외된다.

그러므로 불필요한 노드는 display: none을 활용하여 렌더링 성능을 향상할 수 있다.

 

2️⃣ Reflow 속성 사용 줄이기

가능하면 Reflow보다는 Repaint만 발생하는 속성을 사용하는 것이 좋다.

또한 Reflow, Repaint가 모두 발생하지 않는 transform, opacitiy와 같은 속성도 존재한다.

 

더 자세한 속성 정보를 알고 싶다면 이 사이트를 참조하면 된다.

 

CSS Triggers

@PROPERTY_DESCRIPTION@ B G W E Change from default B G W E Subsequent updates

csstriggers.com

 

3️⃣ 애니메이션은 position: fixed, absolute

애니메이션, 트랜지션 등의 많은 Reflow 연산을 발생시키는 효과들이 존재한다.

이때 position을 fixed나 absolute를 주어 노드를 분리하면 해당 노드만 Reflow가 발생하게 만들 수 있다.

 

4️⃣ 프레임 줄이기

요소가 이동하는 순간마다 Reflow, Repaint가 발생하게 된다.

따라서 이러한 트랜지션, 애니메이션 주기나 효과를 간소화하면 성능을 개선할 수 있다.

애니메이션은 퀄리티도 중요하지만 성능과의 트레이드오프를 생각하는 것도 중요하다.

 

5️⃣ 인라인 스타일 지양하기

인라인 스타일은 HTML이 파싱 될 때 레이아웃에 영향을 주어 추가적인 Reflow를 발생시킨다.

물론 유지보수 및 가독성 측면에서도 인라인 스타일을 지양하는 것이 좋기도 하다.

 

6️⃣ <table> 지양하기

테이블은 점진적으로 렌더링 되지 않고 내부 콘텐츠가 모두 로딩된 후에 그려진다.

그래서 작은 콘텐츠의 변경만 있어도 테이블의 모든 노드에 Reflow가 발생한다.

만약 쓰게 된다면, table-layout: fixed를 통해 테이블의 크기를 고정하는 것도 좋은 방법이다.

 

7️⃣ CSS 하위 선택자 줄이기

// bad
.container .list li .btn {
  background-color: red;
}

// good
.list .btn {
  background-color: red;
}

하위 선택자가 많아지면 CSSOM Tree의 깊이가 깊어지고 Render Tree를 만드는 시간이 늘어나게 된다.

또한 Reflow마다 부모 선택자를 매칭 하는 연산이 길어지는 만큼 성능에 영향을 미치게 된다.

 


성능 최적화는 프론트의 중요한 이슈인 만큼 알아야 할 내용이 깊고 방대하다.

비록 이번엔 얕은 수준으로 소개하지만 이번 글을 계기로 꾸준히 최적화에 대한 공부를 하고싶다.

 

'프론트엔드 > JavaScript' 카테고리의 다른 글

[JavaScript] 자료형 (Data Type)  (0) 2022.04.21
[JavaScript] 로딩 최적화  (0) 2022.04.01
[JavaScript] 브라우저 렌더링  (0) 2022.03.24
[JavaScript] this  (2) 2022.01.05
[JavaScript] Prototype  (0) 2022.01.05