브라우저 동작 원리

지송현·2023년 1월 20일
0

web

목록 보기
1/13

브라우저란?

웹 브라우저, 인터넷 브라우저 또는 웹 탐색기는 웹 서버에서 이동하며(navigate) 쌍방향으로 통신하고 HTML 문서나 파일을 출력하는 그래픽 사용자 인터페이스 기반의 응용 소프트웨어이다. 웹 브라우저는 대표적인 HTTP 사용자 에이전트의 하나이기도 하다.
-위키백과

웹 페이지를 가져와 표시하기 위해 http라는 통신 규약을 이용해 웹 서버와 통신하는 어플리케이션이다.

이번 글에서는 크롬 브라우저 위주로 설명한다.

주요 기능

브라우저의 주요 기능은 방금 말했듯이 사용자가 선택한 자원을 서버에 요청하고 그 결과를 브라우저에 표시하는 것이다. 자원은 보통 html 문서지만 pdf나 이미지 등이 되기도 한다. 자원의 주소는 uri에 의해 정해진다.

브라우저는 html과 css 명세에 따라 html 파일을 해석해서 표시하는데 이 명세는 웹 표준화 기구인 w3c에서 정한다.

표준 명세에서 다음과 같은 기능을 제시한다.

  • http, https
  • html, xml, xhtml
  • gif, png, jpeg, svg 등을 포함한 그래픽 파일 포맷
  • css, javascript
  • 쿠키와 디지털 인증서
  • 즐겨찾기 아이콘 및 플러그인 지원

위 예시 말고도 다양한 기능을 제공한다.

기본 구조

브라우저의 기본 구조는 아래와 같다.

  1. 사용자 인터페이스 : 주소 표시줄, 이전/다음 버튼 등 요청한 페이지를 제외하고 사용자가 상호작용 할 수 있는 모든 부분
  2. 브라우저 엔진 : 최상위 프로세스로써 다른 프로세스들을 조율한다. 또한 주소 창, 뒤로/앞으로 가기 버튼 등의 사용자 인터페이스 부분을 제어하며 네트워크 요청 및 파일 액세스와 같은 웹 브라우저 권한이 부여된 보이지 않는 부분까지 제어한다.
  3. 렌더링 엔진 : 요청한 콘텐츠를 화면에 표시한다. html과 css를 파싱하여 화면에 표시한다.
  4. 통신 : http 요청과 같은 네트워크 호출에 사용된다. 요청을 받으면 네트워크 처리 후 응답을 전달함.
  5. 자바스크립트 해석기 : js 코드를 해석하고 실행한다.
  6. ui 백엔드 : render tree를 브라우저에 그림
  7. 자료 저장소 : 쿠키, 로컬 스토리지 등 모든 종류의 자원을 저장한다.

탐색 과정

브라우저 주소창에 무언가 입력했을 때 일어나는 일을 보며 위의 구성요소들이 각각 어떤 역할을 하는지 살펴보자.

0. 가장 먼저..

위에서 브라우저 엔진이 브라우저 밖의 보이지 않는 부분들을 담당한다고 했다. 브라우저 엔진은 버튼이나 입력창을 그리는 UI 스레드, 인터넷에서 데이터를 수신하기 위해 통신 스택을 건드리는 네트워크 스레드, 파일 접근을 위한 스토리지 스레드 등을 가지고 있다.

따라서 주소창에서 url을 입력하는 순간 브라우저 엔진의 UI 스레드가 동작한다.

1. 입력 처리

사용자가 주소창에 입력을 하게 되면 UI 스레드는 가장 먼저 '검색어인지 UI인지'부터 판단한다. 크롬에서는 주소창이 검색창도 겸하기 때문에 유저가 입력한 문구를 검색 엔진에 보낼 지, 요청한 페이지로 연결할 지 결정해야 한다.

2. 탐색 시작

사용자가 주소를 입력하고 엔터를 치면 UI 스레드가 사이트의 컨텐츠를 받기 위해 네트워크 요청을 초기화한다.

이 시점에 네트워크 스레드는 http 301같은 서버의 리다이렉션 헤더를 수신할 수도 있다. 그럴 경우 네트워크 스레드는 UI 스레드에게 서버가 리다이렉션을 요청했음을 알린다. 그러면 UI 스레드는 새로운 url 요청을 초기화한다.

  • 네트워크 초기화 :
    데이터 오브젝트나 변수의 초기 값 할당을 의미한다. 여기선 '네트워크 요청을 시작한다'와 같은 맥락이다.

3. 응답 읽기

응답이 들어오기 시작하면 네트워크 스레드가 헤더의 content-type을 확인한다.

응답이 html 파일이면 다음으로 랜더링 엔진에 데이터를 전달한다. 그 외 zip 또는 다른 형식의 파일일 경우 다운로드 요청이라는 뜻으로 다운로드 매니저에 데이터를 넘긴다.

이 시점에 안전 브라우징 체크도 수행한다. 만약 도메인과 응답 데이터가 이미 알려진 악성 사이트와 일치한다면 네트워크 스레드는 경고 페이지를 보여준다. 또한, Cross Origin Read Blocking(CORB) 체크를 해서 민감한 cross-site 데이터가 렌더링 엔진에 도달하는 것을 막는다.(위험한 주소가 숨겨져 있을 수 있기 때문에)

  • CORB :
    웹에서 cross origin의 xml, html, json 등의 데이터를 읽어오지 못하게 브라우저에서 막는 동작을 말한다.
    cors 정책을 위반했을 때 막는 행위 자체를 말하는 듯 하다. 이에 대해서는 다음 포스팅에서 자세하게 다루도록 하자.

4. 렌더링 프로세스 찾기

위의 확인 작업이 끝나고 네트워크 스레드가 검색이 아닌 요청된 사이트로 이동해야 함을 확신하면, UI 스레드에게 데이터가 준비되었음을 알린다. 그리고 UI 스레드는 웹 페이지 랜더링을 할 랜더링 프로세스를 찾는다.

5. 내비게이션 실행

이제 다음과 같은 과정을 수행한다.

  1. 탐색을 커밋하기 위해 브라우저 프로세스에서 랜더링 프로세스로 IPC가 전송됨.
  2. 데이터 스트림을 전달하여 렌더링 프로세스가 html 데이터를 계속 받을 수 있게 한다.
  3. 렌더링 프로세스에서 커밋을 확인한다.
  4. 브라우저 프로세스가 탐색을 완료하고 문서 로딩 단계를 시작한다.

문서 로딩 단계에서

  1. 주소창 업데이트
  2. 보안 표시와 사이트 설정 UI가 새 페이지의 정보를 반영해 갱신됨
  3. 탭에 대한 세션 기록이 업데이트 됨.
  4. 탭이나 창을 닫은 이후 탭과 세션을 복원할 수 있도록 세션 기록이 디스크 드라이브에 저장됨.

6. 초기 로드 완료

내비게이션이 실행되면 렌더링 프로세스는 계속 리소스를 로딩하고 페이지를 렌더링한다. 이 렌더링 프로세스의 동작에 대해서는 조금 후에 보도록 하자. 렌더링 프로세스가 렌더링을 끝내면 브라우저 프로세스로 IPC 메시지를 보낸다.(클라이언트 사이드의 js가 여전히 추가적인 리소스를 로드하거나 이후에 화면을 렌더링할 수 있다.)


렌더링 프로세스의 동작 과정

랜더링 프로세스는 탭 내부에서 발생하는 모든 작업을 담당한다. 랜더링 프로세스의 메인 스레드가 개발자가 구현한 대부분의 코드를 처리한다. 웹 페이지를 효율적이고 부드럽게 렌더링하기 위해 별도의 컴포지터 스레드와 래스터 스레드가 실행된다.

즉, 랜더링 프로세스의 주 역할은 서버로부터 받은 html, css, js를 사용자와 상호작용할 수 있는 웹페이로 변환하는 것이다.

1. 구문 분석(parsing)

DOM 생성

랜더링 프로세스가 탐색을 위해 커밋 메세지를 받고 html 데이터를 받기 시작할 때(네비게이션 수행) 메인 스레드는 문자열(html)을 파싱해서 DOM(document object model)으로 변환하기 시작한다.

DOM은 브라우저 내부적으로 웹 페이지를 표현하는 방법일 뿐만 아니라 웹 개발자가 js를 통해 상호작용할 수 있는 데이터 구조이자 API이다.

추가 리소스 로딩

웹사이트는 일반적으로 이미지, css, js와 같은 외부 리소스를 사용한다. 이러한 파일은 네트워크나 캐시에서 로딩해야 한다. DOM을 구축하기 위해 파싱하는 동안 이런 리소스를 만날 때마다 하나하나 요청할 수도 있지만 속도를 높이기 위해 '프리로드 스캐너'가 동시에 실행된다. html 문서에 img, link 같은 태그가 있으면 프리로드 스캐너는 html parser가 생성한 토큰을 확인하고 브라우저 프로세스의 네트워크 스레드에 요청을 보낸다.

파싱을 중단하는 자바스크립트

html 파서는 script 태그를 만나면 문서의 파싱을 일시 중단한 다음 js 코드를 로딩하고 파싱해 실행한다. js는 DOM을 조작할 수 있기 때문이다. 따라서 html parser는 js의 실행이 끝날때까지 기다려야 한다.

2. 스타일 계산

html parsing이 끝나도 웹 페이지 구성은 끝나지 않는다. css가 아직 반영되지 않았다.

메인 스레드는 css를 파싱하고 각 DOM 노드에 해당되는 계산된 스타일을 확정한다. 계산된 스타일은 css 선택자로 구분된 요소에 적용될 스타일에 관한 정보이다.

css를 개발자가 작성하지 않았더라도 dom 노드에는 스타일이 적용되어 있다. 각 브라우저마다 제공하는 기본 스타일이다.

3. 레이아웃

이제 렌더링 프로세스가 문서의 구조와 각 노드의 스타일을 알게 되었지만 여전히 페이지를 렌더링하기에는 부족하다. 각 노드에 대한 스타일은 알지만 그것들이 어떤 식으로 배치되는지는 모르기 때문이다.

레이아웃은 요소의 기하학적인 속성을 찾는 과정이다. 메인 스레드는 DOM과 계산된 스타일을 훑으며 레이아웃 트리를 만든다. 레이아웃 트리는 x, y좌표, 박스 영역의 크기와 같은 정보를 가지고 있다. 레이아웃 트리는 DOM 트리와 비슷한 구조이지만 웹 페이지에 보이는 요소에 관한 정보만 담고 있다.(display : none이 적용된 요소는 포함되지 않는다. 그러나 visiablity: hidden이 적용된 것은 포함된다.)

4. 페인트

여전히 완성된 페이지를 만들 수 없다. 무엇부터 그려야 하는지 순서가 정해지지 않았다. 예를 들어 z-index같은 속성을 고려하지 않고 html에 작성된 순서로 적용된다면 문제가 될 것이다.

페인트 단계에서 메인 스레드는 페인트 기록을 생성하기 위해 레이아웃 트리를 순회한다. 페인트 기록은 1. 배경 2. 텍스트 3. 직사각형과 같이 페인팅 과정을 기록한 것이다.

5. 합성

이제 브라우저는 문서의 구조와 각 요소의 스타일, 요소의 기하학적 속성, 페인트 순서까지 알고 있다. 다음은 무엇이 필요할까? 이제 앞의 정보들을 가지고 스크린의 픽셀로 바꾸는 것을 래스터화(rasterizing)이라고 한다.

가장 단순한 방식의 래스터화는 아마 뷰포트 안쪽을 래스터화하는 방식일 것이다. 사용자가 웹 페이지를 스크롤하면 이미 래스터화한 프레임을 움직이고 나머지 빈 부분을 추가로 래스터화한다. 이 방식은 크롬이 처음 출시되었을 때 사용한 방식이지만 최근에는 합성(compositing)이라는 보다 정교한 방법을 사용한다.

합성은 웹 페이지의 각 부분을 레이어로 분리해 별도로 래스터화하고 컴포지터 스레드라고 하는 별도의 스레드에서 웹 페이지로 합성하는 기술이다. 스크롤되었을 때 레이어는 이미 래스터화되어 있으므로 새 프레임을 합성하기만 하면 된다. 애니메이션 역시 레이어를 움직이고 합성하는 방식으로 만들 수 있다. 해당 레이어는 개발자 도구의 Layers panel에서 볼 수 있다.

6. 래스터와 합성

앞의 레이어 트리가 생성되고, 페인트 순서까지 결정되면 메인 스레드는 다음과 같은 과정을 실행한다.

  1. 컴포지터 스레드에 정보를 커밋한다.
  2. 그 후 컴포지터 스레드가 각 레이어를 래스터화한다.
  3. 레이어는 한 페이지의 전체 길이만큼 클 수 있기 때문에 컴포지터 스레드는 레이어들을 여러 타일로 쪼갠다.
  4. 쪼갠 각 타일을 다수의 래스터 스레드에게 보낸다.
  5. 래스터 스레드들은 각 타일을 레스터화하고 gpu 메모리에 저장한다.

컴포지터 스레드는 서로 다른 래스터 스레들에 대해 우선순위를 정할 수 있어 뷰포트 안이나 근처의 것들이 먼저 래스터화될 수 있다. 또한 레이어는 줌인같은 동작을 처리하기 위해 여러 해상도별로 타일 세트를 가지고 있다.

이후 타일이 레스터화되면 컴포지터 스레드는 '합성 프레임'을 생성하기 위해 타일의 정보를 모은다. 이 타일의 정보를 '드로 쿼드(draw quads)'라고 부른다.

  • 드로 쿼드 :
    메모리에서 타일의 위치와 웹 페이지 합성을 고려해 타일을 웹 페이지의 어디에 그려야 하는지에 관한 정보를 가지고 있다.
  • 합성 프레임 :
    웹 페이지의 프레임을 나타내는 드로 쿼드의 모음

이후

  1. 합성 프레임은 IPC를 통해 브라우저 프로세서로 넘어간다.
  2. 합성 프레임들은 gpu로 전송되어 화면에 표시된다.
  3. 만약 스크롤 이벤트가 발생하면 컴포지터 스레드는 gpu에게 보내질 다른 컴포지터 프레임을 생성한다.

합성의 장점은 메인 스레드와 별개로 작동할 수 있다는 점이다. 컴포지터 스레드는 js의 실행이나 스타일 계산을 기다리지 않아도 된다. 그러나 레이아웃이나 페인트를 다시 계산해야 할 경우 메인스레드가 관여해야 한다.


여기까지가 사용자가 주소창에 url을 입력했을 때 결과가 화면에 보이기까지 브라우저의 동작 과정이다.

profile
백엔드 개발자

0개의 댓글