Post

웹 브라우저 동작 원리

웹 브라우저에 네이버 를 검색하고 화면에 네이버 화면이 출력이 될 때 까지 내부적으로 어떤 동작들이 수행이 되는지


📌 요약

  1. 사용자가 네이버를 검색합니다.
  2. 웹 브라우저는 네이버라는 도메인 이름에 해당하는 IP 주소를 DNS에 요청하고 DNS는 IP 주소를 응답합니다.
  3. DNS로부터 IP 주소를 받으면 웹 브라우저는 IP주소를 이용해 TCP 소켓을 연결합니다.
  4. HTTP 또는 HTTPS 프로토콜로 네이버 서버에 네이버 홈페이지를 띄울 파일들을 요청합니다.
  5. 네이버 서버는 요청받은 파일을 웹 브라우저에 전송합니다.
  6. 웹 브라우저가 응답받은 파일을 렌더링하여 화면으로 구성한다.
    • html을 파싱하여 DOM 노드를 만들고 이 DOM 노드들을 병합하여 DOM Tree를 구성한다.
      • HTML 파서는 <script> 태그를 만나면 Javascript 코드를 실행하기 위해 DOM 생성 프로세스를 중지하고 자바스크립트 엔진으로 권한을 넘긴다.
      • 제어 권한을 넘겨받은 자바스크립트 엔진은 <script> 태그 내의 Javascript 코드 또는 src 속성에 정의된 Javascript 파일을 로드하고 파싱 하여 실행한다.
      • Javascript의 실행이 완료되면 다시 HTML 파서로 제어 권한을 넘겨서 중지했던 시점으로 돌아가 DOM 생성을 재개한다.
    • CSS를 파싱하여 CSSOM(CSS Object Model) Tree를 만든다.
    • DOM Tree와 CSSOM을 이용해 렌더 트리를 만든다.
    • 렌더 트리를 배치한 후 UI 백엔드가 동작하여 각 객체를 화면의 픽셀값으로 나타낸다.

탐색

DNS 조회

image

웹 페이지를 탐색하는 첫 단계는 해당 페이지의 자원이 어디에 위치하는지 찾는 것입니다. 만약 https://example.com를 탐색한다면 HTML 페이지는 IP 주소가 93.184.216.34인 서버에 위치하고 있습니다. 만약 이 사이트를 한 번도 방문한 적이 없다면 DNS 조회가 필요합니다.

브라우저는 DNS 조회를 요청합니다. 이는 최종적으로 이름 서버에 의해서 처리되고, IP 주소로 응답합니다. 최초의 요청 이후에, IP는 일정 기간 동안 캐시됩니다. 이름 서버에 다시 연락하는 대신 캐시에서 IP 주소를 검색하여 후속 요청 속도를 높입니다.

DNS 조회는 보통 호스트 이름 하나당 한 번만 수행됩니다. 하지만 DNS 조회는 요청된 페이지에서 참조하는 다른 호스트 이름에 대해서는 각각 수행해야합니다. 만약 글꼴, 이미지, 스크립트, 광고 그리고 다른 자원들이 서로 다른 호스트 이름을 가지고 있다면, DNS 조회는 각각에 대해서 모두 수행되어야 합니다.


TCP 핸드셰이크(TCP Handshake)

IP 주소를 알고난 후에는, 브라우저는 서버와 TCP 3-way handshake를 통해 연결을 설정합니다. 이 방식은 데이터를 전송하기 전에 (주로 HTTPS를 통해서) 통신하려는 두 주체(이 경우에는 브라우저와 웹 서버)가 TCP 소켓 연결을 위한 매개변수를 주고 받을 수 있도록 만들어졌습니다.

TCP의 3방향 핸드셰이크 기술은 “SYN-SYN-ACK” (더 정확히는 SYN, SYN-ACK, ACK)로 불리기도 합니다. 두 컴퓨터 간 TCP 세션을 협상하고 시작하기 위해서 TCP가 3개의 메세지를 전달하기 때문입니다. 이는 요청이 보내지기 전에 3개의 추가적인 메세지가 컴퓨터 사이에 주고받아진다는 의미입니다.


TLS 협상(TLS Negotiation)

HTTPS를 이용한 보안성있는 연결을 위해서는 TLS 협상이라고 할 수 있는 또 다른 “핸드셰이크”가 필요합니다. 이 핸드셰이크는 통신 암호화에 쓰일 암호를 결정하고, 서버를 확인하고, 실제 데이터 전송 전에 안전한 연결이 이루어지도록 합니다. 이를 위해서 자원에 대한 실제 요청 전에 클라이언트에서 서버로 3번 더 왕복해야합니다.

image


응답

웹 서버로 한 번 연결이 성립되고 나면, 브라우저는 유저 대신에 초기 HTTP GET request를 보냅니다. 웹사이트는 대게 HTML 파일을 요청합니다. 서버가 요청을 받으면, 관련 응답 해더와 함께 HTML의 내용을 응답하게 됩니다.


웹 브라우저

주요 기능

브라우저의 주요 기능은 사용자가 선택한 자원을 서버에 요청하고 브라우저에 표시하는 것이다. 자원은 보통 HTML 문서지만 PDF나 이미지 또는 다른 형태일 수 있다. 자원의 주소는 URI(Uniform Resource Identifier)에 의해 정해진다.

브라우저는 HTML과 CSS 명세에 따라 HTML 파일을 해석해서 표시하는데 이 명세는 웹 표준화 기구인 W3C(World Wide Web Consortium)에서 정한다. 과거에는 브라우저들이 일부만 이 명세에 따라 구현하고 독자적인 방법으로 확장함으로써 웹 제작자가 심각한 호환성 문제를 겪었지만 최근에는 대부분의 브라우저가 표준 명세를 따른다.

브라우저의 사용자 인터페이스는 서로 닮아 있는데 다음과 같은 요소들이 일반적이다.

  • URI를 입력할 수 있는 주소 표시 줄
  • 이전 버튼과 다음 버튼
  • 북마크
  • 새로 고침 버튼과 현재 문서의 로드를 중단할 수 있는 정지 버튼
  • 홈 버튼

브라우저의 사용자 인터페이스는 표준 명세가 없음에도 불구하고 수 년간 서로의 장점을 모방하면서 현재에 이르게 되었다. HTML5 명세는 주소 표시줄, 상태 표시줄, 도구 모음과 같은 일반적인 요소를 제외하고 브라우저의 필수 UI를 정의하지 않았다. 물론 파이어폭스의 다운로드 관리자와 같이 브라우저에 특화된 기능도 있다.


브라우저의 구조

브라우저의 주요 구성 요소


  1. 사용자 인터페이스

    사용자가 접근할 수 있는 영역입니다. URI를 입력할 수 있는 주소 표시줄, 이전/다음 버튼, 북마크 메뉴, 새로 고침 버튼과 현재 문서의 로드를 중단할 수 있는 정지 버튼 , 홈 버튼 등 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분입니다.

  2. 브라우저 엔진

    사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어합니다. Data Storage를 참조하며 로컬에 데이터를 쓰고 읽으면서 다양한 작업을 합니다.

  3. 렌더링 엔진

    웹 서버로부터 응답 받은 자원을 웹 브라우저 상에 나타냅니다. 예를 들어 HTML 문서를 응답받으면 HTML과 CSS를 파싱 하여 화면에 표시합니다. 브라우저의 동작 원리를 이해하려면 렌더링 엔진의 이해가 중요합니다.

    브라우저는 서버로부터 HTML 문서를 응답받으면 렌더링 엔진의 HTML 파서와 CSS 파서에 의해 파싱(parsing)되어 DOM, CSSOM 트리로 변환되고 렌더 트리로 결합합니다. 이렇게 생성된 렌더 트리를 기반으로 브라우저는 웹 페이지를 나타냅니다.

  4. 통신

    HTTP 요청과 같은, 서버와 통신이 가능하게 하는 네트워크 호출에 사용됩니다. 이것은 플랫폼 독립적인 인터페이스이고 각 플랫폼 하부에서 실행됩니다.

  5. UI 백엔드

    select, input 등 기본적인 위젯을 그리는 인터페이스 입니다. 플랫폼에서 명시하지 않은 일반적인 인터페이스로서, OS 사용자 인터페이스 체계를 사용합니다.

  6. 자바스크립트 해석기

    자바스크립트 코드를 해석하고 실행합니다.

  7. 자료 저장소

    자료를 저장하는 계층입니다. 쿠키를 저장하는 것과 같이 모든 종류의 자원을 하드 디스크에 저장할 필요가 있습니다. HTML5 명세에는 브라우저가 지원하는 ‘웹 데이터 베이스’가 정의되어 있습니다. Cookie, Local Storage, Indexed DB 등 브라우저 메모리를 활용하여 저장하는 영역입니다.


렌더링 엔진

렌더링 엔진은 HTML, XML, 이미지 등 요청받은 내용을 브라우저 화면에 표시하는 엔진입니다.

동작 과정

렌더링 엔진은 통신으로부터 요청한 문서의 내용을 얻는 것으로 시작하는데 문서의 내용은 보통 8KB 단위로 전송된다.

렌더링 엔진의 동작 과정

① 렌더링 엔진은 HTML 문서를 파싱 하여 DOM 트리를 구축합니다.
② 그다음 외부 CSS 파일과 함께 포함된 스타일 요소를 파싱 합니다.
③ DOM 트리와 ②의 결과물을 합쳐 렌더 트리를 구축합니다.
④ 렌더 트리 각 노드에 대해 화면 상에서 배치할 곳을 결정합니다.
⑤ UI 백엔드에서 렌더 트리의 각 노드를 그립니다.

렌더링 엔진은 HTML 문서를 파싱하고 “콘텐츠 트리” 내부에서 태그를 DOM 노드로 변환한다. 그 다음 외부 CSS 파일과 함께 포함된 스타일 요소도 파싱한다. 스타일 정보와 HTML 표시 규칙은 “렌더 트리”라고 부르는 또 다른 트리를 생성한다.

렌더 트리는 색상 또는 면적과 같은 시각적 속성이 있는 사각형을 포함하고 있는데 정해진 순서대로 화면에 표시된다.

렌더 트리 생성이 끝나면 배치가 시작되는데 이것은 각 노드가 화면의 정확한 위치에 표시되는 것을 의미한다. 다음은 UI 백엔드에서 렌더 트리의 각 노드를 가로지르며 형상을 만들어 내는 그리기 과정이다.

일련의 과정들이 점진적으로 진행된다는 것을 아는 것이 중요하다. 렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시하는데 모든 HTML을 파싱할 때까지 기다리지 않고 배치와 그리기 과정을 시작한다. 네트워크로부터 나머지 내용이 전송되기를 기다리는 동시에 받은 내용의 일부를 먼저 화면에 표시하는 것이다.

웹킷 동작 과정


파서

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


HTML 파서

HTML 파서는 HTML 마크업을 파싱 트리로 변환합니다. HTML은 일반적인 하향식 또는 상향식 파서로 파싱이 안되는데 그 이유는 다음과 같습니다.

  1. 언어의 너그러운 속성.
  2. 잘 알려져 있는 HTML 오류에 대한 브라우저의 관용.
  3. 변경에 의한 재파싱. 일반적으로 소스는 파싱하는 동안 변하지 않지만 HTML에서 document.write을 포함하고 있는 스크립트 태그는 토큰을 추가할 수 있기 때문에 실제로는 입력 과정에서 파싱이 수정된다.

일반적인 파싱 기술을 사용할 수 없기 때문에 브라우저는 HTML 파싱을 위해 별도의 파서를 생성합니다.

HTML 파싱 과정(HTML5 명세에서 가져옴)




image




① 렌더링 엔진은 HTML 문서를 파싱 하여 DOM 트리를 구축합니다.

DOM (문서 객체 모델 : Document Object Model)

“파싱 트리”는 DOM 요소와 속성 노드의 트리로서 출력 트리가 됩니다. 이것은 HTML 문서의 객체 표현이고 외부를 향하는 자바스크립트와 같은 HTML 요소의 연결 지점입니다. 트리의 최상위 객체는 문서입니다.

DOM은 마크업과 1:1의 관계를 맺습니다.

1
2
3
4
5
6
<html>
    <body>
        <p>Hello World</p>
        <div><img src="example.png" /></div>
    </body>
</html>  

이것은 아래와 같은 DOM 트리로 변환할 수 있습니다.

예제 마크업의 DOM 트리


동작과정

image

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


② CSSOM(Css Object Model)을 생성합니다.

CSS 파일은 스타일 시트 객체로 파싱 되고 각 객체는 CSS 규칙을 포함합니다. CSS 규칙 객체(CSSOM)는 선택자와 선언 객체 그리고 CSS 문법과 일치하는 다른 객체를 포함합니다.

CSS 파싱

렌더링 엔진은 HTML문서를 한줄 한줄 순차적으로 파싱하며 DOM을 생성합니다. 그러다 CSS를 로드하는 <link> 혹은 <style>를 만나면 DOM 생성을 중지한 후 CSS파싱의 결과물인 CSSOM을 생성하는 과정을 진행합니다.

CSS파싱 과정은 바이트 > 문자 > 토큰 > 노드 > CSSOM 생성 순으로 HTML의 파싱과정과 동일합니다.

image


③ 렌더 트리 구축

image

CSSOM 트리와 DOM 트리를 결합하여, 표시해야 할 순서로 내용을 그려낼 수 있도록 하기 위해 렌더 트리를 형성한다. 이 과정을 Attachment라고 합니다.

DOM 트리가 구축되는 동안 브라우저는 렌더 트리를 구축합니다. 표시해야 할 순서와 문서의 시각적인 구성 요소로써 올바른 순서로 내용을 그려낼 수 있도록 하기 위한 목적이 있습니다.

파이어폭스는 이 구성 요소를 “형상(frames)” 이라고 부르고 웹킷은 “렌더러(renderer)” 또는 “렌더 객체(render object)”라는 용어를 사용합니다.

렌더 트리와 DOM 트리 대응

렌더러는 DOM 요소에 부합하지만 1:1로 대응하는 관계는 아닙니다. 예를 들어 “head” 요소와 같은 비시각적 DOM 요소는 렌더 트리에 추가되지 않습니다. 또한 display 속성에 “none” 값이 할당된 요소는 트리에 나타나지 않습니다.(visibility 속성에 “hidden” 값이 할당된 요소는 트리에 나타남).

어떤 렌더 객체는 DOM 노드에 대응하지만 트리의 동일한 위치에 있지 않습니다. float 처리된 요소 또는 position 속성 값이 absolute로 처리된 요소는 흐름에서 벗어나 트리의 다른 곳에 배치된 상태로 형상이 그려집니다. 대신 자리 표시자가 원래 있어야 할 곳에 배치됩니다.

image


DOM과 CSSOM을 결합하여 렌더트리 생성

렌더 트리를 생성하려면 브라우저는 대략 3가지 작업을 수행해야 합니다.

  • DOM 트리의 루트부터 노드 각각을 모두 탐색한다.
    • 이 때 화면에 표시되지 않는 일부 노드들(script, meta 태그 등..)은 렌더 트리에서 제외된다.
    • 또한 CSS 속성 중 display:none 같이 화면에서 숨겨지는 속성도 렌더 트리에 반영되지 않는다.
  • 화면에 표시되는 각 노드에 대해 적절하게 일치하는 CSSOM 규칙을 찾아 적용한다.
  • 화면에 표시되는 노드를 콘텐츠 및 계산된 스타일과 함께 렌더트리로 생성된다.


④ 렌더 트리 배치 (레이아웃)

렌더 트리는 위치와 크기를 가지고 있지 않기 때문에, 어느 공간에 위치해야 할지 각 객체들에게 위치(position)와 크기(size)를 결정해줍니다.

image

렌더 트리가 생성되고, 기기의 뷰포트 내에서 렌더 트리의 노드가 정확한 위치와 크기를 계산합니다. 이때 모든 상대적인 값이 픽셀값으로 변환됩니다. CSS에 상대적인 값인 %, rem, vh으로 할당된 값들은 절대적인 값인 px단위로 변환 됩니다. 이 과정을 배치(Layout) 또는 Reflow 라고 합니다.

배치는 보통 다음과 같은 형태로 진행됩니다.

  1. 부모 렌더러가 자신의 너비를 결정.
  2. 부모가 자식을 검토.
    1. 자식 렌더러를 배치(자식의 x와 y를 설정)
    2. (부모와 자식이 더티하거나 전역 배치 상태이거나 또는 다른 이유로) 필요하다면 자식 배치를 호출하여 자식의 높이를 계산한다.
  3. 부모는 자식의 누적된 높이와 여백, 패딩을 사용하여 자신의 높이를 설정한다. 이 값은 부모 렌더러의 부모가 사용하게 된다.
  4. 더티 비트 플래그를 제거한다.


⑤ 렌더 트리 그리기

image

렌더 트리가 만들어져 레이아웃이 구성되었으면 UI 백엔드가 동작하여 렌더 트리의 각 객체를 화면의 픽셀(px) 값으로 나타냅니다.

이것은 실제로 요소가 stacking contexts에 쌓이는 순서입니다. 스택은 뒤에서 앞으로 그려지기 때문에 이 순서는 그리기에 영향을 미칩니다. 블록 렌더러가 쌓이는 순서는 다음과 같습니다.

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


자바 스크립트

자바스크립트는 자바스크립트 엔진이 처리합니다. HTML 파서는 <script> 태그를 만나면 Javascript 코드를 실행하기 위해 DOM 생성 프로세스를 중지하고 자바스크립트 엔진으로 권한을 넘깁니다. 제어 권한을 넘겨받은 자바스크립트 엔진은 <script> 태그 내의 Javascript 코드 또는 src 속성에 정의된 Javascript 파일을 로드하고 파싱 하여 실행합니다. Javascript의 실행이 완료되면 다시 HTML 파서로 제어 권한을 넘겨서 중지했던 시점으로 돌아가 DOM 생성을 재개합니다.

브라우저 동작 원리

이처럼 브라우저는 동기적으로 HTML, CSS, Javascript를 처리합니다. 하지만 자바스크립트 엔진에 제어 권한이 있을 때 Javascript 코드가 완성되지 않은 DOM을 조작하게 된다면 어떻게 될까요? 당연히 에러가 발생할 것입니다.

이것이 HTML 파일에서 Javascript 코드를 <body> 태그 하단에 위치시키는 이유입니다.



참고

This post is licensed under CC BY 4.0 by the author.