성능 보장 렌더링 순서

클라이언트가 서버로부터 웹페이지를 받아와서, 브라우저에 표기되기 까지 어떤 렌더링 순서를 가지는지 알아본다. 이는 성능을 보장을 위해 필수적이다.

Rendering path

우선 클라이언트가 서버로부터 데이터를 받아와서 브라우저에 표기하는 전체적인 순서는 다음과 같다.

  1. request / response : 브라우저가 서버에게 html 파일 요청
  2. loading : html 로딩
  3. scripting : 각 요소들을 DOM & CSS요소들은 CSSOM으로 변환
  4. rendering : 사용자에게 표시될 요소들을 렌더링 트리로 변환
  5. layout : 각각 요소들의 위치 및 크기 등을 계산
  6. painting : window에 표시

  • Construction 파트 (DOM,CSSOM,RenderTree) : HTML 페이지에서 브라우저가 이해할 수 있도록 브라우저만의 언어로 바꾸는 파트
  • Operation 파트(layout, paint, composition) : 렌더링 트리를 이용하여 구조를 작성하고 어디에 배치할 건지 계산을 한 다음, 실제로 브라우저 window에 그림을 그려주는 파트

layout & paint ?

우선 layout은 만들어진 Render Tree(DOM 요소, 최종적으로 계산된 CSS 스타일이 포함된 트리)를 기반으로 window 위에서 요소의 위치 및 크기 등을 정의하고 배치한다. 즉, 레이아웃을 구상하게 된다.

이렇게 구상된 레이아웃을 통해서 paint 과정이 일어나게 되는데, 이 paint 과정은 바로 계산된 아이들을 브라우저 위에 그림을 그리는게 아니라, 각각 부분들을 잘게 나누어서 이미지를 준비한다.(레이어화 시킨다) 즉, 컴퓨터가 이해할 수 있는 이미지의 형태(비트맵) 데이터로 변환한다.

예를 들어 CSS에서 z-index를 사용하게 되면, 같은 위치에 있는 요소들은 묶어서 하나의 레이어로 만들고, 또 다른 위치에 있는 요소들도 묶어서 하나의 레이어로 만드는 등 레이어별로 paint를 준비해놓는다. (꼭 z-index 별로 레이어를 나누는게 아니라, 다양한 속성값에 따라 브라우저 엔진마다 성능 개선을 위해 레이어를 만든다)

왜 한 번에 그리는게 아닌, 레이어별로 준비하는 걸까?

이것은 성능 개선을 위해 브라우저 엔진이 준비하는 것이다.
요소의 위치를 움직이거나, 투명도를 조금 변환할 때를 생각해보자.
레이어 기능을 이용하지 않고 전체적으로 그림을 그리게 되면 브라우저는 이 전체 웹페이지를, 즉, 이 한 부분을 바꾸기 위해서 다시 전체적으로 그림을 그려야 한다.

하지만, 이렇게 레이어 단위로 처리하게 되면 부분적으로 그 레이어만 수정하면 되기 때문에 성능이 개선되는 것이다.

그래서 CSS에는 ‘will-change’ 라는 속성 값이 있는데, 이 값은 우리가 미리 브라우저에게 추후 변경될 수 있음을 알려주는 것이다. 이 속성 값을 받은 브라우저는 미리 이 요소를 따로 빼서 새로운 레이어에 추가해둔다. 그래서 요소가 실제로 바뀌었을때, 그 레이어만 수정하면 되게끔 준비하는 것이다. 하지만 이 ‘will-change’ 속성값을 불필요하게 너무 많이 쓰는것도 좋지 않다. 브라우저가 쓸데없이 레이어를 너무 많이 만들기 때문에, 성능이 오히려 저하될 수 있다. 그래서 이런 레이어는 중요하지만 너무 많이 남용해서 사용하면 안 된다.

composition 단계

paint  과정을 통해 만들어진 레이어들을 순서대로 브라우저 위에다가 표기한다. z-index가 높은 요소를 제일 마지막에 올리는 등 만들어진 레이어들을 함께 모아서 표기하는 것이다. 이렇게 브라우저가 표기할 수 있는 이 단계까지를 ‘critical rendering path’라고 부른다.

✏️ Operation정리

  • layout : 각 요소들을 배치
  • paint : 그려져야할 요소들을 레이어화
  • composition : 레이어들 순서대로 표기

Construction 파트에서 성능을 향상시키는 방법

DOM 요소가 작으면 작을 수록, CSS 규칙이 작으면 작을 수록 Tree가 작아지기 때문에 성능이 향상된다. 그래서 불필요한 태그를 쓴다던지, 불필요하게 div 태그를 남용한다던지, 쓸데없이 wrapping 클래스나 wrapping 요소를 만든다던지 하는 것은 자제하는게 좋다.

💻 Operation 파트에서 성능을 향상시키는 방법

paint 과정이 자주 일어나지 않도록 만드는게 중요하다. 예를 들어, 페이지에 있는 네모 박스를 translate를 이용해서 움직이는 것은 paint가 일어나지 않는다. 이미 레이어가 준비되어 있기 때문에, 레이어의 위치만 옮기면 된다. 즉, composition 만 일어나면 된다.

그러나 만약, 이렇게 위치를 바꾸는데 무언가 다시 그림이 그려져야 된다면, paint 과정이 다시 발생하기 때문에 성능에 좋지 않다. 최악의 경우는 layout 과정이 다시 일어나는 것이다. 만약 이 박스를 움직임으로써 다른 주변에 있는 아이들의 포지션이 바뀌어야 한다면, 브라우저는 다시 layout 과정으로써 요소별 위치를 계산하게 되고, 또 그것을 기반으로 다시 paint가 이뤄지고 , composition이 일어나기 때문에 성능이 나빠질 수 밖에 없다.

그래서 자바스크립트나 CSS로 DOM 요소를 조작할 때 composition만 일어나면 가장 이상적이고, paint 가 일어나는 것까진 괜찮다.
애니메이션을 하는데, layout  과정이 다시 발생해야 된다고 하면, 그 애니메이션이 꼭 필요한지 다시 생각해보아야 한다.

Layer 테스트

(개발자 툴에서 레이어 확인)

1. 속성을 아무것도 주지 않았을 때

2. image에 z-index를 100을 줬을때

img {
  z-index: 100;
}
z-index : 100;

z-index에 100을 줬을때 브라우저 입장에서는 어차피 한번에 표기되니까 똑같은 레이어로 표시 된다.

2. will-change  옵션을 줬을 때

img {
  will-change: opacity;
}
will-change: opacity;

will-change 속성을 주면 위 이미지와 같이 새로운 레이어가 생성되는 것을 확인할 수 있다.

will-change 속성을 안줬을 경우에 opacity가 변경되면 전체 그림을 다시 그려야한다. will-change 속성을 주면, 브라우저가 해당 요소를 새로운 레이어에 추가하기 때문에, 값이 바뀌었을 때 그 레이어만 변경하면 된다.