우리가 웹사이트를 방문할 때 사용하는 브라우저는 어떤 원리로 작동할까?
우선 사용자가 브라우저의 주소창에 URL을 입력하면 DNS를 통해 IP주소로 변환하고 이 IP주소에 해당하는 서버에 요청을 전송한다. 이때 URL이 www.naver.com과 같이 스킴과 호스트로만 이루어진 경우 암묵적으로 index.html파일을 요청하게 된다.
서버는 브라우저가 요청한 html을 바이트 형태로 메모리에 저장하고 메모리에 저장된 바이트를 네트워크 통신을 통해 응답한다.
이때 html파일을 작성할 때 <meta>태그의 charset 어트리뷰트에 지정된 인코딩 방식을 통해 문자열의 형태로 변환된다.
인코딩을 통해 문자열 형태로 변환된 HTML 파일을 문법적 의미를 갖는 최소한의 단위인 토큰으로 분해한다. 이를 토크나이징이라 한다.
앞서 토크나이징을 통해 생성된 토큰을 객체로 변환하여 노드를 생성한다. 노드의 종류로는 문서 노드,요소 노드,어트리뷰트 노드,텍스트 노드가 있다.
이렇게 생성된 노드는 각각 중첩관계를 갖는다.
<div>
<span>안녕하세요.</span>
</div>
이와 같이 div태그 안에 span태그가 있고 그 안에 안녕하세요.라는 텍스트가 있다면, div요소 노드 안에 span 요소 노드가 있고 그 안에는 안녕하세요.라는 텍스트 노드가 중첩되는 것이다.
이렇게 DOM 트리를 구성하는 과정에서 CSS를 연결하는 link 태그나 style태그를 만나면 DOM 생성을 일시 중단한다. 그리고 link 태그의 href 어트리뷰트에 지정된 CSS 파일을 서버로부터 불러오고 HTML을 파싱하는 것과 동일한 과정을 거쳐 스타일이 적용되는 구조를 나타내는CSSOM 트리를 생성한다.
앞서서 구축한 DOM 트리와 CSSOM 트리를 결합해 렌더트리를 생성한다. 렌더 트리는 페이지에 표시될 내용만 포함된다. display:none과 같은 속성이 적용된 태그나 <head>태그와 그 안에 있는 태그들은 렌더트리에 포함되지 않는다.
각 요소의 크기와 위치를 계산하는 레이아웃 작업이 이루어진다. 이때 마진,패딩,보더 등을 통해 박스 모델이 계산된다. 그리고 %나 rem,em과 같은 상대적인 값이 절대적인 픽셀로 변경된다.
그 후 실제 픽셀을 화면에 그리는 페인트 작업이 이루어진다. 레이아웃 단계에서 계산된 위치와 크기를 통해 요소를 렌더링한다.
레이아웃 작업을 다시 하는 것을 리플로우라 한다. 리플로우가 일어나게 만드는 요소는 다음과 같다.
페인트 작업을 다시 하는 것을 리페인트라 한다. 리페인트가 일어나게 만드는 요소는 다음과 같다.
리플로우는 항상 리페인트를 동반한다. 하지만 리페인트는 항상 리플로우를 필요로 하지는 않는다. 또 리플로우는 요소의 위치와 간격을 다시 계산해야 하기 때문에 리페인트보다 더 많은 비용이 든다. 그렇기 때문에 성능 최적화를 위해 리플로우를 최소화하고 필요할 경우 리페인트만 발생하도록 하는 것이 좋다.
CSS를 읽는 과정과 동일하게 렌더링엔진이 HTML파일을 읽어나가다 <script>태그를 만나면 DOM 생성을 일시 중단하고 <script> 태그의 src 어트리뷰트에 지정된 자바스크립트 파일을 서버에 요청하여 로드한다. 그리고 제어권을 자바스크립트 엔진에게 넘긴다. 자바스크립트의 파싱과 실행이 종료되면 다시 렌더링 엔진에게 제어권이 돌아오고 HTML 파싱이 중단된 지점부터 다시 DOM을 생성해 나간다.
이는 <script>태그가 body 태그 내에 들어가는 이유이기도 하다. 만약 자바스크립트 코드에서 아직 로드되지 않은 DOM 요소를 제어할 경우 오류가 발생하는데 이를 막기 위해 <script>태그는 항상 body의 가장 마지막 부분인 </body>태그 직전에 작성해야 한다.
자바스크립트 엔진은 코드를 해석해서 AST(abstract syntax tree)를 생성한다. 그리고 이를 기반으로 인터프리터가 실행할 수 있는 중간 코드인 바이트 코드를 생성 및 실행한다.
1.자바스크립트 코드가 주어진다.
2.단순한 텍스트 상태의 코드를 문법적 의미를 갖는 최소 단위인 토큰으로 나눈다.
3.나누어진 토큰에 문법적 의미와 구조를 반영한 트리인 AST를 생성한다.
4.AST를 바이트코드 생성기를 통해 바이트코드로 변환한다.
5.변환된 바이트코드를 인터프리터가 실행시킨다.
출처 - Modern Javascript Deep Dive