(HTML) Rendering

호두파파·2021년 1월 29일
0

렌더링

렌더링이랑 서버로붜 HTML 파일을 받아 브라우저에 표현해주는 과정이다.

브라우저는 서버로부터 HTML 문서를 다운 받는다.
렌더링 엔진은 HTML 문서를 파싱해서 DOM 트리를 만든다.

그다음, 외부 CSS 파일과 함께 포함된 스타일 요소를 파싱하여 CSSOM 트리를 만든다. 스타일은 브라우저의 자체 스타일, 사용자 정의 스타일, html 태그에 걸려있는 스타일 순서로 처리되며, 나중에 처리되는 스타일을 따르게 된다.

따라서, 인라인으로 들어가있는 스타일 속성이 우선순위를 가지는 것이다.

다음으로, DOM 트리와 CSSOM을 결합하여 렌더링 트리를 형성한다.
이때, 화면에 드러나지 않는 head태그나 display속성에 none 속성에 할당된 요소는 렌더 트리에 포함되지 않는다.

다음은 기기의 뷰포트 내에서 노드들의 정확한 위치와 크기를 계산하는 과정이 진행된다. 이 단계가 레이아웃단계이며, 리플로우라고도 한다.

페이지에서 각 객체의 정확한 크기와 위치를 파악하기 위해, 브라우저는 렌더링 트리의 루트부터 시작해 렌더링 트리를 순회한다.

이 작업에 끝나면, 이제 렌더링 엔진은 각 요소가 어디에 어떤 크기로 표현될지 알게 된다. 렌더링 엔진은 페인트 이벤트를 발생시켜 렌더링 트리를 화면에 그리고, 이 과정을 페인팅또는 래스터화라고 한다.

모든 HTML을 파싱할 때까지 기다리지 않고 배치와 그리기 과정을 진행한다. 웹페이지에 접속했을 때, 페이지가 한꺼번에 뜨지 않고 점점 화면에 그려지는 것이 이 때문이다.


정리

  • HTML을 파싱하여 DOM 트리를 만든다.
  • CSS를 파싱하여 CSSOM 트리를 만든다.
  • DOM과 CSSOM을 결합하여 렌더링 트리를 만든다.
  • 렌더링 트리에서 각 노드의 크기와 위치를 계산한다.
  • 개별 노드를 화면에 그린다.

렌더링이 완료된 상태에서 사용자의 인터랙션에 의해 화면의 일부 영역이 변경된다면, 리플로우 또는 리페인트 가 발생한다.

정확히 말하자면, 레이아웃이 변경될 경우 리플로우 리페인트를 거치고, 레이아웃에는 영향을 주지 않지만 가시성에는 영향을 주는 엘리먼트가 변경될 때는 리페인트만 거치게 된다. (EX: opacity, background-color 등)

여기서 레이아웃의 변경이란 아래와 같은 경우를 말한다.

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

최적화된 렌더링이란

리플로우를 최소화해야한다

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으로 하면, 렌더링 트리에서 노드가 빠지게 된다. 따라서 노드를 노출시킨채 스타일을 변경하는 것보다, 노드를 감추고 스타일을 변경한 후 노드를 노출시키는 것이 리플로우와 리페인트 발생횟수를 줄이는 방법이다.

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. 노드 복제

변경하려는 요소의 노드를 복제한 후 복제된 노드에 작업을 하고, 교체를 해주게 되면 리플로우와 리페인트는 한번만 발생한다.

5. 캐싱

캐싱은 별도의 변수에 자주 사용하는 값을 저장하는 것이다. scrollWidth와 같은 값을 호출할 경우 리플로우를 유발하기 때문에, 반복 구문을 실행하기 전에 이를 변수에 담아 놓으면 리플로우 발생을 최소화 할 수 있다.


출처 : https://velog.io/@ru_bryunak/%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%B4%EB%9E%80

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글