브라우저 동작 과정과 렌더링 속도 개선

오민영·2023년 2월 9일
0

Web

목록 보기
1/9

브라우저 기본 구조

  1. 사용자 인터페이스
  • 주소 표시줄, 이전/다음 버튼, 북마크 메뉴 등.
  • 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분
  1. 브라우저 엔진
  • 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어함
  1. 렌더링 엔진
  • 요청한 콘텐츠를 표시한다.
  • HTML을 요청하면 HTML과 CSS를 파싱하여 화면에 표시한다.
  1. 통신
  • HTTP 요청과 같은 네트워크 호출에 사용된다.
  • 플랫폼마다 독립적인 인터페이스이고 각 플랫폼 하부에서 실행된다.
  1. 자바스크립트 해석기
  • 자바스크립트 코드를 해석하고 실행한다.
  1. 자료 저장소
  • 자료를 저장하는 계층이다.
  • 쿠키를 저장하는 것과 같이 모든 종류의 자원을 하드 디스크에 저장하는 것

브라우저 핵심 기능

사용자가 참조하고자 하는 웹 페이지를 서버에 요청(Request)하고, 서버의 응답(Response)받아, 브라우저에 표시하는 것으로 브라우저는 서버로부터 HTML | CSS | Javascript | 이미지 파일 등을 응답받는다. HTML | CSS는 렌더링 엔진의 HTML파서 | CSS파서에 의해 파싱되어 DOM | CSSOM 트리로 변환되고, 렌더 트리로 병합이 된다. 이렇게 생성된 렌더트리를 기반으로 브라우저는 웹 페이지를 보여준다.

브라우저 동작 과정

  1. Load
  • 브라우저는 HTML, CSS, JS, 이미지, 폰트, 파일 등 렌더링에 필요한 리소스를 서버에게 요청하고 응답을 받는다.
  1. HTML | CSS는 렌더링 엔진에 의해 파싱되고 트리구조가 생성되며, JS는 자바스크립트 엔진에 읜해 파싱되고 AST를 생성한다.
  2. 렌더링 엔진은 HTML | CSS 파일을 다운로드 받은 후 파싱하여 DOM | CSSOM 을 생성하고, 이를 결합하여 렌더트리를 생성한다.
  3. 자바스크립트 엔진은 JS 파일을 다운로드 받은 후 파싱하여 AST(Abstract Syntext Tree)를 생성하고, Byte 코드로 변환하여 실행된다. 자바스크립트는 Interpreter 언어이므로 '문' 단위로 해석되고 실행된다. 이 때 자바스크립트는 DOM API를 통해 DOM 이나 CSSOM을 변경할 수 있다. 변경된 DOM과 CSSOM은 다시 렌더트리로 결합된다.
  4. 렌더트리를 기반으로 HTML 요소의 레이아웃(위치/크기)을 계산하고 브라우저 화면에 페인팅을 한다.

렌더링 엔진

렌더링 엔진은 요청받은 내용을 브라우저 화면에 표시하는 것이다.
렌더링 엔진의 종류

  • Gecko 엔진 - 파이어폭스에서 사용
  • Webkit 엔진 - 크롬, 사파이레서 사용

렌더링 엔진 동작 과정

  1. DOM 트리 & CSSOM 트리 구축
    1-1. HTML을 파싱하여 DOM(Document Object Model)노드를 만들고, 이 DOM 노드를 병합하여 DOM Tree를 생성한다.
    1-2. CSS을 파싱하여 CSSOM(Css Object Model)노드를 만들고, 이 CSSOM 노드를 병합하여 CSSOM Tree를 생성한다.
    1-3. HTML | CSS 는 단순 텍스트이므로 각각 연산과 관리가 가능하도록 각각의 파서(Parser)를 사용해 관리가 가능한 Object Model로 만든다.
  2. 렌더트리 생성(구축): DOM트리와 CSSOM트리를 결합해서 렌더트리를 생성한다.
  3. 렌더트리 배치(Reflow, Layout): 각 노드에 대해 화면에서의 정확한 위치와 크기를 계산한다.
  4. 렌더트리 그리기(Repaint): UI 백엔드에서 렌더링 트리의 각 노드를 가로지르며 렌더링을 한다.

    렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시하기 때문에, 모든 HTML을 파싱할때까지 기다리지 않고 배치와 그리기 과정이 완료되면 이부를 화면에 먼저 표시한다.

Pharsing(파싱?) - 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미한다. 파싱 결과는 보통 문서구조를 나타는 노드트리인데, 파싱 또는 문법트리라고 부른다.

1. 파싱

1-1. HTML - 파싱 & DOM 생성과정

  1. Bytes
    서버는 브라우저로부터 요청받은 HTML 파일을 읽고, 메모리에 저장하고, 메모리에 저장된 바이트로 응답을 한다.
  2. Characters
    브라우저는 응답을 받은 바이트 형태의 문서를 meta 태그의 charset 속성에 지정된 문자 인코딩 방식(UTF-8)에 따라 문자열로 변환한다.
  3. Tokens
    문자열로 변환된 HTML 문서를 문법적 의미를 갖는 코드의 최소단위인 토큰으로 분해한다.
  4. Nodes
    토큰들의 내용에 따라 객체(Object)로 변환하여 각 노드들을 생성한다.
  5. Object Model
    HTML은 요소 간에 부모-자식 관계인 중첩 관계를 갖는데, 이를 반영하여 노드들은 트리구조로 구성되어 DOM 을 만든다.

1-2. CSSOM - 파싱 & DOM 생성과정

렌더링 엔진은 HTML 문서를 한 줄 한 줄 순차적으로 파싱하여 DOM을 생성한다. 그러다 CSS를 로드하는 Link 요소, 혹은 style 요소를 만나면 DOM을 중지하고, CSS 파싱 결과물인 CSSOM을 생성하는 과정을 진행한다.

<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8"> // 여기까지 해석 후, 
	//link를 만나면 DOM생성을 중지하고 CSS파일을 서버에 요청한 후 응답받아 CSS파싱을 시작한다. 
  <link rel="stylesheet" href="style.css"> 
  ...

(html 파싱 과정과 동일함)
1. Bytes
서버는 브라우저로부터 요청받은 HTML 파일을 읽고, 메모리에 저장하고, 메모리에 저장된 바이트로 응답을 한다.
2. Characters
브라우저는 응답을 받은 바이트 형태의 문서를 meta 태그의 charset 속성에 지정된 문자 인코딩 방식(UTF-8)에 따라 문자열로 변환한다.
3. Tokens
문자열로 반환된 CSS 문서를 문법적 의미를 갖는 코드의 최소단위인 토큰으로 분해한다.
4. Nodes
토큰들의 내용에 따라 객체(Object)로 변환하여 각 노드들을 생성한다.
5. Object Model
CSS은 요소 간의 부모-자식 관계인 중첩 관계를 갖는데, 이를 반영하여 모든 노드들을 트리 구조로 구성하여 CSSOM 을 만든다.

1-3. Javascript - 파싱

자바스크립트는 렌더링 엔진이 아닌, 자바스크립트 엔진이 처리를 한다.
렌더링 엔진은 HTML 문서를 한 줄 한 줄 순차적으로 파싱을 하다가, JS 파일을 로드하는 <script> 태그를 만나면 DOM 생성을 일시 중지한다. <script> 요소의 src에 정의된 JS 파일을 서버에 요청하고 응답을 받으면 JS 코드를 파싱하기 위해, JS 엔진에게 제어권을 넘긴다. 파싱이 끝나면 렌더링 엔진으로 다시 제어권을 넘기고 DOM 생성을 이어나간다. 이 과정에서 생성되지 않은 DOM을 조작한다면 에러가 발생할 수 있다. 따라서 body의 가장 아래에 JS를 위치시키거나 DOM 생성이 전부 완료된 시점에 JS가 실행되도록 하는게 좋다.

  1. Token
    JS 코드를 토크나이저가 어휘를 분석하여 문법적 의미를 갖는 코드의 최소 단위인 토큰으로 분해한다. 이것을 토큰나이징이라고 한다.
  2. AST
    파서가 토큰들을 구문분석하여 AST(추상 구문 트리)로 파싱한다.
  3. 변환
    바이트 코드 생성기가 AST를 바이트코드로 변환한다.
  4. 실행
    인터프리터에 의해 바이트코드를 실행한다.

블록 리소스?(Block Resourse)
HTML 파싱이 일어날 때, CSS | JS로 인해 파싱이 중단될 수 있다. 이렇게 파싱이 중단되는 상황을 'HTML 파싱이 블록되었다' 라고 표현하며, 블록 상태의 원인이 되는 리소스를 '블록 리소스(Block Resource)'라고 부른다. 블록 리소스는 브라우저 로딩 단계 중 페인트 과정을 지연시키므로, 블록 리소스가 HTML 파싱을 막는 상황이 발생하지 않도록 해야한다. 이는 렌더링 경로를 최적화 하면 페인팅을 빠르게 하고 로딩 속도를 개선할 수 있다.

2. 렌더트리 생성(구축) - Attachment

  • CSSOM 트리와 DOM 트리를 결합하여, 표시해야 할 순서로 내용을 그려낼 수 있도록 하기 위해 렌더 트리를 형성하는데, 이 과정을 Attachment라고 한다.
  • 렌더트리에는 페이지를 렌더링하는데 필요한 노드만 렌더링한다.
  • 렌더트리는 페이지에 표시되는 모든 DOM 컨텐츠와 각 노드에 대한 모든 스타일 정보를 갖고 있다.

렌더트리 생성 과정

  1. DOM 트리의 루트부터 노드 각각 모두 탐색한다.
    • 렌더링이 되지 않는 script / meta 요소와 같은 노드들은 렌더링 트리에서 생략된다.
    • CSS 속성 중 display: none과 같이 화면에서 숨겨지는 속성도 렌더 트리에 반영되지 않는다.
  2. 화면에 표시되는 각 노드에 대해 적절하게 일치하는 CSSOM 규칙을 찾아 적용한다.
  3. 화면에 표시되는 모든 노드의 콘텐츠 및 계산된 스타일 정보를 포함하는 렌더 트리가 구현된다.

위 DOM | CSSOM이 아래와 같이 렌더 트리가 생성된다.

3. 렌더트리 배치 - Layout | Reflow

렌더 트리가 생성되고, 기기의 뷰포트 내에서 이 노드들이 정확히 어디에 얼마만큼 그려야 하는지는 각을 재는 작업이 필요하다. 예를 들어, width: 50%는 무엇에 대한 50퍼센트인지 알아야 한다. font-size: 4rem은 어디서부터 계산해야하는지도 알아야 한다. 이 과정을 배치(Layout) 또는 Reflow 라고 한다.

브라우저는 렌더 트리의 루트부터 순회하면서 페이지 내 각 개체의 정확한 위치와 크기를 파악해낸다. 일반적인 흐름인 상 → 하 , 왼 → 우 순으로 진행이 된다. 이렇게 측정된 값은 화면에서 절대적인 픽셀 값으로 변환된다. 예를 들어, ‘width: 50%는 250px이고, font-size: 4em은 22px이야’ 와 같이 명시적인 요구 명세서를 받는 것이다.

만약 width: 50vw 상태에서 브라우저 너비가 달라지면 이 값은 다시 계산해야 한다. 하지만 렌더 트리에 변화가 있는 것이 아니므로. Layout(Reflow) 단계부터 다시 계산한다.

부모 요소에게 Reflow가 일어나면 자식 요소에도 영향을 받기 때문에, 최대한 DOM 구조 하위에 있는 노드에게 변화를 주는 것이 좋다.

4. 렌더트리 그리기 - Painting

Layout(Reflow) 프로세스가 완료되면, 브라우저는 paint 이벤트를 발생시킨다. 렌더링 트리를 화면의 픽셀로 변환하게 되는 것이다.
렌더트리의 각 노드를 화면의 실제 픽셀로 나타낼 때 painting 메서드가 호출된다. 페인팅 과정 후 브라우저 화면에 UI가 나타나게 된다.

그리기 순서

  1. 배경 색
  2. 배경 이미지
  3. 테두리
  4. 자식
  5. 아웃라인
  6. ...

Reflow & Repaint

렌더링 과정을 모두 마친 후 최종적으로 브라우저에 페이지가 그려진다. 하지만 특정 액션이나 이벤트에 따라 HTML 요소의 크기나 위치 등의 레이아웃 수치가 변하면 해당 요소의 영향을 받는 자식 노드나 부모 노드들을 포함하여 Layout Reflow 과정을 다시 수행하게 된다.

이럴경우 각 요소들의 위치와 크기를 다시 계산하게 되는데, 이 과정을 Reflow. 그리고 Reflow 된 렌더 트리를 다시 화면에 그려주는 과정을 Repaint라고 한다.

Reflow(Layout)

뷰포트 내에서 렌더 트리와 각 요소 노드의 정확한 위치와 크기를 계산하는 과정, Reflow → Repaint 순서로 렌더링

  • DOM 요소의 추가 / 제거될 때
  • DOM 요소의 크기와 위치가 변경될 때 (margin, padding, border, width, height 등)
  • 브라우저 창의 크기가 변경될 때 (윈도우 리사이징)
  • CSS Animation, Transition..
  • Font-size, Font-family 이 변경될 때
  • 텍스트 내용이 변경될 때
  • 스크롤 될 때 (offset, scrollTop, scrolllLeft)
  • DOM API를 통한 노드 요소의 추가 및 삭제가 이루어질 때

Repaint(Redrow)

화면의 구조가 변경되었을 때는 Reflow 과정을 거쳐 화면 구조를 다시 계산한 후, Repaint 과정을 통해 화면을 다시 그린다. 즉, 화면의 구조가 변경되었을 때는 Reflow와 Repaint 모두 발생하다.

화면의 구조가 변경되지 않는 화면 변화의 경우, Repaint만 발생한다.

예 → opacity, background-color, visibility, outline 등의 스타일 변경 시에는 Repaint만 발생한다.

  • 화면에 변화가 있을 때 화면을 그리는 과정
  • 화면이 변경되는 모든 경우 발생

리플로우 최소화 하는 방법

브라우저가 렌더링을 더 빠르고 효율적으로 진행되기 위해서는 Reflow 과정을 최소화 하는 것이 좋다.

  1. 가급적 레이아웃을 피한다.
    • 꼭 필요한 경우가 아니라면 width | height 속성은 변경하지 않는다.
    • transform을 사용하거나, visibility | disaplay 보다는 opacity를 사용하는 것이 성능 개선에 좋음
  2. 이전 레이아웃 모델 대신 Flexbox를 사용
    • 동일한 시각적 배치를 표현할 때는 float 보다는 flexbox를 사용하는 것이 좋다.
  3. 애니메이션 - 흐름 밖에서 변경
    • 애니메이션과 같이 복잡한 렌더링 변경이 필요한 경우, absolute | fixed와 같이 주변 요소에 영향을 주지 않는 선에서 설정하는게 좋다!

Reference

JavaScript | Reflow 란 (feat. 브라우저 렌더링)

NAVER D2

브라우저 동작 과정

웹 브라우저의 동작원리를 알아보자

profile
이것저것 정리하는 공간

0개의 댓글