Featured image of post 랜더링 업데이트 최적화

랜더링 업데이트 최적화

화면이 수정될 때, 렌더링 과정을 최적화하는 방법

재배치(Reflow)와 다시 그리기(Repainting)

처음 화면이 그려진 후 사용자의 인터렉션에 의해 요소가 변경되는 일이 발생하였을때, Render 트리가 변경되면서 발생한다.

이 과정이 빈번하게 발생할 수록 성능 및 사용자 경험이 저하되기 때문에 이를 최소화하는 것이 좋다.

다시 그리기(Repainting)

재배치가 발생하거나 요소의 색상등이 변경된 경우, 다시 화면에 표현하는 동작.

  • 화면의 구조가 변경되었을 때
    Reflow 과정을 거쳐 화면 구조를 다시 계산한 후 Repaint 과정을 통해 화면을 다시 그린다.

    • 화면의 구조가 변경되었을 때에는 Reflow와 Repaint 모두 발생합니다.
  • 화면의 구조가 변경되지 않는 화면 변화의 경우
    Repaint만 발생화면 구조(Layout)이 변경되었을 때, 뷰포트 내에서 렌더 트리의 노드의 정확한 위치와 크기를 계산하는 과정을 다시 수행해야 한다.

    • opacitybackground-colorvisibilityoutline 등의 스타일 변경 시에는 Repaint만 동작한다.

Repaint는 변경된 화면을 실제 화면에 반영하는 과정으로 최적화할 수 있는 방법은 화면 변화를 최소화할 수 있는 방법 뿐이다.

재배치(Reflow)

화면 구조(Layout)이 변경되었을 때, 뷰포트 내에서 렌더 트리의 노드의 정확한 위치와 크기를 계산하는 과정

Reflow가 발생하는 경우

  • DOM 노드의 추가, 제거
  • DOM 노드의 위치 변경
  • DOM 노드의 크기 변경(margin, padding, border, width, height 등..)
  • CSS3 애니메이션과 트랜지션
  • 폰트 변경, 텍스트 내용 변경
  • 이미지 크기 변경
  • offset, scrollTop, scrollLeft과 같은 계산된 스타일 정보 요청
  • 페이지 초기 렌더링
  • 윈도우 리사이징

위의 내용에서 빠졌더라도 화면의 구조가 변경되었다면 Reflow가 발생한다.

Reflow 최적화

재배치 작업은 다시 그리기 작업을 동반하기 때문에 경우에 따라 Render 트리 전체를 재구성할 수도 있으므로 다시 그리기만 발생할 떄에 비해서 비용이 훨씬 비싸다.

  • 재배치, 다시 그리기 작업을 최소화 하는 과정을 통해 최적화를 한다.

    • Reflow는 렌더 트리의 변화를 최소화하는 등..
  • DOM의 depth를 최소화

    • DOM의 깊이와 크기를 작게 구성하여 재배치를 더 빠르게 처리하게 만든다.
  • 스타일 변경을 한번에 처리한다.

    1
    2
    3
    4
    5
    
    .newstyles {
        width: 100px;
        height: 200px;
        margin: 10px;
    }
    
    1
    2
    3
    4
    5
    
    // 비효율적인 코드 예시
    var myelement = document.getElementById('myelement');
    myelement.width = '100px';
    myelement.height = '200px';
    myelement.style.margin = '10px';
    
    1
    2
    3
    
    // 개선된 예시
    var myelement = document.getElementById('myelement');
    myelement.classList.add('newstyles');
    

    미리 만들어놓은 스타일을 한번에 적용하여 재배치를 최소화 할 수 있다.

  • 주변에 영향을 주는 요소를 제한한다.
    인터렉션에 의해 크기나 위치가 변경되는 요소는 병경될 때 주변 요소들이 최대한 영향받지 않도록 정의한다.

    • 스타일을 변경할 경우 가장 하위 노드의 클래스를 변경
    • 애니메이션이 있는 노드는 positionfixed 또는 absolute 로 지정한다.
  • 개발자 도구를 이용하여 분석
    개발자 도구를 이용하여 재배치와 다시 그리기가 얼마나 발생하는지 확인하고 해당 요소를 최적화 시도한다.

  • 라이브러리를 사용한다.
    React, Vue는 트리 형태의 Object를 통해 Virtual DOM을 구성하고 요소가 변경될 때 업데이트한다. 그 후 최종 상태의 Virtual DOM을 실제 DOM에 반영하여 재배치와 다시 그리기를 최소화 시켜 렌더링 최적화를 구현한다.

  • 스타일을 변경할 경우 가장 하위 노드의 클래스를 변경

DOM 노드의 크기 또는 위치가 변경되면 하위 노드와 상위 노드까지 영향을 미칠 수 있다. 따라서 가장 하위 노드의 스타일을 변경할 경우, 전체 노드가 아니니 일부 노드로 영향을 최소화 할 수 있다.

하지만 실무에서는 보통 변경해야 할 노드들이 정해져 있기 때문에 적용 범위가 크지 않을 수 있다.

애니메이션이 있는 노드는 positionfixed 또는 absolute로 지정한다.

애니메이션 효과는 많은 Reflow 비용이 발생하게 됨.

position 속성을 fixed 또는 absolute 로 지정하면, 해당 노드를 전체 노드에서 분리시켜 일부만 Reflow가 발생하도록 제한시킬 수 있다.

애니메이션 효과를 줘야 하는 노드에 position 속성이 적용되지 않았다면 애니메이션 시작 시 position 속성 값을 fixed 또는 absolute로 변경하였다가 애니메이션 종료 후 다시 원복 시켜 렌더링을 최적화할 수 있다.

<table> 레이아웃을 피한다.

<table> 은 점진적으로 렌더링 되지 않고, 모두 로드되고 테이블 너비가 계산된 후 화면에 그려진다. 테이블 안의 콘텐츠의 값에 따라 테이블 너비가 계산된다.

콘텐츠의 값에 따라 테이블 너비가 계산되기 때문에, 테이블 콘텐츠의 작은 변경만 있어도 테이블 너비가 다시 계산되고 테이블의 모든 노드들이 Reflow가 발생한다.

부득이하게 <table>을 사용할 때는 table-layout:fixed 값을 지정하는 것이 좋다.

  • table-layout:fixed는 테이블의 콘텐츠의 길이에 따라 테이블의 너비가 계산되는 것이 아니기 때문에, table-layout의 기본 값인 auto에 비해 성능이 더 좋다. <table>을 레이아웃 용도가 아닌 데이터 표시 용도로 사용할 때도 table-layout:fixed를 지정하는 것이 성능 면에서 더 좋습니다.

IE의 CSS 표현식을 사용하지 않는다.

CSS 표현식은 비용이 매우 높기 때문에 사용을 피해야 함

1
2
3
.expression {
  width: expression(document.documentElement.clientWidth > 0 ? '1000px' : 'auto');
}

Reflow가 발생할 때마다 자바스크립트 표현식이 다시 계산되기 때문에 CSS 표현식은 비용이 비싸다.

  • 애니메이션이 동작한다면, 애니메이션에 의한 Reflow가 발생할 때마다 자바스크립트 표현식이 계산됨.

CSS 하위 선택자를 최소화한다.

1
2
3
4
5
6
7
8
/* 잘못된 예 */
.reflow_box .reflow_list li .btn{
    display:block;
}
/* 올바른 예 */
.reflow_list .btn {
  display:block;
}

CSS 하위 선택자를 최소화하는 것이 렌더링 성능에 더 좋다.

렌더 트리는 DOM과 CSSOM이 합쳐져서 만들어 지는데, DOM은 HTML이 파싱 되어 만들어진 트리이고, CSSOM은 CSS가 파싱 되어 만들어진 트리이다.

두 트리를 결합하여 렌더 트리를 만드는데, CSS 하위 선택자가 많아지만 CSSOM 트리의 깊이가 깊어지게 되고 결국 렌더 트리를 만드는 시간이 더 오래 걸릴 수 있다.

숨겨진 노드의 스타일을 변경한다.

display:none으로 숨겨진 노드를 변경할 때는 Reflow가 발생하지 않기 때문에 숨겨진 노드를 표시하기 전에 노드의 콘텐츠를 먼저 변경한 후 화면에 나타내면 Reflow를 줄일 수 있다.

클래스를 사용하여 한 번에 스타일을 변경한다.

스타일을 변경할 때, 스타일을 각각 변경할 경우 추가 Reflow가 발생할 수 있기 때문에 한번에 스타일을 변경하는 것이 좋다.

요약

  • Repaint(Redraw)는 화면에 변화가 있을 때 화면을 그리는 과정
  • Reflow(Layout)는 뷰포트 내에서 렌더 트리의 노드의 정확한 위치와 크기를 계산하는 과정
  • Repaint가 발생하는 경우는 화면이 변경되는 모든 경우
  • Reflow가 발생하는 경우는 화면의 구조가 바뀌었을 경우
  • Reflow를 최적화하는 방법
    1. 스타일을 변경할 경우 가장 하위 노드의 클래스를 변경한다.
    2. 인라인 스타일을 사용하지 않는다.
    3. 애니메이션이 있는 노드는 position을 fixed 또는 absolute로 지정한다.
    4. 퀄리티, 퍼포먼스의 타협점을 찾는다.
    5. <table> 레이아웃을 피한다.
    6. IE의 CSS 표현식을 사용하지 않는다.
    7. CSS 하위 선택자를 최소화한다.
    8. 숨겨진 노드의 스타일을 변경한다.
    9. 클래스를 혹은 cssText 사용하여 한 번에 스타일을 변경한다.
    10. DOM 사용을 최소화한다.
    11. 캐시를 활용한다.
  • 라이브러리를 사용한다.