브라우저에 대한 이해

JS (TIL & Remind)·2022년 2월 15일
1

브라우저에 대한 이해

개요

브라우저의 핵심 기능은 사용자가 참조하고자 하는 웹페이지를 서버에 요청(Request)하고 응답(Response)을 받아 브라우저에 표시하는 것이다.

서버의 응답은 주소를 통해 요청하는데, 이 주소를 URI(Uniform Resource Identifier) 라고 한다.

브라우저의 구조

User Interface - 브라우저의 주소창, 뒤로가기, 버튼 등의 UI.

Browser engine - User Interface와 Rendering Engine 사이의 동작을 제어한다.

Rendering engine - 서버로 부터 응답 받은 내용을 화면에 표시해주는 엔진. HTML/CSS를 파싱하여 결과를 보여준다.

Networking - HTTP 통신과 같은 네트워크 통신에 사용된다. 플랫폼과는 독립적인 인터페이스를 가진다.

JavaScript Interpreter - 자바스크립트를 파싱하고 실행하는 해석기.

UI Backend - 콤보박스와 같은 기본적인 위젯을 그린다.

Data Persistence - 쿠키와 같은 데이터를 저장하는 공간. Local Storage, Indexed DB, 쿠키 등 브라우저의 메모리, 로컬 메모리를 활용한다.

렌더링 엔진 작동 과정

렌더링 엔진은 브라우저별로 차이가 있다.
파이어폭스는 Mozilla에서 직접 개발한 게코(Gecko) 엔진을 사용한다.
사파리, 크롬은 Apple이 오픈소스로 개발한 웹킷(Webkit) 엔진을 사용한다.
(크롬은 28 버전부터 Webkit을 기반으로 한 블링크(Blink) 엔진을 사용한다.)
💡 웹킷과 게코의 동작 과정을 보면 약간의 용어 차이가 있으나 기본적인 동작 과정은 동일하다.


1. 서버로부터 받은 HTML, CSS 파일을 각각 HTML Parser와 CSS Parser로 파싱하여 DOM(Document Object Model) 트리, CSSOM(Css Object Model) 트리를 구축한다.
2. DOM 트리와 CSSOM 트리를 결합하여 렌더 트리를 구축한다.
3. 각 노드의 정확한 좌표를 계산하여 화면 상에서 배치할 곳을 결정한다. (Reflow)
4. UI Backend 에서 렌더 트리의 각 노드를 그린다.

💡 더 좋은 UX를 위해, 렌더링 엔진은 모든 HTML, CSS 파일을 한번에 렌더링 하지 않고, 네트워크를 통해 받은 순서대로 위의 과정을 거쳐 렌더링하여 조금 더 빠르게 화면에 표시할 수 있다.

자바스크립트 엔진

자바스크립트는 렌더링 엔진이 아닌 자바스크립트 엔진이 처리한다.

렌더링 엔진이 HTML을 파싱하는 과정에서 script 태그를 만나게 되면, 자바스크립트 코드를 실행하기 위해 렌더링 과정을 잠시 멈추고 자바스크립트 엔진으로 제어 권한을 넘긴다.

제어 권한을 받은 자바스크립트 엔진은 script 태그 내의 자바스크립트 코드 혹은 src 에 정의된 자바스크립트 파일을 로드하고 파싱하여 실행한다.

자바스크립트의 실행이 완료되면 HTML 파서로 다시 제어 권한을 넘긴 뒤 멈췄던 렌더링 과정을 다시 수행한다.

💡 위의 내용에서 볼 수 있듯이, 브라우저는 동기적으로 HTML, CSS, Javascript를 처리한다. 따라서, script 태그의 위치에 따라 DOM 렌더링이 지연될 수도 있다. 그래서 보통 **script 태그는 body 태그의 가장 하단에 위치**시켜 렌더링에 지장이 안가게끔 한다. (또한, DOM이 완성되지 않은 시점에 자바스크립트가 DOM을 조작하는 행위를 방지할 수도 있다.)

Reflow

렌더 트리를 구축하고 각 노드의 정확한 위치와 크기를 계산하는 단계를 Reflow가 발생한다고 말한다. 또한, 어떠한 액션이나 이벤트에 의해 DOM 요소의 크기나 위치 등을 변경하면 해당 노드의 하위 노드와 상위의 노드를 포함해 정확한 위치와 크기를 재계산하는데, 이 또한 Reflow가 발생하는 것이다.

Reflow 발생은 브라우저의 퍼포먼스에 영향을 미친다.

// Reflow가 자주 발생할수록 브라우저의 퍼포먼스에 안좋은 영향을 미친다.
const button = document.getElementById('my-button');
button.style.width = '50px';
button.style.height = '50px';
button.style.top = '10px';
button.style.left = '30px';

// 위 코드는 다음과 같이 수정하여 Reflow를 줄일 수 있다.
const button = document.getElementById('my-button');
button.style.cssText = `
		width: 50px;
		height: 50px;
		top: 10px;
		left: 30px;
`;

Reflow를 최소화 하는 방법

  1. DOM Tree의 Depth를 최소화 한다.
    • DOM Tree의 Depth가 얕을수록, Reflow가 발생했을 때, 연산하는 비용을 절감할 수 있기 때문이다.
  2. Javascript로 스타일 변화를 줄 경우, 가급적 한 번에 처리한다. (위 코드 참고)
  3. CSS 하위 선택자는 필요한 만큼만 작성한다.
    • 선택자를 찾는 과정도 연산 비용에 포함되기 때문이다.
  4. 애니메이션이 있는 요소는 position: absolute 혹은 position: fixed로 지정하는 것이 좋다.
    • 계속 변화하는 애니메이션이 다른 요소에 영향을 주지 않도록 하기 위함이다.
  5. 인라인 스타일 사용을 지양한다.
    • HTML이 파싱될 때 레이아웃에 영향을 미쳐 추가 Reflow를 발생시키기 때문이다.
  6. 태그 사용을 지양한다. - 콘텐츠의 값에 따라 테이블 너비가 계산되기 때문에, 작은 변경만 있어도 테이블의 모든 노드가 Reflow 되기 때문이다.

    Repaint

    렌더 트리를 다시 화면에 그려주는 과정이다. Reflow가 발생하면 변경된 렌더 트리를 화면에 다시 그려주는데 이 때, Repaint가 발생한다.

    또한, 레이아웃의 위치나 크기에 영향을 주지 않는 background-color, color 등과 같은 스타일 속성이 변경될 때 Reflow는 발생하지 않지만, Repaint는 발생한다.

    RePaint 발생은 브라우저의 퍼포먼스에 영향을 미친다.

    Repaint를 최소화 하는 방법

    CSS Property 중에 transform, opacity, cursor 등의 속성들은 Reflow와 Repaint 자체를 발생시키지 않는다. 따라서 Reflow와 Repaint를 가능한 발생시키지 않도록 CSS Style을 구성한다면, 퍼포먼스 향상에 도움이 될 것이다.

profile
노션에 더욱 깔끔하게 정리되어있습니다. (하단 좌측의 홈 모양 아이콘)

0개의 댓글