공부 및 일상기록

브라우저 렌더링 과정 본문

개발

브라우저 렌더링 과정

낚시하고싶어요 2023. 1. 13. 01:22

사용자가 주소창에 올바른 URL을 입력하면 해당 사이트의 화면이 노출된다. 이 짧은 순간에 브라우저는 수많은 일을 처리한다.

 

브라우저는 다음과 같은 구조가 있다.

모든 브라우저가 이렇다는 것은 아니다.

브라우저 아키텍처

  • User Interface : 주소표시줄, 이전, 이후버튼, 홈버튼 등 페이지 외의 모든 영역을 의미한다.
  • 브라우저 프로세스 : 사용자 UI와 렌더링 프로세스 사이의 동작을 제어한다. 주소표시줄, 이전,이후 버튼 등 브라우저의 동작부분을 제어한다. 파일 접근에 필요한 권한처리등도 이뤄진다.
  • 렌더링 프로세스 : 탭 안에서 웹사이트가 표시되는 영역을 모두 제어한다. 요청한 콘텐츠를 파싱하고 화면에 나타내주는 일을 수행한다.
  • Networking : HTTP/HTTPS 네트워크 요청이 처리된다.
  • JavaScript Interpreter : 자바스크립트 코드를 파싱할 때 사용된다.
  • UI Backend : 기본 위젯을 그릴 때 사용된다. UI Backend는 특정 플랫폼이 아닌 OS의 방법을 사용한다.
  • 데이터 저장소 : 쿠키나 localstorage, indexedDB  같이 로컬에 저장되어 좀 더 오래 유지외어야만 하는 스토리지 메커니즘을 지원하는 영역이다.
  1. 사용자가 URL을 입력하면 브라우저프로세스에서 제일 먼저 작업이 시작된다.
  2. 최근 등장한 브라우저는 대부분 주소창을 자사의 검색창과 동일하게 사용하고 있다. 검색창에 입력이 이뤄질 경우 UI스레드는 해당 텍스트가 검색어인지 URL인지 확인한다. 검색어일 경우 검색 엔진 URL에 검색어를 결합해 페이지를 이동시키며 URL일 경우 네트워크 호출을 수행한다. 이때부터 브라우저에는 로딩이 되고 있다는 표시가 나타나게 된다. 동시에 네트워크 스레드는 적절한 프로토콜로 요청을 처리하기 시작한다.
  3. 응답이 도착하기 시작하면 응답이 어떤 타입인지 판단한다. 렌더러 프로세스가 다룰수 있는 html이나 pdf같은 경우 렌더러 프로세스로 전달하며, 다운로드 파일 형식일 경우 다운로드 매니저에게 데이터를 전달해 준다. 이 단계에서 해당 사이트가 악성 페이지인지 검사또한 이뤄지며 악성페이지일 경우 중단한다.
  4. 모든 검사가 끝나면 네트워크 스레드는 UI스레드에게 작업이 준비되었다고 알려준다. UI 스레드는 웹페이지를 렌더링할 렌더러 프로세스를 찾는다. 네트워크 요청이 오래 걸릴 수 있으므로 URL 요청을 보낼 때 UI스레드는 렌더링을 수행할 프로세스를 미리 찾아 바로 사용하는 식으로 최적화가 되어있기도 한다.
  5. 데이터와 렌더러 프로세스가 준비되면 본격적으로 페이지의 이동이 시작된다. 이 시점에 렌더러 프로세스는 html데이터를 수신하여 문서를 로딩하며, 브라우저의 주소 표시줄, 사이트 제목 같은 관련 UI들도 갱싱된다. 이후에 탭을 이동할 수 있도록 세션 기록이 저장된다.
  6. 렌더링이 완료되면 렌더러 프로세스는 브라우저 프로세스로 로딩이 완료되었음을 알리고 UI스레드의 로딩 표시를 중지한다.

 

 

여기서 우리는 렌더러 프로세스에서 일어나는 일을 더 구체적으로 살펴보자.

 

렌더러의 프로세스 작업

렌더러 프로세스는 탭 내부에서 발생하는 모든 작업을 담당하며, html, css, 자바스크립트를 사용자와 상호작용할 수 있는 웹페이지로 변환한다.

렌더러 프로세스작업은 위와같이 실행되며 모든 html을 파싱할때까지 기다리지 않고 레이아웃과 페인트를 우선적으로 처리해 내용의 일부를 나타내는 방식으로 수행된다.

 

1. 파싱

파싱(Parsing)은 구문 분석이라고도 불린다. 일련의 문자열을 의미있는 토큰으로 분해하여 토큰간의 위계관계를 분석해 구조를 결정짓는 것이다.

 

html 파싱

  • 데이터를 수신하기 시작하면 렌더러 프로세스의 메인 스레드는 html을 파싱해 DOM으로 변환하기 시작한다. 파싱의 최종 결과가 DOM트리이다.
  • DOM트리를 파싱하는 과정에서 <img>, <link>같은 태그가 있으면 미리 브라우저 프로세스의 네트워크 스레드로 요청을 보낸다. 
  • <script>태그의 경우는 좀 다르다. 자바스크립트에는 문서 전체의 구조를 바꿀수있는 메서드나 프로퍼티가 존재하므로 문서 중간에 자바스크립트 코드가 있는 경우 이를 로딩하고 html파싱을 잠시 중단한다. 그리고 자바스크립트 파싱이 완료되면 html 파싱이 재개된다.
  • 만약 스크립트 파일에서 문서 조작이 이뤄지지 않는다면 <script>태그에 async나 defer속성을 지정하여 html파싱을 중지하지 않고 자바스크립트 코드를 비동기적으로 로딩하고 실행할 수 있다.

 

CSS 파싱

  • html파싱 과정에서 <link>태그를 만나면 해당 css 리소스를 가져온다. html과 비슷하게 css역시 토큰화 노드생성을 거쳐 CSSOM이라는 트리 구조를 가진다.
  • CSSOM은 자바스크립트에서 CSS를 조작할 수 있게 도와주는 API집합이다. DOM이 html을 조작할 수 있게 도와준다면 CSSOM은 css를 조작할수 있게 도와주는 것이다.
  • CSSOM트리의 노드는 DOM요소의 선택자에 맞춰 적용될 CSS 스타일의 정보가 포함되어있다.
  • 화면에 나타나지 않는 요소들에 대한 DOM요소는 포함되어 있지 않다.
  • CSS는 부모의 스타일을 상속받는 경우가 존재하므로 CSSOM은 하향식으로 생성된다.

 

렌더트리 만들기

렌더트리는 CSSOM트리와 DOM트리가 결합되어 생성된다. 각 노드는 렌더객체로 이뤄져 있으며 렌더 객체는 눈에 보이는 노드만을 포함한다. 렌더 트리의 생성 과정을 아래처럼 요약할 수 있다.

  1. <html>태그와 <body>를 처리하며 렌더 트리 루트를 구성한다.
  2. DOM트리를 순회하며 최상위 노드(<html>)로부터 보여지지 않는 노드(<link>, <script>, <meta>등)를 생략한다.
  3. display:none처럼 CSS로 숨겨지는 노드 또한 트리에서 생략한다.
  4. float이나 positon같은 속성을 사용했을 경우 흐름에서 벗어나 실제 그려지는 위치로 렌덕객체가 이동한다.
  5. 화면에 나타나는 노드에 CSSOM규칙을 찾아 일치하는 스타일링을 적용한다.

레이아웃

레이아웃은 각 요소의 상대적인 위치, 크기를 찾는 과정이다. 렌더트리의 모든 노드들은 자식 노드들의 레이아웃을 호출하는 layout을 갖는다. 레이아웃은 최상위 노드인 <html>부터 시작하며 이후 자식 렌더 객체들의 레이아웃을 배치하며 반복적으로 발생한다. 노드 위치는 (x,y)좌표계를 사용하며 최상위 노드의 위치는 (0,0)이다.

레이아웃은 흐름 기반의 배치모델을 사용한다. 왼쪽에서 오른쪽, 위에서 아래 방향으로 진행된다.

일반적으로 이후의 요소는 이전의 요소에 영향을 주지 않지만 display의 table이나 flex, grid를 사용할 경우 여러 흐름이 생성되어 영향을 줄 수 있다.

 

초기 레이아웃 이후 DOM 노드가 추가되거나 변경되었을 경우 이 변경된 부분을 계산하기 위해 전체 노드를 대상으로 레이아웃이 발생하면 많은 낭비가 이뤄진다.

따라서 더티비트(dirty bit)라는 방식을 사용한다. 랜더객체는 다시 배치할 필요가 있는 변경요소, 추가된 노드, 그리고 자식 노드를 더티 라고 표시한다.

 

레이아웃이 이뤄지는 과정은 다음과 같다.

  1. 부모 노드가 자식 노드의 너비를 결정한다. 너비는 블록의 너비나 렌더 객체의 스타일 속성, 박스 모델을 고려해 계산된다.
  2. 자식 렌더링 객체를 배치한다
    • 자식 렌더링 객체들의 x,y값을 지정한다.
    • 부모 혹은 자식 객체가 더티한 경우 재계산이 필요하다. 자식 렌더링 객체의 layout()메서드를 호출해 높이를 계산한다.
  3. 자식렌더링 객체의 높이를 더해 부모 렌더 객체의 높이를 계산한다.
  4. 레이아웃 중인 렌더 객체의 더티 플래그를 제거한다.

 

페인트

페인트 단계에선 렌더트리를 순회하며 레이어를 만들고 레이어의 배경, 테두리, 텍스트, 그려지는 순서, 레이어간의 순서 등 그려지는 과정을 기록한다.

 

합성

이 단계에서 그려질 요소들의 순서, 스타일, 위치 등을 모두 알고 있다.

이 정보를 화면의 픽셀로 변환하는 것을 래스터화(rasterizing)이라고 한다.

합성은 각 레이어를 분리해서 래스터화한 뒤 브라우저에서 페이지의 크기, 뷰포트에 맞게 합성해 화면으로 나타낸다.

지금까지 html, css, 자바스크립트를 화면에 픽셀로 나타내기 위한 과정을 주요 렌더링 경로(Critical Rendering Path, CRP)라고 부른다.

 

CRP과정에서 렌더트리를 만들고 이 결과를 이용해 레이아웃을 수행하며, 페인트를 진행한다. 이런 연쇄적인 구조 때문에 초기 계산이 필요한 변화를 발생시킨다면 화면을 업데이트 할 때 더 많은 시간을 사용하게 된다.

 

CRP가 중요한 이유는 브라우저가 어떻게 화면을 렌더링하는지 알 수 있을 뿐만 아니라 성능에도 큰 영향을 미치는 과정이기 때문이다. 

'개발' 카테고리의 다른 글

BOM과 DOM에 대하여  (0) 2023.01.13
리플로우와 리페인트에 대하여  (0) 2023.01.13
<jQuery> 임포트 주소  (0) 2022.09.13
<Flask> 시작하기  (1) 2022.09.13
[jQuery+Ajax] 랜덤 이미지 바꾸기  (0) 2022.09.08