[CS] 브라우저 렌더링 과정
브라우저에서 해당 URL을 입력후 브라우저 렌더링 과정에 대해서 어떻게 일어나는지 정리해보자.
TL;DR
렌더링 과정
1. 브라우저 주소창에 특정 주소를 입력
2. 해당 주소의 서버를 찾고 만약 캐시가 있는지 먼저 체크, 그리고 DNS(Domain Name System)가 실제 서버로 연결하고 http, https에 따라 통신한다.
3. 별도의 path없이 기본 url 요청 '/'로 보낼시 루트 설정은 기본 설정의 index.html을 요청하게되고 서버에서 루트폴더에 있는 index.html을 클라이언트쪽(브라우저)으로 보내준다.
4. 해당 index.html을 받은 브라우저에서 텍스트로 이루어진 파일을 파싱(parsing)하면서 DOM 트리 생성
5. html을 읽다 중간 link tag에서 CSS요청 발생시 html을 잠시 중단하고 CSS관련하여 파싱을 진행
6. CSS 파싱완료시 잠시 중지된 html을 다시 읽고 DOM 트리를 완성
7. 완성된 DOM 트리와 CSSOM 트리를 합쳐 렌더트리를 생성
8. html 파싱을 하다 script tag로 Javascript 코드가 있을시 html 파싱을 잠시 중단
9. 제어 권한을 Javascript engine에게 넘기고 Javascript 코드 또는 파일을 로드하여 파싱을 실행
TL;DR을 통하여 간략하게 정리한다면 저런 과정을 통해 렌더링을 하고 사용자에게 화면을 보여주게된다. 위의 과정을 이미지로 확인한다면 좀더 이해하기 쉽다.
렌더링(Process 단계)보다 더 포괄적인 기본적인 구조
https://www.w3.org/TR/navigation-timing-2/#processing-model
기본 렌더링 과정
WebKit 렌더링 과정
Gecko 렌더링 과정
그렇다면 기본 브라우저의 구성은 어떻게 되는지도 그림을 통해서 이해할 필요가 있다. 그래야 다음 아래 자세한 설명에 대해서 어떤것을 이야기하는지 알 수 있기 때문이다.
브라우저 기본 구조
사용자 인터페이스
사용자 인터페이스는 브라우저창을 띄었을때 요청한 페이지를 보여주는 창을 제외한 주소 표시줄, 이전/다음 버튼, 북마크 메뉴 등 모든 부분이다.
브라우저 엔진
브라우저 엔진은 주로 HTML, CSS 등 웹 페이지 구성을 위한 자료를 해석하여 사용자의 장치에 맞게 시각적인 표현으로 변환하는 역할을 한다. 또한 사용자 인터페이스와 렌더링 엔진 사이의 동작 제어를 한다고 한다.
렌더링 엔진
렌더링 엔진은 브라우저 엔진의 또다른 말이기도 한데 브라우저 엔진은 렌더링 엔진, 레이아웃 엔진 이라고도 불리고 렌더링과 레이아웃은 별도 엔진의 의해 관리 될 수 있지만 서로 밀접하게 연결되어 있고 브라우저 엔진과 같이 묶어서 말한다. 따라서 위의 브라우저 엔진에서 해석하고 시각적인 표현으로 변환한다고 하는게 이부분으로 생각해도된다. 요청한 콘텐츠 HTML과 CSS를 파싱하여 화면에 표시한다.
통신
HTTP 요청과 같은 네트워크 호출에 사용됨. 플랫폼 독립적인 인터페이스이고 각 플랫폼 하부에서 실행됨
UI 백엔드
콤보 박스와 창 같은 기본적인 장치를 그림. OS사용자 인터페이스 체계를 사용
자바스크립트 해석기
자바스크립트 코드를 해석하고 실행
자료 저장소
자료를 저장하는 계층. 쿠키를 저장하는 것과 같이 모든 종류의 자원을 하드 디스크에 저장할 필요가 있다. HTML5 명세에는 브라우저가 지원하는 '웹 데이터 베이스'가 정의되어 있다.
렌더링 엔진이 HTML 문서를 Parsing하고, 내부에서 DOM(Document Object Model) 노드로 변환한다. 그 다음 CSS 파일과 스타일 요소를 함께 Parsing, Render Tree를 생성한다. Render Tree 생성 후에 각 노드가 화면에 표시되도록 배치를 시작하고, UI backend layer를 이용하여 배치된 노드들이 그려지게 된다.
브라우저마다 조금씩 구조차이가 있기 때문에 아래를 통해 이해해보자.
크롬 브라우저
V8은 Javascript 인터프리터이고 DOM에 대해서 전혀모르고 Javascript만을 담당, 실행한다.
Blink는 V8을 포함하는 렌더링 엔진이고 Chromium은 Blink를 포함하는 브라우저 프로젠트 라고한다.
참고링크
파이어폭스 브라우저
이제 디테일하게 하나하나 짚으면서 설명해보자.
웹페이지 로드 과정
위의 그림을 한번더 보고 일련의 과정에 대해서 설명해보고자 한다.
사용자가 주소표시줄(사용자 인터페이스)에 URI를 입력하고 URI 주소값을 브라우저 엔진에게 전달한다. 이때 Redirect는 URL을 서버에서 명시적으로 res.redirect등을 통해 리다이렉트 시켜주는것을 의미한다. 서버의 Redirect 시그널을 받은 브라우저는 해당 URL로 Http 요청을 보낸다.
Http요청을 보낼때 해당 요청에 대한 유효한 응답에 해당하는 데이터를 자료저장소(AppCache, Application Cache)에서 먼저 찾아본다(캐시 체크). 만약 이미 캐싱되어 있는 값이 있다면 네트워크 통신을 하지않고 그에 대한 데이터를 분석하고 바로 사용하여 퍼포먼스를 높여주고 추가 데이터 요청이 필요하다면 URI을 통신레이어에 전달한다. 통신레이어에게 전달하여 서버에 html, css, js 값을 서버에 요청한다.
이때 DNS과정을 거치는데, Http요청을 통해 보낸 도메인(http://www.domain.com)을 실제 HTML파일 등의 리소스들을 가지고 있는 서버의 IP주소(12.3.4.55)로 변환해주는 역할을 한다.이를 통해 사용자 친화적인 도메인에서 컴퓨터 친화적인 서버 IP주소로 매핑이 이루어진다.
그후 TCP, Request, Response 과정을 거치는데 TCP레이어에서 요청/Request가 성공적으로 이루어지면 응답/Response가 오고 이를 다음 단계를 거쳐 가공하게 되는 것이다. 그 다음단계는 Process과정으로 드디어 DOM 렌더링 과정인것이다.
전달받은 html, css을 렌더링 엔진이 파싱(DOM tree, CSSOM tree 구축)한다. 통신레이어에게 전달받은 Javascript 를 통신레이어가 자바스크립트 해석기에 전달해서 해석하고 해석된 결과를 완성된 DOM tree를 조작한다. 조작이 완료된 DOM node(DOM Tree 구성요소)는 render object(render tree 구성요소) 로 변한다. 마지막으로 UI 백엔드에 전달되어 render object가 화면에 그려진다. Load 단계에서
렌더 과정까지 마무리되면 다운로드한 파일을 사용자가 알아볼수 있게 화면에 보여준다.
객체 모델 생성
브라우저가 페이지 렌더링을 하려면 DOM과 CSSOM을 먼저 생성한다. DOM과 CSSOM은 서로 독립적인 데이터 구조이다.
파싱이란?
파싱은 문서의 내용을 토큰(token)으로 분석하고, 문법적 의미와 구조를 반영한 파스 트리(parse tree)를 생성하는 과정이다.
파싱, DOM(Document Object Model) 생성 과정
Bytes -> Characters : 원시 바이트를 디스크나 네트워크에서 읽어온 후 해당 파일에 대해서 지정된 인코딩 방식에 따라 문자로 변환한다.
Characters -> Tokens : 변환된 문자열을 W3C HTML 표준에 지정된 고유 토큰으로 변환한다.
Tokens -> Nodes (Lexing, 낱말 분석) : 변환된 토큰을 해당 속성 및 규칙에 정의하는 객체로 변환한다.
Nodes -> DOM : HTML 마크업이 여러 태그 간의 관계를 정의하기 때문에 생성된 객체는 트리 데이터 구조로 형성됨.
CSSOM 생성과정
CSSOM 생성과정은 DOM과 마찬가지로 CSS 규칙을 브라우저가 이해할 수 있는 방식으로 변환되며 위의 DOM생성 과정과 같이 반복하게 된다. CSSOM이 트리구조를 가지게 되는 이유는 하향식으로 규칙을 적용하며 계산된 스타일을 최종적으로 전달하는 일을 하기 때문이다.
Rendering Tree 생성
DOM 과 CSSOM은 Attachment 과정을 거쳐 렌더링 트리를 생성한다. DOM과 CSSOM만 있다고해서 화면에 보여줄 수 있는게 아니고 이것을 합쳐 rendering tree로 만들어줘야한다. 이때 meta 태그나 display: none 속성을 가진 요소들은 렌더와 관계없기 때문에 렌더 트리에 포함되지 않는다.
이때 Rendering Tree에서 레이아웃을 실행하여 각 노드의 형태와 위치, 크기를 계산하고 완료된 Rendering tree를 이용해 픽셀 값을 채워 넣고 개별 노드를 화면에 paint 해준다.
이러한 일렬의 과정을 거쳐서 화면에 보여주게 되는데 앞서 말한거와 같이 중간에 HTML을 파싱하다가 CSS 파일을 로드하는 link 태그나 style 태그를 만나면 DOM 생성을 잠시 중단하고 CSS 파일을 서버로 요청하고 HTML과 동일한 파싱과정을 거쳐 CSSOM을 생성하고 파싱이 완료한 뒤에 다시 HTML 파싱을 진행하여 DOM 생성을 재개한다.
자바스크립트 코드
화면에 보여주는 랜더링 부분말고도 어떠한 동작을 담당하는 자바스크립트 코드가 있을때도 있는데 만약 해당 html코드에 script가 있다면
마찬가지로 자바스크립트 역시 script 태그를 만나면 HTML파싱을 잠시 중단하고 자바스크립트 파일을 불러와 자바스크립트 코드를 파싱하는데 이때 자바스크립트 엔진에게 제어권한을 넘겨주고 이를 해석하고 AST(Abscract Syntax Tree, 추상적 구문트리)를 생성한다. 이렇게 생성된 AST를 기반으로 인터프리터가 실행할 수 있는 중간 코드인 바이트 코드를 생성해서 실행하게된다.
Reflow/Repaint
이때 자바스크립트 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있다. 변경된 DOM , CSSOM은 다시 렌더 트리로 결합된다. 변경된 렌더트리 기반으로 레이아웃과 페인팅 과정을 거쳐 브라우저의 화면에 다시 렌더링하는데 이를 리플로우, 리페인팅 이라 한다. 리플로우는 레이아웃 계산을 다시 하는 것을 말하며, 노드 추가/삭제, 요소의 크기/위치 변경 등 레이아웃에 영향을 주는 변경이 발생한 경우에 한하여 실행된다.
<script async src="extern.js"/>
<script defer src="extern.js"/>
만약 html파싱을 중단하고 자바스크립트를 불러오는 과정을 원하지 않고 html을 다 파싱한후에 실행하기를 원하다면 defer를 속성에 추가하면된다. 단 외부 js파일을 불러오는 경우에만 가능하다
defer
async
여러개의 script 태그에 async를 지정하면, 태그의 순서와 상관없이 먼저 로드가 완료된 순서부터 실행, 이때 HTML 파싱은 멈춤
-> 순서 보장 ❌
자바스크립트코드 파싱/실행과정
AST에 대해 자세히 알려면 참고링크를 클릭한다.
전체적인 해당 사이트 접속시 브라우저 렌더링 과정