브라우저 동작 원리 정리

이병관·2022년 2월 24일
0
post-thumbnail

웹브라우저의 동작 원리

https://gs.statcounter.com/search-engine-market-share

웹 트래픽 분석사이트인 StatCounter에 따르면 구글 크롬 브라우저가 웹브라우저의 전체 시장을 77%이상 점유하는 모습입니다. 이는 시장 자체를 압도하는 수치이며 브라우저의 동작 원리를 어느정도 이해해야 함을 상기시킵니다.

따라서 이번 주차에서는 브라우저의 동작원리, 구글 크롬과 애플 사파리가 사용중인 웹킷(Webkit)을 위주로 동작 원리를 설명하겠습니다.

크롬은 v28부터 웹킷에서 파생된 블링크(Blink)라는 엔진을 사용중이며, 파이어폭스, 인터넷 익스플로러, 사파리를 제외한 모든 웹브라우저에서 사용중입니다.


왜 웹브라우저 동작 원리를 알아야 하나요?

브라우저 동작원리를 모른다면 단순히 코드를 줄이고,

재활용성을 높이는 작업 외에 Low Level 단계의 개선점 찾아보려는 생각을 못할수있습니다.



따라서 어떤식으로 동작하는지 안다면 어느 부분에서 문제가 발생할지 예측할수 있고, 해당 부분을 집중적으로 개선시켜 클라이언트의 이탈율을 막을수 있고, 전체적으로는 검색엔진 최적화(SEO)를 꾀할 수 있습니다.

웹 브라우저의 주요 기능

웹 브라우저의 주요기능은

첫째, 사용자가 입력한(원하는) 웹페이지, 이미지, 동영상 등의 자원을 서버에게 요청하는 역할

둘째, 서버로부터 전달(응답)받은 자원을 화면에 출력하는 역할입니다.

다음은 일반적인 사용자 인터페이스입니다

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

브라우저의 기본 구조


구성 요소


네비게이션

브라우저의 주소 표시줄에 URL을 입력할 경우, 브라우저가 인터넷에서 데이터를 가져와 페이지를 표시합니다.

그 전에 사이트를 요청하고, 브라우저가 해당 페이지의 렌더링을 준비하는 과정인

'네비게이션'에 대해 간단히 알아보겠습니다.

주소표시줄, 북마크, 등 탭 영역의 밖부분과, 네트워크 요청, 파일접근과 같은 부분을 처리하는 프로세스인

브라우저 프로세스가 이 부분을 맡습니다

브라우저 프로세스에는 다음과 같은 스레드가 존재합니다

  • 네트워크 스레드 : 인터넷에서 데이터를 가져오기 위해 네트워크 스택을 다룸
  • 스토리지 스레드 : 파일에 대한 접근 제어
  • UI스레드 : 브라우저의 버튼과 입력란을 그림

1) 입력처리

주소창에 사용자가 입력을 시작하면, UI스레드는 우선 입력되는 내용이 검색어인지, URL인지 확인합니다.

이 입력된 내용을 판단하여 검색 엔진을 킬지, 요청한 사이트를 킬지 결정합니다.

2) 네비게이션 시작

엔터키를 입력후, 사이트의 컨텐츠를 가져오기 위해 UI스레드가 네트워크를 호출하고, 그와 동시에 로딩스피너를 표시합니다.

네트워크 스레드는 요청에 대한 DNS LOOKUP 그리고 TLS 연결 설정과 같은 프로토콜을 통해 요청을 처리합니다

DNS: Domain Name System, 웹사이트의 IP주소와 도메인 주소를 이어주는 시스템
TLS: 인터넷을 통한 통신 보안을 제공하며 클라이언트/서버 어플리케이션의 비밀을 유지하고, 안정적인 방식으로 통신할수 있도록 해준다
참고: HTTPS는 HTTP 프로토콜에 SSL/TLS을 결합한것

3) 응답 읽기

응답 본문인 페이로드가 들어오면

네트워크 스레드는 페이로드의 형식을 확인합니다.

  • HTML파일과 같이 렌더러 프로세스가 다룰수 있다면 렌더러 프로세스에게 전달합니다
  • 렌더러 프로세스가 다룰수 없다면 다운로드 매니저에 데이터를 전달합니다.

또한 이 단계에서 응답된 데이터가 안전한 사이트인지, 검사가 실행됩니다.

만일 악성 사이트로 알려진 사이트와 일치할 경우 네트워크 스레드는 경고 메세지를 표시합니다.

4) 렌더러 프로세스 찾기.

검사가 끝나고, 요청된 사이트로 이동될때, 네트워크 스레드가 UI스레드에게 데이터 준비가 되엇음을 알립니다.

UI스레드는 웹 페이지 렌더링을 수행할 렌더러 프로세스를 찾습니다.

5) 네비게이션 실행

데이터와 렌더러 프로세스가 준비되었다면, 네비게이션을 실행합니다.

브라우저 프로세스렌더러 프로세스가 HTML 데이터를 계속 수신할수 잇도록 데이터 스트림을 전달합니다.

렌더러 프로세스의 렌더링이 끝날경우, UI스레드는 로딩스피너의 작동을 중지시킵니다.

이제 렌더러 프로세스는 어떠한 과정으로 렌더링을 하는지 알아보겠습니다.

렌더링 엔진의 동작 과정


렌더링 엔진의 동작과정은 기본적으로 크게 다음과 같이 나누어 집니다.

파싱

자 이제, 인터넷에 주소를 쳤습니다. 그렇다면 클라이언트는 그 사이트의 index.html이든 뭐든 알맞은 html을 가져오게 되겟죠?

그렇다면 브라우저 렌더링 엔진은 HTML문서를 파싱하여

브라우저가 이해할수 있는 DOM Document Object Model 을 그립니다.


DOM?

간략하게 살펴보자면, 웹페이지를 이루는 태그들(<html>, <head>, <body>, <title...>)을

자바스크립트가 이용하도록 브라우저가 트리구조로 만든 객체 모델입니다.

즉 간단하게 말하자면 HTML과 자바스크립트 사이를 이어주는 다리라고 볼수 있죠


HTML을 파서가 파싱을 할때, 일반적인 파서로는 파싱이 불가능 하기에 따로 HTML파서로 파싱을 합니다.

그 이유는 다음과 같습니다

  1. HTML은 '너그러운 속성'(forgiving nature)을 가지고있습니다
  2. 다시 말해, <html>태그를 입력하지않거나, 중간에 태그를 빠트리거나, 닫는 태그를 닫지 않아도 자체적으로 에러를 복구합니다.

2. 스크립트나, 스타일 태그를 만날경우 html 파싱을 잠시 중단해야합니다.

3. 파싱 중간에 스크립트나 스타일에 의해 DOM이 추가.변경.삭제 될수있습니다.

만일 그럴경우 HTML은 다시 처음부터 파싱하게 됩니다.

따라서 HTML은 전통적인 구문분석도 불가능하고 문맥 자유 문법이 아니기 때문에

HTML파서를 따로 두어 파싱을 진행합니다.

1 + 2 ) DOM(Document Object Model), CSSOM (CSS Object Model) 생성

  • HTML을 파싱하여 DOM 노드를 만든 뒤, 이 노드들을 병합하여 DOM 트리를 만듭니다

- HTML의 <body>, <p>, <div>등의 태그들을 '노드' 라 말합니다.

  • CSS를 파싱하여 CSSOM(CSS Object Model)트리를 만듭니다.

HTML 파싱, DOM 생성 과정

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html> 

HTML 파싱 과정은 다음과 같습니다.

  • 바이트 : 서버는 브라우저로 부터 요청받은 HTML 파일을 읽은 후, 메모리에 저장한 뒤, 메모리에 저장된 바이트(3C 62 6F....)를 응답합니다.
  • 문자 변환: 브라우저는 응답받은 바이트 형태의 문서를 meta태그의 charset 어트리뷰트에 지정된 인코딩 방식에 따라 문자열을 반환합니다.

<html><head>...</head><body><p>Hello<span>web performance</span>....</html>

만일 meta charset명시를 하지 않을 경우에는?

 웹브라우저 설정 상황에 따라 자동으로 인코딩을 추정해서 처리해주는데, 처리가 정확할 경우도 있지만, 그렇지 못하는 경우도 있습니다. 다양한 경우에 한글이 깨지지 않고 잘 보이기를 기대한다면 위 태그는 꼭 적어주는 것이 좋습니다.

  • 토큰화 : 문자열로 변환된 HTML 문서를, 문법적 의미를 가지는 코드의 최소 단위인 토큰(Token)으로 변환합니다.
{
  startTag:'html'
  content: {
    startTag: 'head'
    content: {
    ....
      startTag:'Title'
      content: {
        'Critical Path'
      }
      endTag:'Title'
    }
    endTag: 'head'

    content:{
      startTag:'body'
      content: {....}
      endTag:'body'
    }
  }
  endTag: 'html'
}
//토큰화된 HTML 예제

토큰은 시작 태그, 종료 태그, 속성 이름과 속성 값이 존재합니다.

  • Node: 토큰들의 내용에 따라 객체로 변환하여, 각 노드들을 생성합니다.

토큰의 내용들에 따라 html, head등 노드들이 구현됨

  • DOM : 요소간의 중첩에 따라 모든 노드들을 트리구조로 구성하여 DOM을 생성합니다

도식화 된 DOM TREE

DOM TREE

CSSOM(CSS Object Model)생성(Parsing)

HTML문서를 순차적으로 파싱하며 DOM을 생성 하다 CSS를 불러오는 link 또는 Style 태그를 만날시, DOM생성을 잠시 중단하고, CSS를 파싱하여 CSSOM을 생성합니다.

CSS파싱이 완료되면 렌더링 엔진은 다시 HTML 파싱이 중단된 지점으로 돌아가 파싱을 재개합니다.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet"> // CSS만남, CSS파일 서버 요청, CSSOM 생성시작
    <title>Critical Path</title>
    ...
</html> 


CSS파싱 과정은 HTML 파싱 과정과 동일하게

바이트 ➝ 문자 ➝ 토큰 ➝ 노드 ➝ CSSOM 생성 순으로 이루어져있습니다.

3) DOM트리 + CSSOM트리(Attachment) 및 렌더트리 구축

  1. DOM 트리의 루트(html)에서 시작해서 노드 각각을 읽어 나갑니다
    • <meta> 태그나 <script> 태그 등의 노드는 표시되지 않습니다.

(사용자에게 보이지않아도 되는 내용은 렌더링 출력에 반영되지 않습니다.)

  • 일부 노드는 CSS를 통해 숨겨지며 렌더 트리에서도 생략이 됩니다.

(예를들어 display:none 과 같이 숨겨지는 노드)

2. 각 노드에 대해 적절하게 일치되는 CSSOM 규칙을 찾아 적용합니다.

3. 표시된 노드를 콘텐츠 및 계산된 스타일과 함께 내보냅니다.

visibility : hidden 속성은 display : none 속성과 다릅니다. 전자는 요소를 보이지 않게 만들지만, 이 요소는 레이아웃에서 공간을 차지합니다. 즉, 비어 있는 상자로 렌더링 됩니다. 반면, 후자는 요소가 보이지 않고 레이아웃에 포함되지 않도록 렌더 트리에서 요소를 완전히 제거합니다.


JavaScript를 만나다

위의 과정들을 간략히 순차적으로 정리하자면 다음과 같습니다.

  1. 클라이언트에서 웹페이지를 접속할 시, 서버로 요청을 보냅니다.
  2. 서버에서 HTML 파일을 전송하고, 클라이언트는 HTML파일을 파싱합니다.
  3. 파싱을 진행하며 DOM 트리를 생성합니다.
  4. HTML파싱중, css파일 요청이 있을 경우 서버에 CSS 파일을 요청합니다
  5. 서버에서 CSS파일을 전송하고, 클라이언트는 이를 다시 파싱하여 CSSOM을 생성합니다

4. DOM 트리와 CSSOM을 결합하여(Attachment) 렌더 트리를 만듭니다.

만일 여기서. HTML 파싱중 script태그를 만날 경우 조금 달라집니다.

3. 파싱을 진행하며 DOM 트리를 생성합니다.

  • HTML파싱중, css파일 요청이 있을 경우 서버에 CSS 파일을 요청합니다
  • 서버에서 CSS파일을 전송하고, 클라이언트는 이를 다시 파싱하여 CSSOM을 생성합니다
  • script태그가 존재합니다. 서버에 js파일을 요청합니다.
  • 클라이언트는 서버로부터 js파일을 파싱합니다.
  • 자바스크립트 파일의 처리가 끝날때까지 렌더트리 생성이 중단됩니다.

그리고 요청한 자바스크립트 코드를 파싱하게 되는데,

이때 사용되는것은 렌더링 엔진이 아닌 자바스크립트 엔진입니다.

여기서 DOM이나 CSSOM을 조작하는 자바스크립트 코드가 있다면,

기존에 생성된 DOM이나 CSSOM을 변경합니다.

이렇게 변경된 부분들이 수정, 반영되어 최종적으로 렌더링 됩니다

4) 랜더 트리 배치(Layout)

랜더 트리 배치. 즉 레이아웃 단계에서는. 노드의 정확한 위치 및 크기를 계산합니다.

노드의 정확한 크기와 위치를 파악하기 위해 루트부터 노드를 순회하면서 계산하고, 레이아웃의 결과로

각 노드의 정확한 위치와 크기를 픽셀값으로 렌더트리에 적용합니다.

만약 CSS에서 크기를 %로 지정했을시, 레이아웃 단계를 거친 후, %값을 픽셀단위로 변화시킵니다.

픽셀 % 계산 전

픽셀 크기 계산 후

뷰포트(ViewPort) : 컨텐츠가 표시되는 브라우저의 영역, 크기를 말합니다. 모바일에서는 디스플레이, PC에서는 브라우저 창의 크기에 따라 틀려집니다. 화면에 표시되는 요소들의 크기 중 상대적으로 계산해서 그려내야하는 %, vh과 같은 태그들은 viewport의 크기가 달라질때마다 매번 계산을 다시하게됩니다.

5) 그리기(Paint)

Layout(렌더 트리 배치)계산이 끝난 후, 실제 화면을 그리게됩니다.

요소들의 위치, 크기, 스타일계산이 모두 끝난 렌더 트리를 사용해 실제 픽셀에 넣게됩니다.

5-1) 래스터화(Raterizing)

이렇게 문서의 각 스타일, 기하학적 속성, 어떤식으로 페인트 되어질지 알고 있기 때문에.

이 정보를 토대로 화면의 픽셀로 변화시키게 됩니다.

즉 텍스트, 요소의 색, 이미지, 그림자와같은 효과, 둥근 모서리 등 모든 것들이 보여지게 되는것이죠

이를 래스터화(Raterizing)이라 합니다.

초창기에는 화면에 보여지는 부분인 뷰포트부분만 래스터화 하는 방식이였으나

이젠 합성(Composite)이라는 과정으로 이루어집니다.

5-2) Composite(합성)

그리기(paint)단계에서는 여러 레이어Layer로 나누어 픽셀을 채워넣습니다.

이렇게 레이어를 분리해놓음으로, 특정요소가 수정되어서 다시 paint작업이 실행될때,

변경된 layer만 그려주게 되는 이점이 있습니다.

Layer 생성 조건 예:

  • position 관련 속성
  • overflow, alpha 값
  • 3D transform, animation
  • <canvas><video>
  • scrollbar가 존재하는 경우 별도의 layer 생성하여 처리
  • 같은 z-index의 레이어가 겹치는 경우 별도의 layer 생성하여 처리

따라서 합성Composite 단계에서는 여러개의 레이어로 나누어진 래스터화 된 픽셀들을

실제 사용자가 볼수있는 화면으로 합성해주는 단계입니다.


다시 한번 렌더링, Reflow와 Repaint

만일 클라이언트의 어떤 액션이나, 이벤트에 의해 DOM요소의 크기나 위치가 변경된다면,

그 영향을 받는 모든 요소의 노드들이 다시 Layout단계를 거쳐 화면에 그려지게 되는데,

이 노드 계산을 다시 하게되는것을 Reflow

다시 그려지게 되는것을 Repaint라 합니다.

Reflow(Layout)

너비, 높이등 요서에 대한 위치의 기하학적 변화가 있다면 Layout 단계가 다시 실행됩니다.

브라우저가 다른 모든 요소들을 확인하고, 영향을 받는 요소들을 다시 그립니다

Repaint

기하학적 변화가 아닌, 이미지, 색상과 같은 요소들이 변할경우, layout부분이 일어나지않고 paint 작업이 수행됩니다.

Composite

layout과 paint 둘다 거치지 않는 속성이라면 레이어의 변화만 주는 composite단계가 일어납니다.

이처럼 레이아웃(reflow)단계에서는 연산해야 하는 양이 많기 때문에 자주 일어날경우 렌더링 속도에 악영향을 줄수 있습니다.

===

참고자료

profile
뜨겁고 매콤하고 화끈하게

1개의 댓글

comment-user-thumbnail
2022년 2월 26일

잘 봤습니다.. ㅎㅎ

답글 달기