webDeveloper/Browser

렌더링이란? 그리고 렌더링을 최적화 할 방법들.

findTheValue 2021. 8. 2. 01:42

렌더링이란?

개발자가 작성한 문서를 브라우저에서 그래픽 형태로 출력하는 것.

imgpost-thumbnail

  • 주소창에 구글 검색
  • 구글 서버로 찾아감
  • DNS(Domain name server)가 연결해줄 곳을 찾음(실제 서버가 어디있는지 알고있는 서버.)
  • 서버로부터 HTML 파일을 받아 브라우저에 뿌려주는 과정.
  • 로더가 서버로부터 전달 받는 리소스 스트림을 읽는다.(data인지 file인지 다운로드 할껀지..)
  • 브라우저는 서버로부터 HTML 문서를 다운받고(W3C(world wide web consortium)의 명세에 따라 HTML해석)
  • Rendering 엔진은 HTML 문서를 파싱해서 DOM tree를 만든다
  • 그 다음 CSS를 파싱해 CSSOM tree를 만든다.
  • DOM tree와 CSSOM tree를 결합해 렌더링 트리를 형성한다.(head태그나 display:none은 보이지않음)
  • (visibility : invisible 은 공간은 차지하고 요소가 보이지 않으므로 렌더링 요소에 포함된다.)
  • 다음은 기기의 viewport에서 노드들의 정확한 위치와 크기를 계산한다.(layout단계= reflow)(px단위로)
  • 페이지에서 각 객체의 크기와 위치를 파악하기 위해 브라우저는 렌더링 트리의 루트부터 시작해 렌더링 트리를 순회한다.
  • 이 작업들이 끝나면 렌더링엔진은 각 요소가 어디에 어덯게 표현될지 알게되고 페인트 이벤트를 발생시켜 렌더링트리를 화면에 그린다(페인팅 = 래스터화)
    • 모든 HTML을 파싱할때까지 기다리지는 않고 배치와 페인팅을 진행하면서 한다,(한번에 화면이 뜨지않고 점점 그려지는 현상)

CSS는 위에 Script는 아래에.

문서를 파싱해서 DOM tree를 만들어도 스타일 규칙이 없으면 렌더링 할 수 없다.

즉 빨리 스타일 규칙을 찾아야 렌더링 트리가 만들어지므로 스타일 시트 파일을 모두 다운로드 시키기 위해태그 사이에 놓는다.

자바스크림트는 DOM객체를 이용해서 컴포넌트들을 조작하는데 head태그에 놓으면 HTML파서가 파싱을 멈추고 스크립트 파일을 읽게 된다. 때문에 읽는 시간에 소요가 걸려 웹페이지가 느리게 보이게 된다. 또는 순서가 안맞아 적용이 안될 수도 있다.

때문에 일반적으로 바디를 끝내기 전에 스크립트 파일을 모아둔다.

(JS애니메이션을 일찍 보이고싶다면 따로떼서 위로 올린다.)

별첨 : 렌더링엔진
  • chrome : webkit -> Blink
  • safari : webkit
  • firefox : gecko

렌더링엔진들은 좀 더 나은 사용자 경험을 위해 모든 HTML파싱이 끝나기도 전에 이후의 과정을 미리 사용자에게 보여 줄 수 있는 일부 내용들을 출력하게 된다.

레이아웃의 변경

  • 사용자 인터렉션에 의해 수정이 이루어진다면 reflow 혹은 repaint가 발생한다.(레이아웃의 변경)
  • 레이아웃 변경 없이 가시성 element만 변경될때는 repaint만 발생(opacity,background-color)

imgimg

  • DOM 엘리먼트 추가, 제거 또는 변경
  • CSS 스타일 추가, 제거 또는 변경
  • CSS 스타일을 직접 변경하거나, 클래스를 추가함으로써 레이아웃이 변경될 수 있다.
  • 엘리먼트의 길이를 변경하면, DOM 트리에 있는 다른 노드에 영향을 줄 수 있다.
  • CSS3 애니메이션과 트랜지션. 애니메이션의 모든 프레임에서 리플로우가 발생한다.
  • offsetWidth 와 offsetHeight 의 사용. offsetWidth 와 offsetHeight 속성을 읽으면, 초기 리플로우가 트리거되어 수치가 계산된다.
  • 유저 행동. 유저 인터랙션으로 발생하는 hover 효과, 필트에 텍스트 입력, 창 크기 조정, 글꼴 크기 변경, 스타일시트 또는 글꼴 전환등을 활성화하여 리플로우를 트리거할 수 있다.

따라서 최적의 렌더링 성능을 위해서는 위 과정을 최적화 해야함.

추가적으로 React가 빠른 이유는 reflow repaint를 한번에 묶어서 수정해주는 Virtual DOM을 사용하기 때문. 연산을 한번에 묶어서 dom에 전달, 수정하게 된다.(성능개선)

그러나 react자체가 빠른건 아니기때문에 최적화가 되지 않은 react는 오히려 DOM보다 느릴 수 있음을 알아야한다.


최적화 방법

리플로우를 최소화 하는 방법.

1. 인라인 스타일 사용 X

가독성을 해치고 수차례 리플로우를 발생시킨다. 그에 반해 외부 스타일을 사용할 경우 한번만 리플로우를 발생시킨다.

2. 작업 그루핑

DOM요소의 정보를 요청하고 변경하는 코드는 같은 형태의 작업끼리 묶어 실행시키는 것이 좋다.

function change() {     
  let width = document.getElementById("layer1").style.width;
  document.getElementById("layer2").style.width = width; 
  let height = document.getElementById("layer3").style.height; 
  document.getElementById("layer4").style.height = height; 
}
--->아래로 수정.
function change() {     
  let width = document.getElementById("layer1").style.width;
  let height = document.getElementById("layer3").style.height; 
  document.getElementById("layer2").style.width = width; 
  document.getElementById("layer4").style.height = height; 
}

3. 노출 제어

display를 none으로 하면 렌더링 트리에서 노드가 빠지게 된다. 따라서 노출시킨채 스타일의 수정보다, 렌더링 전에 스타일을 변경한 후 노출시키는게 리플로우와 리페인트를 줄일 수 있다.(visible invisible을 display none으로 바꾸는 것도 좋음.)

let element = document.getElementById("box1"); 

for(let i=50; i<100; i++) {     
  element.style.width = i + "px"; 
} 
for(let i=1; i<=50; i++) {  
  element.style.borderWidth = i + "px"; 
}

// 아래와 같이 렌더링 트리에서 빼준 다음, 스타일을 적용하고 다시 노출.
// 이 경우 두번의 리플로우 리페인트만 발생

let element = document.getElementById("box1");
element.style.display = "none"; 

for(let i=50; i<100; i++) {    
  element.style.width = i + "px"; 
} 
for(let i=1; i<=50; i++) {  
  element.style.borderWidth = i + "px"; 
} 

element.style.display = "block";

4. 노드 복제

변경하려는 노드를 복제한 후 복제된 노드에 작업하고 교체해준다.(reflow, repaint한번씩만 발생한다.)

5. 캐싱

별도의 변수에 자주 사용하는 값을 저장한다.

6. absolute, fixed 사용하기.

영향을 받는 노드가 없기 땜누에 reflow가 필요없이 repaint연산비용만 들게 된다.


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

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 Repaint가 일어나지 않는 transform, opacitiy와 같은 속성도 있습니다. 따라서 left, right, width, height 보다 transform을, visibility/display 보다 opacitiy를 사용하는 것이 성능 개선에 도움이 됩니다.

(Repaint가 일어나지 않는 원리에 대해서는 차후 포스팅에서 다루도록 하겠습니다)

출처: https://boxfoxs.tistory.com/408 [박스여우 - BoxFox]

브라우저 기본 구조

브라우저는 크게 화면을 조정하는 영역과 데이터를 조정하는 영역으로 구분 할 수 있다.

img

  • 사용자 인터페이스 : 사용자가 브라우저에서 직접 조작 할 수 있는 영역
  • 브라우저 엔진 : 사용자 인터페이스가 렌더링 엔진에 쿼리를 전달 할 수 있게 조작을 담당
  • 렌더링 엔진 : html과 css문서를 파싱 / 해석 하여 화면에 표현
  • 네트워킹 : http 요청을 할 수 있고 네트워크 호출할 수 있는 영역
  • 자바스크립트 해석기 : JavaScript 코드를 해석하고 실행
  • UI 백엔드 : select / input 등 기본적인 위젯을 기리는 인터페이스
  • 자료저장소 : Cookie, Local storage등 모든 자료를 저장하는 영역

DOM ( Document Object Model )

DOM은 HTML 마크업을 DOM모델로 변환하는 기술이다, HTML 요소들이 객체기반으로 구조화 될 수 있도록 가공한다

즉 웹 페이지에 대한 프로그래밍 인터페이스라고 볼 수 있다.

DOM 변환 가정은 아래와 같다

img

\1. 변환 : 브라우저가 HTML의 원시 바이트를 디스크나 네트워크에서 읽어온뒤 해당 파일에 대해 지정된

인코딩(Ex : UTF-8)에 따라 개별 문자로 변환한다

\2. 토근화 ; 브라우저가 문자열을 W3C(World wide Web) HTML5 표준에 지정된 고유 토큰 으로 변환한다, 각 토큰은 특별한 의믜와 고유한 규칙을 가지게된다

\3. 렉싱 : 방출된 토큰은 해당 속성 및 규칙을 정의하는 '객체'로 변환된다.

\4. DOM 생성 : 마지막으로, HTML 마크업이 여러 태그 간의 관계를 정의하기 때문에 생성된 객체는 트리 데이터 구조 내에 연결된다, 이 트리 데이터 구조에는 원래 마크업에 정의된 상위-하위 관게도 포함된다,

즉 HTML 객체는 Body 객체의 상위이고, Body는 단락(paragraph) 객체의 상위인 식이된다.


브라우저 호환성 테스트.

img

​ DevOps를 위한 웹 UI 테스트 환경 구성 예 - 출처 : CURVE

프론트엔드 테스트 환경에서는 크롬, 파이어폭스 등 브라우저별로 자동 테스트를 수행하여 브라우저의 호환성에 문제가 없는지 검사함.