브라우저 렌더링 최적화 (feat. Chrome)

gyomni·2022년 9월 17일
2

Week I Learned

목록 보기
20/20
post-thumbnail

렌더링 최적화를 왜 알아야할까?

frames per second

1초에 몇 프레임이 그려지는지를 표현한 단위로, 60fps는 1초에 60장의 프레임이 그려지는 것을 뜻한다.

한장의 프레임을 그리는데는 1000/60=16.666...

즉, 16.6ms안에 브라우저가 새로운 화면을 렌더링 하는 것을 성공하지 못할 경우
기기에서는 다음 화면을 그려내도 브라우저가 표현해야하는 다음 화면을 다 그리지 못한다.
=> 기기에서는 바꼈지만 브라우저에서는 바뀌지 못했기 때문에 animation이 끊겨서 보이는 현상이 생길 수 있다.

따라서 60fps를 가지고 있는 기기에서는 브라우저 렌더링 프로세스를 모두 수행하는데 걸리는 시간 제한이 16.6ms인 것.
그래서 위와 같은 단계를 16.6ms안에 수행해야 한다.
이게 쉬운 경우도 있지만 어려운 경우도 있으므로 불필요한 과정들을 생략하고 최대한 압축해서 위와 같은 과정을 16.6ms안에 수행해 내는 것이 기본적인 최적화이다. (요즘 기기 대부분 60fps)

크롬 브라우저 rendering path 이해하기

document.getElementById("Box").style.height = '100px';

코드를 보면 document에서 id가 Box인 tag를 가지고 와서 style object안의 height에 100px이라는 string값을 할당하고 있다.

    1) Recalculate Style
    : style값을 다시 계산하게 된다.

    2) height 속성의 변경으로 좌표 계산이 필요한지 확인한다.

    3) 만약 계산이 필요하다면 Layout이 발생한다.
    (=reflow)

    4) 효과적인 처리를 위해 Layer Tree를 업데이트한다.

    5) 계산된 영역의 정보를 Paint 즉, 그린다.
    (=repaint)

    6) Composite Layer작업에서 layer를 병합한 후 화면에 출력한다.

    => 부드러운 animation을 위해서는 위의 작업들이 모두 16.6ms내에 처리되어야 한다.
    그래야만 animation이 끊기지 않고 동작하는 것을 사용자에게 제공할 수 있다.

Layer Model


: 웹 페이지를 랜더링하기 위해 필요한 이미지 단위 요소

레이어는 웹 브라우저 화면이 있을 때 기존의 렌더링 프로세스와는 완전 별개로 렌더링 프로세스가 진행되는 또 하나의 화면이라고도 부를 수 있다.

이런 레이어가 여러개가 생겼을 때
최종적으로 브라우저 렌더링 프로세스를 거쳐서 어떠한 화면을 렌더링 할 것인지 모든 레이어를 병합하는 과정이 바로 composite layer이다.

-> 레이어들을 배치/합성하여 최종적인 웹 페이지를 표현
-> 모든 페이지는 root layer를 가짐
-> layer의 이미지는 텍스쳐로서 Paint 작업 시, CPU에 의해 Video Memory에 로드 되므로 layer 생성 비용이 크고, 추가 Memory가 필요하다.

기기 안에서 어떤 장치가 연산해서 각 렌더링 프로세스를 수행하는 것일까?

결국 이 장치가 어떤 연산을 통해 렌더링을 수행하기 때문에 비용이 작게드는 장치를 이용해서 렌더링을 한다면 더 빠른 렌더링이 가능해질 것이다. => 렌더링 최적화

Rendering 성능 개선 전략

script가 들어왔을 때

  • CPU에서는 Recalculate Style(스타일을 다시 계산하는 작업)이 일어난다.

  • 그리고 나서 layout과정이 일어난다.
    이 부분에서 브라우저 엔진 안에 있는 렌더링 엔진에 layout process가 CPU에서 일어난다.

  • 그런 다음 layer를 업데이트 하게 된다.
    메인 메모리로 결과를 전달해서 저장한 후
    Video Memory로 Paint라는 과정을 통해 전달을 하게 된다.

  • GPU안에서 연산을 하는데,
    이 연산이 바로 Compsite Layer이다.
    즉, 여러가지 layer들이 병합되는 작업을 GPU에서 수행.

    여기서 기억해야 할 것은, CPU가 동작 하기 위해 소모되는 computing power(연산 능력)보다 GPU가 훨씬 작아서 GPU가 훨씬 빨리 연산이 가능하다.(=Compsite Layer 작업 비용이 훨씬 적다)

        Layout, paint -> CPU에서 일어남
        Composite -> GPU에서 일어남

  • 따라서 렌더링 성능 개선을 위해서는!
    같은 DOM node를 동작시키는 코드라면!
    Layout, Paint를 발생시키는 것 보다 Composite을 발생시키는 것이 유리하다. (Composite의 연산비용이 훨씬 적기 때문)


    결론적으로,
    어떻게하면 렌더링을 더 빠르게 할 수 있을까? 성능을 개선할 수 있까?

    Layout, Paint를 줄이고 최적의 Layer를 구성하자 !


    => Layout, Paint를 최소한으로 발생시킨다.

    => DOM node가 똑같은 동작을 한다면 Layout과 Paint대신 Composite으로 대체한다!

    => Layer를 잘 분리하고, 불필요한 Layer를 제거한다.

Reflow / Repaint

이제 더 빠른 렌더링 성능 개선을 위한 방안들은 알게 되었다.
그렇다면, 어떻게
Layout, Paint를 줄이고, Layout, Paint 대신 Composite를 활용하고, 최적의 Layer를 구성할 수 있는가?

구체적으로 알아보자.

  • Reflow -> Layout
  • Repaint -> Paint
    로 이해하면 된다.

참고로 Repaint와 Reflow의 비용을 비교하는 것은 쉽지 않지만,
Reflow가 발생하면 Repaint는 무조건 발생하고,
반대로 Repaint가 발생하면 Reflow는 발생하지 않는 경우도 있다고 한다.

고로 Reflow를 최대한 발생시키지 않는 것이 Repaint를 발생시키지 않는 것보다
렌더링 비용을 절약하는 방법이 된다!

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 ....

    => 다 그런것을 아니지만,
    공통적으로 CSS box modeling에서 box size에 영향을 주는 CSS property들이다.

    => 그러므로 CSS box size를 바꾸게되면 layout이 새롭게 일어나기 때문에 layout 과정을 다시 수행해야 한다.

    => 위와 같은 속성의 값을 재할당할 경우,
    Layout이 다시 발생한다는 것을 인지하고 사용해야 한다.

    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 ....

    => 위와 같은 속성을 사용할 경우 Layout은 일어나지 않아서 렌더링 프로세스에 소모되는 비용이 훨씬 줄어든든다. (렌더링 프로세스가 더 빨리 일어날 수 있다.)

animation 구현에서

Reflow(Layout)이 일어나는 대표적인 속성에 position, left, top이 있는 것을 알 수 있다. animation을 구현하는 방법은 대표적으로 requestAnimationFrame, transform 이렇게 2가지를 들 수 있다.

1) requestAnimationFrame함수에서 position의 left/top을 1px씩 더해서 움직이게 하는 것

2) transform으로 움직이게 하는 것

위의 방법 중에서 transform으로 구현이 가능한 animation이라면,
되도록 transform를 사용하자.
( 복잡한 animation이라 transform을 사용할 수 없는 경우는 어떨수 없지만!... )

requestAnimationFrame보다 transform을 사용하는 것은 렌더링 최적화와 어떤 연관이 있는 걸까?

    => transform이라는 속성이 Layer를 분리한다.
    따라서 Layer가 분리되면 기존의 Layer가 진행하고 있던 브라우저렌더링 프로세스와 전혀 다른 별개의 브라우저 렌더링 프로세스가 생긴다고 생각하면 된다.

    즉, 싱글쓰레드가 멀티쓰레드처럼 동작하는 것.
    따라서 기존 렌더링 프로세스가 지연이 되더라도
    blocking없이 쭉 렌더링 프로세스가 진행될 수 있다.( transform속성을 사용하면 새로운 Layer를 만들기 때문에)

    그래서 속도가 더 빠르고 끊김 없는 animation으로 구현할 수 있게 된다.

    => 그러므로
    requestAnimationFrame, transform 둘다 사용해서 똑같은 animation구현할 수 있을 경우에는 transform을 사용하는 것이 성능적인 측면에서 훨씬 도움이 된다.

    결국 Layer를 분리해서 렌더링 한 후에 그것을 Composite하는 과정은 GPU에서 수행하는데, 이 과정에서 만큼은 GPU에 수행하는 데 필요한 computing power(연산 비용)은 CPU가 소비하는 비용보다 훨씬 더 작다.

최적의 Layer 구성

DOM node에서 필요하지 않는, 사용하지 않는 Layer들을 display:none을 할당해서 지울 수 있다.


Layout, Paint을 최소화 시키고
대체할 수 있다면 Composite으로 대체하자!

이것이 렌더링 최적화 기법의 핵심이라고 할 수 있다.

그래야만 computing power를 덜 사용해서 rendering process가 개선되서 더 빨리 렌더링을 할 수 있다!

목표는 16.6ms!!

참고 자료

profile
Front-end developer 👩‍💻✍

0개의 댓글