브라우저의 렌더링 과정
parsing → attachment → layout → paint → composite
- parsing : DOM Tree, CSSOM Tree 생성
- attachment(style) : DOM Tree, CSSOM Tree를 합쳐 Render Tree 생성
- layout(Reflow) : Render Tree의 모든 노드들의 위치, 크기 등 정확한 값을 계산
- paint : 계산된 렌더 트리를 화면상 기준으로
- composite : 레이어를 합성하여 실제 화면을 그린다.
Reflow & Repaint
- DOM의 노드 중 하나라도 변경사항이 생기면 전체 노드들이 다시 Reflow과정을 거치기 때문에 퍼포먼스 저하를 불러온다.
1. Reflow(Layout)
- 브라우저 렌더링이 끝난 후 생성된 DOM 노드의 레이아웃 변경 시 영향 받은 모든 노드의 수치를 다시 계산하여(Recalculate) 렌더 트리를 재생성하는 과정
예시
- 윈도우 리사이징
- 노드 추가 혹은 제거
- 내용 변화
- JS를 통한 DOM의 변화
- 요소의 크기, 위치 변경
2. Repaint
- Reflow 과정이 끝난 후 재생성된 렌더 트리를 다시 그리는 과정이 Repaint
Reflow, Repaint 최소화
- 렌더링 최적화를 위해 Reflow, Repaint를 최소화하는 것이 좋음
Reflow가 발생하는 경우
- 윈도우 리사이징 (뷰포트 변화는 Global Layout에 영향)
- 폰트의 변화 (
height
계산에 영향을 주므로 Global Layout에 영향)
- 스타일 추가 또는 제거(레이아웃을 바꾸므로)
- 내용 변화 (인풋박스에 텍스트 입력 등..)
- :hover와 같은 CSS Pseudo Class(CSS: The Definitive Guide: The Definitive Guide 55p에서,
hover
할 시 나타나는 변화로 인한 우려가 생긴다는 의미인 듯 합니다.)
- 클래스 Attribute의 동적 변화
- JS를 통한 DOM 동적 변화
- 엘리먼트에 대한 offsetWidth / offsetHeight (화면에서 보여지는 좌표) 계산시
- 스타일 Attribute 동적변화
최소화하는 방법
- 클래스 변화에 따른 스타일 변화를 원할 경우, 최대한 DOM 구조 상 끝단에 위치한 노드에 적용하기
- 인라인 스타일 피하기
- 애니메이션이 들어간 엘리먼트는 가급적
position:fixed
혹은 position:absolute
로 지정
- 퀄리티와 퍼포먼스 사이에서 타협하라.
- 테이블 레이아웃을 피하기
- IE의 경우, CSS에서 JS표현식 피하기
- JS를 통해 스타일 변화를 주어야 할 경우, 가급적 한번에 처리하기
- CSS Rules는 필요한 만큼 정리하기
position:relative
사용시 주의
1. 클래스 변화에 따른 스타일 변화를 원할 경우, 최대한 DOM 구조 상 끝단에 위치한 노드에 적용하기
- DOM 트리에서 말단에 위치한 노드에 클래스 변화를 주면 Reflow의 행동반경을 전체 페이지가 아닌 일부 노드들로 제한할 수 있음
- 전체 페이지를 감싸는 wrapper에 클래스를 수정하는 행위는 피해야함
2. 인라인 스타일 피하기
- 인라인 스타일이 있는 경우 Reflow는 페이지 전체에 걸쳐 수차례 발생
3. 애니메이션이 들어간 엘리먼트는 가급적 position:fixed
혹은 position:absolute
로 지정
- JS (특히 JQuery)나 CSS3로 width, height 또는 위치이동을 구현한 애니메이션은 초단위로 상당한 Reflow를 발생시킴
- 해당 개체의
position
속성을 fixed
혹은 absolute
로 주게 되면 다른 레이아웃에 영향을 끼치지 않아 해당 요소만의 Repaint를 유발
4. 퀄리티와 퍼포먼스 사이에서 타협하라.
- 애니메이션의 계산과 페이지 Reflow 계산이 동시다발적으로 발생하여 CPU 퍼포먼스 비용이 발생
- 1초에 1px 움직이는 애니메이션 A와 1초에 3px 움직이는 애니메이션 B 중 A가 B에 비해 훨씬 큰 비용 발생
5. 테이블 레이아웃을 피하기
- 테이블로 구성된 페이지의 레이아웃은 점진적으로 페이지 렌더링이 진행되지 않음
- 모두 로드되고 계산된 후에야 화면에 뿌려짐
- 테이블 레이아웃에서는 아주 작은 변화마저도 해당 테이블 전체에 모든 노드에 대한 Reflow를 발생시킴
- 테이블에
table-layout: fixed
속성을 주면 디폴트값인 auto
에 비해 성능면에서 유리
6. IE의 경우, CSS에서 JS표현식 피하기
- Reflow될 때마다 CSS 표현식이 다시 계산되기 때문에 CSS 표현식의 비용이 매우 높음
7. JS를 통해 스타일 변화를 주어야 할 경우, 가급적 한번에 처리하기
var toChange = document.getElementById('elem');
toChange.style.background = '#333';
toChange.style.color = '#fff';
toChange.style.border = '1px solid #ccc';
#elem { border:1px solid #000; color:#000; background:#ddd; }
.highlight { border-color:#00f; color:#fff; background:#333; }
document.getElementById('elem').className = 'highlight';
8. CSS Rules는 필요한 만큼 정리하기
.section_service .list_service li .box_name .btn_more {display:block;width:100px;height:30px;}
.section_service .list_service .btn_more {display:block;width:100px;height:30px;}
두 예시 모두 같은 요소를 선택하는 CSS 선택자
- 같은 요소를 선택하지만 예시1이 더 많은 선택자를 사용하므로써 퍼포먼스가 하락
- 두번째 예처럼 딱 필요한 선에서 핵심만 짚는 CSS Rules 선언 필요
9. position:relative
사용시 주의
position:relative
가 position:absolute
, float
보다 큰 비용 발생
- 이유는 Box model → Normal flow → Positioning의 3단게를 모두 거치기 때문
Virtual DOM
- 이러한 Reflow로 인한 문제를 해결하기 위해 Virtual DOM이 고안됨
- 메모리에 Virtual DOM을 저장하고 DOM의 변화가 생겼을 때 이전에 저장된 가상돔과 새로 그려야하는 가상돔을 비교하여 변경된 부분만을 실제 DOM에 반영