03. 브라우저는 어떻게 동작하는가?

jiseung·2022년 5월 9일
23

Frontend-Roadmap

목록 보기
3/3
post-thumbnail

브라우저는 널리 사용되는 소프트웨어다. 이 글을 통해 브라우저 주소 창에 google.com을 입력했을 때 브라우저가 어떻게 동작해서 구글 페이지가 화면에 보이게 되는지 알아보자.

왜 중요할까

빠른 사이트는 더 나은 사용자 경험을 제공한다. 사용자는 로딩 속도가 빠르고 상호 작용하기 쉬운 콘텐츠가 포함된 웹 경험을 원하고 기대한다.

웹 성능에 있어서 대기 시간브라우저가 단일 스레드라는 사실에 대해 이해하는 것이 중요하다.

  1. 대기 시간
  • 대기 시간은 빠른 로드를 보장하기 위해 극복해야 하는 것
  • 페이지 로드가 가능한 한 빨리 이루어지도록 해야 한다.
  1. 브라우저는 단일 스레드
  • 부드러운 스크롤에서 터치 반응에 이르기까지 사이트 상호 작용이 원활하게 이루어지도록 해야 한다.
  • 메인 스레드가 우리가 하는 모든 작업을 완료하고, 사용자 상호 작용을 처리하는 데 언제든 사용할 수 있도록 하는 렌더링 시간이 핵심
  • 브라우저의 단일 스레드 특성을 이해하고, 기본 스레드의 책임을 최소화하여 렌더링을 원활하게 하하고 상호 작용에 대한 즉각적인 응답을 보장해야 한다.

탐색

웹 페이지를 로드 첫 번째 단계

사용자가 주소 표시줄에 URL을 입력하고, 링크를 클릭하고, 양식을 제출하고, 기타 작업을 수행하여 페이지를 요청할 때마다 발생한다.

DNS 조회

웹 페이지 탐색의 첫 번째 단계는 해당 페이지의 자산이 있는 위치를 찾는 것

사이트를 방문한 적 없다면 브라우저는 DNS 조회를 요청한다.
1. 네임 서버에 도메인 입력
2. 네임 서버는 IP 주소로 응답

한 번의 요청 후 IP는 한동안 캐시될 가능성이 높으며,
다음 조회부터는 네임 서버에 연결하는 대신 캐시에서 IP 주소를 검색해 요청 속도를 높인다.

DNS 조회는 일반적으로 페이지 로드에 대해 각각의 호스트 이름당 한 번만 수행하면 된다.

TCP HandShake

IP 주소를 알게 되면 브라우저는 TCP 3-way handshake(SYN, SYN-ACK, ACK) 메커니즘을 통해 서버에 대한 연결을 설정한다. 이를 통해 브라우저와 웹 서버가 데이터 전송 전에 HTTPS를 통해 네트워크 TCP 소켓 연결의 매개변수를 협상할 수 있다.

TLS 협상

HTTPS를 통해 보안 연결을 설정하려면 또 다른 handshake가 필요하다.

이 handshake를 TLS 협상이라고 하며,
통신을 암호화하는데 사용할 암호를 결정하고 서버를 확인해서
실제 데이터 전송을 시작하기 전에 보안 연결이 설정되어 있는지 확인한다.

브라우저

웹 브라우저는 사용자가 그래픽 사용자 인터페이스를 통해 웹 페이지 또는 기타 온라인 콘텐츠에 액세스하고 표시할 수 있도록 하는 소프트웨어 응용 프로그램이다.

브라우저의 주요 기능

브라우저의 주요 기능은 사용자가 선택한 자원(HTML, PDF, 이미지 등)을 서버에 요청하고 브라우저에 표시하는 것이다.
자원의 주소는 URI(Uniform Resource Identifier)에 의해 정해진다.

브라우저는 HTML과 CSS 명세에 따라 HTML 파일을 해석해서 표시하는데 이 명세는 웹 표준화 기구(World Wide Web Consortium, W3C)에서 정한다.

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

    • URI를 입력할 수 있는 주소 표시줄 (주소 표시줄, 상태 표시줄, 도구 모음 등은 HTML5 명세의 필수 UI이다)
    • 이전 버튼다음 버튼
    • 북마크
    • 새로 고침 버튼과 현재 문서 로드를 중단하는 정지 버튼
    • 홈 버튼

브라우저의 기본 구조

브라우저의 주요 구성 요소는 다음과 같다.

  1. 사용자 인터페이스 : 사용자가 웹 페이지에서 사용할 수 있는 모든 시각적 요소와 상호 작용할 수 있도록 한다.
    • 요청한 페이지를 보여주는 창을 제외한 모든 부분
    • 주소표시줄, 이전/다음 버튼, 북마크 메뉴 등
  2. 브라우저 엔진 : 모든 웹 브라우저의 핵심 구성 요소
    • 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어
    • 사용자 인터페이스에서 받은 입력에 따라 렌더링 엔진을 쿼리하고 처리
  3. 렌더링 엔진 : 사용자가 요청한 특정 웹 페이지를 화면에 렌더링하는 역할
    • 요청한 콘텐츠를 표시한다 (HTML 요청 시 HTML과 CSS를 파싱해서 화면에 표시)
  4. 통신/네트워킹 : HTTP 요청과 같은 네트워크 호출에 사용. (플랫폼 독립적인 인터페이스)
    • HTTP 또는 FTP와 같은 표준 프로토콜을 사용하여 네트워크 호출을 관리
    • 인터넷 통신과 관련된 보안 문제를 처리
  5. UI 백엔드 : 기본 운영 체제의 사용자 인터페이스 방법을 사용
    • 플랫폼에 명시하지 않은 일반적인 인터페이스 (OS 사용자 인터페이스 체계 사용)
    • 콤보 박스와 창 같은 기본적인 장치를 그림.
  6. 자바스크립트 해석기 : 웹 사이트에 포함된 자바스크립트 코드를 구문 분석하고 실행
    • 해석된 결과가 생성되면 사용자 인터페이스에 표시하기 위해 렌더링 엔진으로 전달
  7. 자료 저장소 : 자료를 저장하는 계층
    • 쿠키를 저장하는 것과 같이 모든 종류의 자원을 하드 디스크(로컬)에 저장할 필요
    • HTML5 명세에 브라우저가 지원하는 웹 데이터 베이스가 정의되어 있다.

크롬은 대부분의 브라우저와 달리 각 탭마다 별도의 렌더링 엔진 인스턴스를 유지한다.
즉, 각 탭은 독립된 프로세스로 처리된다.

렌더링 엔진

사용자가 특정 문서를 요청하면 렌더링 엔진은 요청받은 HTML 및 XML 문서와 이미지 등을 브라우저 화면에 표시할 수 있다. 렌더링 엔진은 네트워킹 계층에서 해당 특정 문서의 콘텐츠를 8KB 청크로 수신하기 시작한다.
플러그인, 브라우저 확장 기능을 이용해 PDF 등도 표시할 수 있다.

렌더링 엔진들

파이어폭스 : 모질라게코(Gecko)엔진
인터넷 익스플로러: 트라이던트(Trident)엔진
사파리, 크롬(iOS) : 웹킷(Webkit)엔진 (최초 리눅스 플랫폼에서 동작하기 위해 제작된 오픈소스 엔진)
크롬, 오페라 : 블링크(blink)엔진

모든 브라우저에는 고유한 렌더링 엔진이 있다. 따라서 자연스럽게 모든 브라우저에는 사용자 화면에서 웹 페이지를 해석하는 고유한 방법이 있다. 여기에서 웹 개발자에게 웹 사이트의 브라우저 간 호환성과 관련하여 문제가 발생한다.

  • 크로스 브라우저 테스트
    여러 브라우저에서 기능 및 디자인 측면에서 웹 애플리케이션의 일관성을 확인하는 데 사용되는 품질 보증 방법

동작 과정

렌더링 엔진통신으로부터 요청한 문서의 내용을 얻는 것으로 시작한다.

TCP 느린시작 / 14kb
콘텐츠의 첫 번째 응답 패킷(청크)은 일반적으로 14kb의 데이터다.
네트워크 연결 속도의 균형을 맞추기 위해 느린 시작은 네트워크의 최대 대역폭을 결정할 수 있을 때까지 전송되는 데이터의 양을 점진적으로 증가시킨다.

혼잡 제어
하드웨어 및 네트워크 조건에 따라 연결 용량을 제한하고 패킷 및 ACK 흐름을 사용해 전송 속도를 결정한다.

다음은 렌더링 엔진의 기본적인 동작 과정이다.

  1. 요청된 HTML 페이지는 렌더링 엔진에 의해 외부 CSS 파일 및 스타일 요소를 포함한 청크로 구문 분석된다. 그런 다음 HTML 요소는 DOM 노드로 변환되어 콘텐츠 트리 또는 DOM 트리를 형성한다.

    • DOM 트리 구축을 위한 HTML 파싱
    • HTML 문서를 파싱
    • 콘텐츠 트리내부에서 태그DOM 노드로 변환
    • 외부 CSS 파일과 함께 스타일 요소도 파싱
    • 스타일 정보와 HTML 표시 규칙은 렌더 트리라고 부르는 또 다른 트리 생성
  2. 동시에 브라우저는 렌더 트리를 생성한다. 이 트리에는 스타일 정보와 요소가 표시되는 순서를 정의하는 시각적 지침이 모두 포함된다. 렌더 트리는 콘텐츠가 원하는 순서로 표시되도록 한다.

    • 렌더 트리 구축
    • 렌더 트리는 색상 또는 면적과 같은 시각적 속성이 있는 사각형을 포함하며 정해진 순서대로 화면에 표시된다.
  3. 렌더 트리는 레이아웃(배치) 프로세스를 거친다. 렌더 트리가 생성될 때 위치 또는 크기 값이 할당되지 않는다. 원하는 위치를 평가하기 위한 값을 계산하는 전 과정을 레이아웃(배치) 과정이라고 한다. 이 과정에서 모든 노드에 정확한 좌표가 할당된다. 이렇게 하면 모든 노드가 화면의 정확한 위치에 표시된다.

    • 렌더 트리 배치
    • 각 노드가 화면의 정확한 위치에 표시되는 것
  4. 마지막 단계는 화면을 그리는 것이다. 여기서 렌더 트리가 탐색되고 렌더러의 paint() 메서드가 호출되어 UI 백엔드 레이어를 사용하여 화면의 각 노드를 그린다.

    • 렌더 트리 그리기
    • UI 백엔드에서 렌더 트리의 각 노드를 가로지르며 형상을 만들어 내는 것

렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시한다.
그러기 위해 모든 HTML을 파싱할 때까지 기다리지 않고 배치와 그리기를 시작한다.
즉, 네트워크로부터 나머지 내용이 전부 전송되기를 기다리면서 받은 내용의 일부를 먼저 화면에 표시한다.

게코 vs 웹킷
과정게코웹킷
시각적으로 처리되는 트리형상 트리(frame tree, 각 요소는 frame)렌더 트리(render tree, 각 요소는 render object)
요소 배치리플로(reflow)배치(layout)
DOM 노드와 시각 정보 연결콘텐츠 싱크(content sink)어태치먼트(attachment)

파싱과 DOM 트리 구축

파싱

브라우저가 데이터의 첫 번째 청크를 수신하면 수신된 정보의 구문 분석을 시작할 수 있다.
구문 분석은 브라우저가 네트워크를 통해 수신한 데이터를 DOM 및 CSSOM으로 변환하는 단계이며, 이것은 렌더러가 페이지를 화면에 그리는 데 사용한다.

DOM : 브라우저에 대한 마크업의 내부 표현. DOM도 노출되며 JavaScript의 다양한 API를 통해 조작할 수 있다.

파싱 일반

  • 문서 파싱
    브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것
  • 파싱 결과
    문서 구조를 나타내는 노드 트리(파싱 트리, 문법 트리)

문법

  • 문맥 자유 문법
    파싱은 문서에 작성된 언어, 또는 형식의 규칙에 따른다. 그리고 파싱될 형식은 정해진 용어와 구문 규칙에 따라야 한다.

파싱-어휘 분석기 조합

  • 파싱어휘 분석구문 분석으로 구분할 수 있다.

    1. 어휘 분석 : 자료를 토큰(유효하게 구성된 단위의 집합체)으로 분해하는 과정
    2. 구문 분석 : 언어의 구문 규칙을 적용하는 과정
  • 파서가 하는 일은 두가지가 있다.

    1. 어휘 분석기(토큰 변환기) : 자료를 유효한 토큰으로 분해
      • 공백과 줄 바꿈 등 의미 없는 문자를 제거한다.
    2. 파서 : 언어 구문 규칙에 따라 문서 구조를 분석함으로써 파싱 트리 생성
  • 파싱 과정은 반복된다.

    • 파서는 어휘 분석기로부터 새 토큰을 받아 구문 규칙과 일치하는지 확인한다.
    • 규칙에 맞으면 토큰에 해당하는 노드가 파싱 트리에 추가되고 파서는 또 다른 토큰을 요청한다.
    • 규칙에 맞지 않으면 파서는 토큰을 내부적으로 저장하고 토큰과 일치하는 규칙이 발견될 때까지 요청한다.(발견되지 않는 경우 예외 처리 === 구문 오류)

변환

파서 트리는 최종 결과물이 아니다.
파싱은 보통 문서를 다른 양식으로 변환(컴파일)하는데,
컴파일러파싱 트리 생성 후 이를 기계 코드 문서로 변환한다.

파싱 예

예) 수학 표현식 2+3-1

어휘: 수학 언어는 정수, 더하기 기호, 빼기 기호를 포함한다.

구문
1. 언어 구문의 기본적인 요소는 표현식, 항, 연산자이다.
2. 언어에 포함되는 표현식의 수는 제한이 없다.
3. 표현식은 "항" 뒤에 "연산자" 그 뒤에 또 다른 항이 따르는 형태로 정의한다.
4. 연산자는 더하기 토큰 또는 빼기 토큰이다.
5. 정수 토큰 또는 하나의 표현식은 항이다.

2+3-1에서
2 : 5번째 규칙
2+3 : 5, 3번째 규칙
2+3-1 : 5, 3번째 규칙

어휘와 구문에 대한 정의

  1. 어휘
  • 보통 정규 표현식으로 표현한다.
    INTEGER : 0|[1-9][0-9]*  
    PLUS : +  
    MINUS : - 
  1. 구문
  • 보통 BNF(문맥 자유 문법을 나타내기 위해 만들어진 표기법)에 따라 정의한다.
    expression := term operation term  
    operation := PLUS | MINUS  
    term := INTEGER | expression  

파서의 종류

  1. 하향식 파서
  • 구문의 상위 구조(가장 높은 수준의 규칙)로부터 일치하는 부분을 찾기 시작
  1. 상향식 파서
  • 낮은 수준에서 높은 수준으로 규칙을 찾는다.
  • 부분적으로 일치하는 표현식을 파서 스택에 쌓는다.
  • 입력 값의 오른쪽으로 이동하기 때문에 이동-감소 파서라고 부른다.

파서 자동 생성

  • 파서 생성기

    • 파서를 생성해 줄 수 있는 도구
    • 언어에 어휘, 구문 규칙 등 문법을 부여하면 그에 동작하는 파서를 만들어 준다.
  • 웹킷의 파서

    • 플렉스(Flex) : 어휘 생성. 정규 표현식 정의를 포함하는 파일을 입력 받는다.
    • 바이슨(Bison) : 파서 생성. BNF 형식의 언어 구문 규칙을 입력 받는다.

HTML 파서

HTML 파서는 HTML 마크업을 파싱 트리로 변환한다.

HTML 문법 정의

HTML 어휘와 문법은 W3C에 의해 명세로 정리되어 있다.

HTML 파서는 문맥 자유 문법이 아니다

HTML 정의를 위한 공식적인 형식으로 DTD(문서 형식 정의)가 있지만, 이것은 문맥 자유 문법이 아니다.
HTML은 XML과 유사하지만, 보다 "유연"하고 "너그러운" 문법이기 때문이다.

HTML DTD

HTML 정의는 DTD 형식 안에 있는데 SGML 계열 언어의 정의를 이용한 것이다.
이 형식은 허용되는 모든 요소와 그 속성, 중첩 구조에 대한 정의를 포함한다.

DTD는 오래된 콘텐츠에 대한 하위 호환성을 위해 오래된 마크업을 지원한다.

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

파싱 트리는 DOM 요소와 속성 노드의 트리로서 출력 트리가 된다.

DOM

  • HTML 문서의 객체 표현
  • 외부로 향하는 자바스크립트와 같은 HTML 요소의 연결 지점
  • 트리의 최상위 객체는 문서
  • 마크업과 1:1 관계
  • W3C에 의해 명세 존재

트리가 DOM 노드를 포함한다 === DOM 접점의 하나를 실행하는 요소를 구성한다

파싱 알고리즘

HTML은 일반적인 하향식/상향식 파서로 파싱이 안된다. 이유는 다음과 같다.

  1. 언어의 너그러움
  2. HTML 오류에 대한 브라우저의 관용
  3. 변경에 의한 재파싱 가능
  • HTML에서 document.write을 포함하고 있는 스크립트 태그는 토큰을 추가할 수 있기 때문에 입력 과정에서 파싱이 수정된다.

그래서, 브라우저는 HTML 파싱을 위해 별도의 파서를 생성하는 단계를 진행한다.

  1. 토큰화 : 어휘 분석으로서 입력 값을 토큰으로 파싱한다.
    • HTML 토큰 : 시작 태그, 종료 태그, 속성 이름, 속성 값
    • 토큰을 인지해서 트리 생성자로 넘기고 다음 토큰을 확인하기 위해 다음 문자를 확인한다.
  2. 트리 구축

토큰화 알고리즘

알고리즘은 상태 기계다.
각 상태는 하나 이상의 연속된 문자를 입력 받아 이 문자에 따라 다음 상태를 갱신한다.

결과는 현재 토큰화 상태트리 구축 상태 영향을 받으므로, 같은 문자에 대해서도 결과가 다르게 나올 수 있음을 의미한다.

트리 구축 알고리즘

파서가 생성되면 문서 객체가 생성된다.
트리 구축이 진행되는 동안 문서 최상단에 DOM 트리가 수정되고 요소가 추가된다.

토큰화에 의해 생성된 각 노드는 트리 생성자에 의해 처리된다.

  • DOM 트리에 요소로 추가되거나
  • 스택(임시 버퍼 저장소)에 추가된다. (삽입 모드)

파싱이 끝난 이후의 동작

브라우저는 문서와 상호작용할 수 있게 된다.

문서 상태는 완료가 되고 로드 이벤트가 발생한다.

브라우저의 오류 처리

"Invalid Syntax" error : 브라우저가 모든 오류 구문을 교정하기 때문에 발생하는 에러

파서는 다음과 같은 오류들을 처리해야 한다.

  1. 어떤 태그의 안쪽에 추가하려는 태그가 금지된 것일 때 일단 허용된 태그를 먼저 닫고 금지된 태그는 외부에 추가한다.
  2. 파서가 직접 요소를 추가해서는 안된다. 문서 제작자에 의해 뒤늦게 요소가 추가될 수 있고 생략 가능한 경우도 있다. HTML, HEAD, BODY, TBODY, TR, TD, LI 태그가 이런 경우에 해당한다.
  3. 인라인 요소 안쪽에 블록 요소가 있는 경우 부모 블록 요소를 만날 때까지 모든 인라인 태그를 닫는다.
  4. 이런 방법이 도움이 되지 않으면 태그를 추가하거나 무시할 수 있는 상태가 될 때까지 요소를 닫는다.

<예시> 웹킷이 하는 일
1. <br></br>로 내부적으로 처리해 브라우저 간 호환성을 높인다.
2. A stray <table> : 테이블 중첩 관계가 잘못 되었을 때, 형제 관계로 만든다.
3. 중첩된 <form> : 안쪽의 <form>은 무시한다.
4. 태그의 중첩이 너무 깊을 때 : 최대 20개의 중첩만 허용하고 나머지는 무시한다.
5. 잘못 닫힌 <html>, <body> 태그 : <body>태그를 닫지 않고 종료를 위한 end()를 호출한다.

CSS 파싱

CSS는 문맥 자유 문법이다. 즉 파서 유형을 이용한 파싱이 가능하다.

웹킷 CSS 파서

웹킷은 CSS 파일로부터 자동으로 파서를 생성하기 위해 플렉스와 바이슨 파서 생성기(상향식 이동 감소 파서 생성)를 사용한다.

  • CSS 파일은 스타일 시트 객체로 파싱되고 각 객체는 CSS 규칙을 포함한다.
  • CSS 규칙 객체는 선택자, 선언 객체, CSS 문법과 일치하는 다른 객체를 포함한다.

스크립트와 스타일 시트의 진행 순서

스크립트

웹은 파싱과 동시에 수행되는 동기화 모델이다.

  • 스크립트가 실행되는 동안 문서 파싱은 중단된다.

  • 스크립트를 지연(defer)으로 표시할 수 있는데, 이 경우 문서 파싱은 중단되지 않고 문서 파싱 완료 후 스크립트가 실행된다.

  • HTML5는 스크립트를 비동기로 처리하는 속성을 추가했기 때문에 별도의 맥락에서 파싱되고 실행된다.

예측 파싱

웹킷과 파이어폭스는 예측 파싱과 같은 최적화를 지원한다.

  • 스크립트 실행 중 다른 스레드는 문서의 나머지를 파싱한다.
    - 자원을 병렬로 연결해 받고, 속도를 개선한다.
  • DOM 트리를 수정하지 않고 메인 파서의 일로 넘긴다.
    - 외부 스크립트/스타일 시트/이미지 등 외부 자원만을 파싱한다.

스타일 시트

스타일 시트는 DOM 트리를 변경하지 않으므로 문서 파싱을 기다리거나 중단할 필요가 없다.

스크립트가 문서 파싱 중 스타일 정보를 요청하는 경우?

  • 파이어폭스 : 아직 로드/파싱 중인 스타일 시트가 있으면 스크립트 실행 중단
  • 웹킷 : 로드 중인 스타일 시트 중 문제되는 속성이 있을 때만 스크립트 실행 중단

렌더 트리 구축

DOM 트리가 구축되는 동안 브라우저는 렌더 트리를 구축한다.
목적 : 표시해야 할 순서와 문서의 시각적인 구성 요소(형상, 렌더러, 렌더 객체)로써 올바른 순서로 내용을 그려내기 위함

렌더러는 자신과 자식 요소를 어떻게 배치하고 그려야 하는지 알고 있다.

DOM 트리와 렌더 트리의 관계

렌더러는 DOM 요소에 부합하지만 1:1 대응 관계는 아니다.

  • 비시각적 DOM 요소 존재
  • 여러 개의 시각 객체와 대응하는 DOM 요소
  • DOM 노드에 대응하지만 트리의 다른 곳에 배치

트리를 구축하는 과정

  • 파이어폭스의 프레젠테이션

    • DOM 업데이트를 위한 리스너
  • 웹킷의 어태치먼트

    • 스타일을 결정하고 렌더러를 만드는 과정
    • 모든 DOM 노드에는 attach 메서드가 있고, DOM 트리에 노드를 추가하면 새 노드의 attach 메서드를 호출한다.
  • html태그와 body태그를 처리함으로써 렌더 트리 루트 구성

    • 루트 렌더 객체는 CSS 명세에서 포함 블록(최상위 블록)과 일치
      • 파이어 폭스의 ViewPortFrame
      • 웹킷의 RenderView
    • 이것이 문서가 가리키는 렌더 객체이며, 트리의 나머지 부분은 DOM 노드를 추가함으로써 구축된다.

스타일 계산

렌더 트리를 구축하려면 각 렌더 객체의 시각적 속성에 대한 계산이 필요하다.

스타일 계산의 어려움
1. 수 많은 스타일 속성에 대한 메모리 문제
2. 최적화되어 있지 않다면 각 요소에 할당된 규칙을 찾는 것은 성능 문제를 야기
3. 규칙을 적용하는 것은 계층 구조를 파악해야 하는 복잡한 규칙을 수반

브라우저가 스타일 문제를 어떻게 처리할까?

  1. 스타일 정보 공유
  • 웹킷 노드는 스타일 객체(RenderStyle)를 참조한다. 스타일 객체는 일정 조건 아래 공유할 수 있다.
    • 동일한 마우스 반응 상태를 가진 요소
    • 아이디가 없는 요소
    • 태그 이름 일치
    • 클래스 속성 일치
    • 지정된 속성 일치
    • 링크 상태 일치
    • focus 상태 일치
    • 속성 선택자 영향을 받는 요소가 문서 전체에 없어야 한다.
    • 요소에 인라인 스타일 속성이 없어야 한다.
    • 문서 전체에서 형제 선택자를 사용하지 않아야 한다.
  1. 파이어폭스 규칙 트리
    파이어 폭스는 스타일 계산을 위해 규칙 트리스타일 문맥 트리를 가지고 있다.
    (웹킷은 오직 DOM 노드로 관련 스타일을 처리한다.)
  • 스타일 문맥

    • 최종 값 저장
    • 올바른 순서 안에 부합하는 규칙을 적용하고 논리로부터 구체적인 값을 변환함으로써 계산
    • 부합하는 모든 규칙을 트리에 저장하는데 경로의 하위 노드가 높은 우선순위
    • 규칙 저장은 느리게 처리
    • 계산된 경로를 트리에 추가
  • 트리가 작업량을 줄이는 방법

  1. 구조체로 분리
    스타일 문맥은 구조체로 나뉘는데,
    트리는 최종적으로 계산된 값을 포함하여 전체 구조체를 저장한다.
  2. 규칙 트리를 사용하여 스타일 문맥 계산
    규칙 트리의 경로를 계산하거나 이미 존재하는 경로를 사용한다.
    그 다음 경로 안에서 규칙을 적용한다.
    이것은 최종 값과 메모리 계산을 절약한다.
    구조체에서 선언을 발견할 수 없는 경우, 구조체는 "상속 타입"
    문맥 트리에서 부모 구조체를 향하면서 성공적으로 구조체를 공유한다.
    재설정 구조체라면 기본 값들이 사용될 것이다.
  • 가장 구체적인 노드에 값을 추가하면 실제 값으로 변환하기 위해 추가적인 계산이 필요하다.

  • 트리 노드에서 결과를 저장하므로 자식에게도 사용할 수 있다.

  • 같은 트리 노드를 가리키는 형제 요소가 있는 경우 전체 스타일 문맥이 공유된다.

  • 규칙 트리

    • 올바른 순서에 따라 속성을 적용하는 것을 돕는다.
  1. 쉬운 선택을 위한 규칙 다루기
  • 스타일 규칙을 위한 방법

    • CSS 규칙을 외부 스타일 시트에서 선언하거나 style 요소에서 선언
    • 인라인 스타일 속성
    • HTML의 시각적 속성(CSS 규칙으로 변환)

    스타일 시트를 파싱한 후 규칙선택자에 따라 여러 해시맵 중 하나에 추가된다.
    아이디, 클래스 이름, 태그 이름을 사용한 맵이 있고, 일반적인 맵이 있다.
    이러한 최적화는 찾아야할 규칙의 95% 이상을 제거한다.

  1. 계단식 순서에 따라 규칙 적용하기

    스타일 객체는 모든 CSS 속성을 포함하고 있는데, 어떤 규칙과도 일치하지 않는 일부 속성은 부모 요소의 스타일 객체로부터 상속받는다. 그 외 다른 속성들은 기본 값으로 설정된다.

문제는 하나 이상의 속성이 정의될 때 발생된다.

  • 스타일시트 Cascade
    스타일 속성 선언이 여러 스타일 시트에서 나타날 수 있고,
    하나의 스타일 시트 안에서도 여러 번 나타날 수 있다.
    ➡️ 규칙을 적용하는 순서가 매우 중요하다!
  1. 브라우저 선언 (browser declarations)
    • 중요도가 가장 낮다.
  2. 사용자 일반 선언 (user normal declarations)
    • 저작자의 선언을 덮어쓸 수 있는 것은 !important 뿐이다.
  3. 저작자 일반 선언 (author normal declarations)
    • HTML 시각 속성은 CSS 속성 선언으로 변환되며 저작자 일반 선언 규칙으로 간주된다.
  4. 저작자 중요 선언 (author important declarations)
  5. 사용자 중요 선언 (user important declarations)
  • 특정성(specificity)

    • 선택자 특정성

      • 선택자 없이 'style' 속성이 선언된 것이면 1을 센다. 그렇지 않으면 0을 센다. (=a)

      • 선택자에 포함된 아이디 선택자 개수를 센다. (=b)

      • 선택자에 포함된 속성 선택자(클래스 선택자와 속성 선택자)와 가상 클래스 선택자의 숫자를 센다. (=c)

      • 선택자에 포함된 요소 선택자와 가상 요소 선택자의 숫자를 센다. (=d)

        특정성의 값 = a-b-c-d 연결. 가장 높은 숫자에 의해 사용할 진법을 정한다.

      • 규칙 정렬
        맞는 규칙을 찾으면 cascade 규칙에 따라 정렬된다.
        웹킷은 목록이 적으면 버블 정렬, 많으면 병합정렬을 사용하며
        규칙에 ">" 연산자를 덮어쓰는 방식으로 정렬을 실행한다.

      • 점진적 처리
        웹킷은 @import를 포함한 최상위 수준의 스타일 시트가 로드되었는지 표시하기 위해 플래그를 사용한다.
        DOM 노드와 시각정보를 연결하는 과정에서 스타일이 완전히 로드되지 않았다면 문서에 자리 표시자를 사용하고,
        스타일 시트가 로드됐을 때 다시 계산한다.

배치

렌더러가 생성되어 트리에 추가될 때 크기와 위치 정보는 없는데 이런 값을 계산하는 것을 배치 또는 리플로라고 부른다.

HTML흐름 기반의 배치 모델을 사용하는데 이것은 보통 단일 경로를 통해 크기와 위치 정보를 계산할 수 있음을 의미한다. (왼쪽~오른쪽, 위~아래)

배치는 반복되며 HTML 문서의 <html>요소에 해당하는 최상위 렌더러에서 시작된다.
배치는 프레임 계층의 일부 또는 전부를 통해 반복되고 각 렌더러에 필요한 크기와 위치 정보를 계산한다.

모든 렌더러는 "배치" 또는 "리플로" 메서드를 갖는데, 각 렌더러는 배치해야 할 자식의 배치 메소드를 불러온다.

Dirty bit system

전체를 다시 배치하지 않기 위해 브라우저는 dirty bit를 사용한다. 렌더러는 다시 배치할 필요가 있는 변경 요소 또는 추가된 것과 그 자식 요소를 dirty라고 표현한다.

dirtychildren are dirty 플래그가 존재하는데, children are dirty플래그는 자신을 제외하고 자식 가운데 적어도 하나를 다시 배치할 필요가 있다는 의미이다.

전역 배치와 점증 배치

배치는 렌더러 트리 전체에서 일어날 수 있는데, 이것을 전역 배치라 하고, 다음과 같은 경우에 발생한다.
1. 글꼴 크기 변경과 같이 모든 렌더러에 영향을 주는 전역 스타일 변경
2. 화면 크기 변경에 의한 결과

배치는 dirty 렌더러가 배치되는 경우에만 점증되는데, 추가적인 배치가 필요하므로 약간의 손실이 발생할 수 있다.

점증 배치는 렌더러가 dirty일 때 비동기적으로 일어난다.
예를 들어 네트워크로부터 추가 내용을 받아서 DOM 트리에 더해진 다음 새로운 렌더러가 렌더 트리에 붙을 때이다.

비동기 배치와 동기 배치

점증 배치는 비동기로 실행된다.
파이어폭스 : 리플로 명령을 쌓아놓고 스케줄러는 이 명령을 한꺼번에 실행
웹킷 : 점증 배치를 실행하는 타이머. 트리를 탐색해 dirty 렌더러를 배치

  • offsetHeight 같은 스타일 정보를 요청하는 스크립트는 동기적으로 점증 배치 실행
  • 전역 배치는 보통 동기적으로 실행
  • 때때로 배치는 스크롤 위치 변화 같은 속성들 때문에 초기 배치 이후 콜백으로 실행된다.

최적화

배치가 크기 변경이나 렌더러 위치 변화 때문에 실행되는 경우 렌더러의 크기는 캐시로부터 가져온다.

배치 과정

배치는 보통 다음과 같은 형태로 진행된다.
1. 부모 렌더러가 자신의 너비를 결정
2. 부모가 자식을 확인 (자식 렌더러를 배치, 필요하다면 자식 배치를 호출해 자식의 높이 계산)
3. 부모는 자식의 누적 높이, 여백, 패딩을 사용해 자신의 높이 설정
4. dirty bit 플래그 제거

파이어폭스는 상태 객체를 배치(리플로)를 위한 매개 변수로 사용하는데, 상태는 부모 너비를 포함한다.
파이어폭스의 리플로 결과는 매트릭스 객체이며 높이가 계산된 렌더러를 포함한다.

너비 계산

렌더러의 너비는 포함하는 블록의 너비, 렌더러의 너비, 여백, 테두리를 이용해 계산된다.

배치할 필요가 있지만 너비가 고정된 경우 값은 캐시에 저장된다.

줄 바꿈

렌더러가 배치되는 동안 줄을 바꿀 필요가 있을 때 배치는 중단되고 줄 바꿀 필요가 있음을 부모에 전달한다.
부모는 추가 렌더러를 생성하고 배치를 호출한다.

그리기

화면에 내용을 표시하기 위한 렌더 트리가 탐색되고 렌더러의 paint메서드가 호출된다.
그리기는 UI 기반의 구성 요소를 사용한다.

전역과 점증

배치와 마찬가지로 전역 또는 점증 방식으로 그리기 수행된다.

점증 그리기에서 일부 렌더러는 전체 트리에 영향을 주지 않는 방식으로 변경된다.

변경된 렌더러는 화면 위의 사각형을 무효화하는데 OS는 이것을 dirty 영역으로 보고 paint이벤트를 발생시킨다. OS는 몇 개의 영역을 하나로 합치는 방법으로 효과적으로 처리하며, 크롬은 OS의 동작을 어느 정도 모방한다.

프레젠테이션은 이러한 이벤트를 듣고 렌더 최상위로 메시지를 전달하며,
트리는 적절한 렌더러에 이를 때까지 탐색되고 그려진다.

그리기 순서

요소가 stacking contexts에 쌓이는 순서와 같다.
블록 렌더러가 쌓이는 순서는 다음과 같다.

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

파이어폭스 표시 목록

파이어폭스는 렌더 트리를 검토하고 그려진 사각형을 위한 표시 목록을 구성한다. 이러한 방법으로 트리는 여러 번 리페인팅하는 대신 한 번의 탐색으로 그려낸다.

파이어폭스는 다른 불투명 요소 뒤에 완전히 가려진 요소는 추가하지 않는 방법으로 최적화를 진행한다.

웹킷 사각형 저장소

리페인팅 전에 웹킷은 기존의 사각형을 비트맵으로 저장해 새로운 사각형과 비교하고 차이가 있는 부분만 다시 그린다.

동적 변경

브라우저는 변경에 대해 최소한의 동작으로 반응하려고 노력한다.

  • 요소의 색깔이 바뀌면 해당 요소의 리페인팅만 발생
  • 요소의 위치가 바뀌면 요소와 자식, 형제의 리페인팅재배치 발생
  • DOM 노드를 추가하면 노드의 리페인팅재배치 발생
  • html요소의 글꼴 변경 등에서 캐시를 무효화하고 트리 전체 배치, 리페인팅 발생

렌더링 엔진의 스레드

렌더링 엔진은 통신을 제외한 거의 모든 경우 단일 스레드로 동작한다.
파이어폭스, 사파리 : 렌더링 엔진의 스레드는 브라우저의 주요한 스레드
크롬 : 탭 프로세스의 주요 스레드

이벤트 순환

브라우저의 주요 스레드는 이벤트 순환으로 처리 과정을 유지하기 위해 무한 순환된다.
배치그리기 같은 이벤트를 위해 대기하고 이벤트를 처리한다.

CSS2 시각 모델

캔버스

서식 구조가 표현되는 공간. 브라우저가 내용을 그리는 공간
기본적으로 투명해서 겹치는 경우 비쳐 보이고, 투명하지 않을 경우 정의한 색이 지정된다.

CSS 박스 모델

문서 트리에 있는 요소를 위해 생성되고 시각적 서식 모델에 따라 배치된 사각형 박스를 설명한다.

각 박스는 콘텐츠 영역과 패딩(Optional), 테두리, 여백이 있다.
박스의 유형은 display 속성(block, inline, none)에 의해 결정된다.

위치 결정 방법

위치를 결정하는 방법은 다음과 같은 세 가지이다.
1. Normal : 렌더 트리에서 객체의 자리가 DOM 트리의 자리와 같고 박스 유형, 면적에 따라 배치된다.
2. Float : 객체는 일반적인 흐름에 따라 배치된 다음 왼쪽이나 오른쪽으로 흘러 이동한다.
3. Absolute : 객체는 DOM 트리 자리와는 다른 렌더 트리에 놓인다

위치는 position 속성과 float 속성에 의해 결정된다.

  • static(기본 값)과 relative로 설정하면 일반적인 흐름에 따라 위치가 결정된다.
  • absolutefixed로 설정하면 절대적인 위치가 된다.

박스 유형

블록 박스: 브라우저 창에서 사각형 블록을 형성한다.
인라인 박스: 블록이 되지 않고 블록 내부에 포함된다.

블록은 다른 블록 아래 수직으로 배치되고 인라인은 수평으로 배치된다.

인라인 박스는 라인 또는 라인 박스 안쪽에 놓이며 라인은 적어도 가장 큰 박스만큼 크지만 baseline 정렬에서 더 커질 수 있다.

위치 잡기

relative

일반적인 흐름에 따라 위치를 결정한 만큼 필요한 만큼 이동

float

라인의 왼쪽 또는 오른쪽으로 이동

absolute, fixed

일반적인 흐름과 무관, 관여하지 않으며 면적은 상대적이다.

Layer

CSS의 z-index속성에 의해 명시된다.
stacking contexts으로 구분되며 뒤쪽 요소가 먼저 그려지고 앞쪽 요소는 사용자에게 가까운 쪽으로 나중에 그려진다. 앞쪽 요소는 겹치는 이전 요소를 가린다.

스택은 z-index속성에 따라 순서를 결정하며 z-index 속성이 있는 박스는 지역 스택을 형성한다.
뷰포트는 바깥쪽의 스택이다.

참고 자료

How Browsers Work: Behind the scenes of modern web browsers
브라우저는 어떻게 동작하는가?
Understanding the Role of Rendering Engine in Browsers
Populating the page: how browsers work

profile
Frontend Web Developer

3개의 댓글

comment-user-thumbnail
2022년 5월 9일

조만간 책 나오면,, 싸인 부탁드릴게요!

1개의 답글
comment-user-thumbnail
3일 전

이 글을 읽고 크롬이 더 빨라졌습니다.

답글 달기