브라우저의 주요 기능
브라우저의 주요 기능은 사용자가 선택한 자원을 서버에 요청하고 브라우저에 표시하는 것이다. 자원은 보통 HTML 문서지만 PDF 나 이미지 또는 다른 형태일 수 있다. 자원의 주소는 URI (Uniform Resource Identifer) 에 의해 정해진다.
브라우저는 HTML 과 CSS 명세에 따라 HTML 파일을 해석해서 표시하는 데 이 명세는 웹 표준화 기구인 W3C (World Wide Web Consortium) 에서 정한다.
브라우저의 사용자 인터페이스는 서로 닮아 있는데 다음과 같은 요소들이 일반적이다.
- URI를 입력할 수 있는 주소 표시 줄
- 이전 버튼과 다음 버튼
- 북마크
- 새로 고침 버튼과 현재 문서의 로드를 중단할 수 있는 정지 버튼
- 홈 버튼
브라우저의 기본 구조
브라우저의 기본 구조는 다음과 같다.
- 사용자 인터페이스 : 사용자가 접근할 수 있는 영역. URI를 입력할 수 있는 주소 표시줄, 이전/다음 버튼, 북마크 메뉴, 새로 고침 버튼과 현재 문서의 로드를 중단할 수 있는 정지 버튼 , 홈 버튼 등 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분.
- 브라우저 엔진 : 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어한다. data storage 를 참조하며 로컬에 데이터를 쓰고 읽으면서 다양한 작업을 한다.
- 렌더링 엔진 : 웹 서버로부터 응답 받은 자원을 웹 브라우저상에 나타낸다. 예를 들어 HTML 문서를 응답받으면 HTML 과 CSS 를 파싱하여 화면에 표시한다. 렌더링 엔진의 HTML 파서와 CSS 파서에 의해 파싱되어 DOM, CSSOM 트리로 변환되고 렌더 트리로 결합한다. 이렇게 생성된 렌더 트리를 기반으로 브라우저는 웹 페이지를 나타낸다.
- 통신 : HTTP 요청과 같은, 서버와 통신이 가능하게 하는 네트워크 호출에 사용된다.
- UI 백엔드 : select, input 등 기본적인 위젯을 그리는 인터페이스이다.
- 자바스크립트 해석기 : 자바스크립트 코드를 해석하고 실행한다.
- 자료 저장소 : cookie, loscal storage, indexed DB 등 브라우저 메모리를 활용하여 자료를 저장하는 영역이다.
렌더링 엔진
렌더링 엔진은 HTML, XML, 이미지 등 요청받은 내용을 브라우저 화면에 표시하는 엔진이다. 각 브라우저마다 렌더링 엔진이 다르기 때문에 같은 페이지가 다르게 보일 수 있다.
-moz-border-radius: 1em; // 파이어폭스 브라우저에 적용
-ms-border-radius: 2em; // 익스플로어에 적용, 보통 생략
-o-border-radius: 3em; // 오페라에 적용
-webkit-border-radius: 4em; // 구글, 사파리 브라우저에 적용
렌더링 엔진의 종류는 아래와 같다.
- Blink - 크롬, 오페라
- Webkit - 사파리
- Trident - 익스플로어
- EdgeHTML - 마이크로소프트 엣지
렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시한다. 그래서 일련의 과정들이 동기적으로 진행되지 않는다. HTML을 파싱 할 때까지 기다리지 않고 렌더 트리 배치와 그리기 과정을 시작한다.
렌더링 엔진 동작 과정
렌더링 엔진은 통신으로부터 요청한 문서의 내용을 얻는 것으로 시작하는데 문서의 내용은 보통 8KB 단위로 전송된다. 다음은 렌더링 엔진의 기본적인 동작 과정이다.
1. 렌더링 엔진은 HTML 문서를 파싱 하여 DOM 트리를 구축한다.
<html>
<body>
<p>Hello World</p>
<div><img src="example.png" /></div>
</body>
</html>
DOM 은 마크업과 1:1 관계를 성립한다. 위와 같은 코드는 아래와 같은 DOM 트리로 변환할 수 있다.
브라우저는 서버로부터 HTML 문서를 모두 전달받고 HTML 파서를 통하여 파싱(parsing)하고 파싱 트리를 생성한다. 이 생성된 파싱 트리를 기반으로 DOM 트리를 생성한다.
2. 그다음 외부 CSS 파일과 함께 포함된 스타일 요소를 파싱한다.
CSS 파일은 스타일 시트 객체로 파싱되고, 각 객체는 CSS 규칙을 포함한다. CSS 규칙 객체 (CSSOM, Css Object Model) 는 선택자와 선언 객체 그리고 CSS 문법과 일치하는 다른 객체를 포함한다.
3. DOM 트리와 ②의 결과물을 합쳐 (DOM + CSSOM) 렌더 트리를 구축한다.
DOM 트리가 구축되는 동안 브라우저는 DOM 트리를 기반으로 렌더 트리를 생성한다. 렌더 트리는 문서를 시각적인 요소로 만들어주는 역할을 한다. 웹킷은 이 구성 요소를 "렌더러(rendere)" 또는 "렌더 객체(render object)"라는 용어를 사용한다. 렌더러는 자신과 자식 요소를 어떻게 배치하고 그려내야 하는지 알고 있다.
렌더러는 DOM 요소에 부합하지만 1:1로 대응하는 관계는 아니다. 그 이유는 <head>, display:'none'와 같은 사용자가 볼 수 없는 DOM 요소는 렌더 트리에 추가되지 않는다. (visibility 속성에 "hidden" 값이 할당된 요소는 트리에 나타난다.)
4. 렌더 트리 각 노드에 대해 화면 상에서 배치할 곳을 결정한다. (레이아웃)
렌더 트리는 위치와 크기를 가지고 있지 않기 때문에, 어느 공간에 위치해야 할지 각 객체들에게 위치(position)와 크기(size)를 결정해줍니다.
5. UI 백엔드에서 렌더 트리의 각 노드를 그린다.
렌더 트리가 만들어져 레이아웃이 구성되었으면 UI 백엔드가 동작하여 렌더 트리의 각 객체를 화면의 픽셀(px) 값으로 나타낸다.
파싱과 DOM 트리 구축
파싱 일반
문서 파싱은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미한다. 변환된 결과를 parse tree, syntax tree 라고 한다. 파싱은 어휘 분석과 구문 분석 두 가지로 구분한다.
- 어휘 분석은 자료를 토큰으로 분해하는 과정이다.
- 구문 분석은 언어의 구문 규칙을 적용하는 과정이다.
문서 소스를 받으면 파서는 어휘 분석을 통해 공백과 줄 바꿈과 같은 의미 없는 문자를 제거하고 토큰으로 분해한다. 그 후 구문 분석으로 파싱 트리를 생성한다.
보통 어휘 분석기(어휘 분석 하는 파서)로 부터 토큰을 받아서 구문 규칙과 일치하는지 확인하여 맞으면 토큰에 해당하는 노드가 파싱 트리에 추가되고 또 다른 토큰을 요청한다.
일치하지 않으면 토큰을 내부적으로 저장하고 토큰과 일치하는 규칙이 발견될 때까지 요청하고, 맞는 규칙이 없는 경우 예외로 처리한다. (문서가 구문 오류를 포함하고 있다는 것)
변환
파서 트리는 최종 결과물이 아니다. 파싱은 보통 문서를 다른 양식으로 변환하는데 컴파일이 하나의 예가 된다. 소스 코드를 기계 코드로 만드는 컴파일러는 파싱 트리 생성 후 이를 기계 코드 문서로 변환한다.
HTML 파서
HTML 파서는 HTML 마크업을 파싱 트리로 변환한다. HTML은 파서가 요구하는 문맥 자유 문법에 의해 쉽게 정의할 수 없다. 그러나 파싱은 CSS와 자바스크립트를 파싱하는데 사용된다.
HTML은 암묵적으로 태그에 대한 생략이 가능하다. (시작, 종료 태그 생략 등..) 이러한 방식은 웹 제작을 편하게 만들어주었지만 공식적인 문법으로 작성하기 어렵게 만드는 문제가 있다. 따라서 HTML은 파싱하기 어렵고 전통적인 구문 분석이 불가능해서 문맥 자유 문법이 아니다.
스크립트와 스타일 시트의 진행 순서
스크립트
웹은 파싱과 실행이 동시에 수행되는 동기화 모델이다. 파서가 <script> 태그를 만나면 실행을 하고, 스크립트가 실행되는 동안 문서의 파싱은 중단된다.
제작자가 스크립트를 "지연(defer)으로 표시하면 문서 파싱은 중단되지 않고 문서 파싱이 완료된 이후에 스크립트가 실행된다. HTML5는 스크립트를 비동기(asynchronous)로 처리하는 속성을 추가했기 때문에 별도의 맥락에 의해 파싱되고 실행된다.
예측 파싱
웹킷이나 파이어폭스는 예측 파싱과 같은 최적화를 지원한다. 스크립트를 실행하는 동안 다른 스레드는 네트워크로부터 다른 자원을 찾아 내려받고 문서의 나머지 부분을 파싱한다. 이런 방법은 자원을 병렬로 연결하여 받을 수 있고 전체적인 속도를 개선한다. 참고로 예측 파서는 DOM 트리를 수정하지 않고 메인 파서의 일로 넘긴다. 예측 파서는 외부 스크립트, 외부 스타일 시트와 외부 이미지와 같이 참조된 외부 자원을 파싱할 뿐이다.
스타일 시트
한편 스타일 시트는 다른 모델을 사용한다. 이론적으로 스타일 시트는 DOM 트리를 변경하지 않기 때문에 문서 파싱을 기다리거나 중단할 이유가 없다. 그러나 스크립트가 문서를 파싱하는 동안 스타일 정보를 요청하는 경우라면 문제가 된다. 스타일이 파싱되지 않은 상태라면 스크립트는 잘못된 결과를 내놓기 때문에 많은 문제를 야기한다. 이런 문제는 흔치 않은 것처럼 보이지만 매우 빈번하게 발생한다. 파이어폭스는 아직 로드 중이거나 파싱 중인 스타일 시트가 있는 경우 모든 스크립트의 실행을 중단한다. 한편 웹킷은 로드되지 않은 스타일 시트 가운데 문제가 될만한 속성이 있을 때에만 스크립트를 중단한다.
렌더 트리 구축
DOM 트리가 구축되는 동안 브라우저는 렌더 트리를 구축한다. 표시해야할 순서와 문서의 시각적인 구성 요소로써 올바른 순서로 내용을 그려내기 위한 목적이 있다. 각 렌더러는 CSS 박스에 부합하는 사각형을 표시하는데 렌더러는 너비, 높이, 위치와 같은 기하학적 정보를 포함한다.
DOM 트리와 렌더 트리의 관계
렌더러는 DOM 요소에 부합하지만 1:1 대응은 아니다. 예를 들어 head 요소와 같은 비시각적 DOM 요소는 렌더 트리에 추가되지 않고, display 속성에 none값이 할당 된 요소도 트리에 나타나지 않는다.
하나의 사각형으로 묘사할 수 없는 복잡한 구조는 여러 개의 시각 객체와 DOM 요소가 대응한다.
트리를 구축하는 과정
파이어폭스에서 프레젠테이션은 DOM 업데이트를 위한 리스너로 등록된다.
웹킷에서는 모든 DOM 노드에 attach 매서드가 있다. DOM 트리에 노드를 추가하면 새 노드의 attach 메소드를 호출하고, attach 메소드는 스타일을 결정하고 렌더러를 만든다. 그리고 html 태그와 body 태그를 처리함으로써 렌더 트리 루트를 구성하는데 트리의 나머지 부분은 DOM 노드를 추가함으로써 구축된다.
스타일 계산
렌더 트리를 구축하려면 각 렌더 객체의 시각적 속성에 대한 계산이 필요하다. 스타일은 인라인 스타일 요소와 HTML의 시각적 속성과 같은 다양한 형태의 스타일 시트를 포함한다.
HTML의 시각적 속성은 대응하는 CSS 스타일 속성으로 변환된다.
스타일 계산을 할 때의 어려움은 다음과 같다.
- 수 많은 스타일 속성들을 수용하면서 메모리 문제 야기
- 최적화되어 있지 않다면 각 요소에 할당된 규칙을 찾는 것은 성능 문제를 야기
- 규칙을 적용하는 것은 계층 구조를 파악해야 하는 다단게 규칙 수반
이를 해결하기 위해 웹킷 노드는 스타일 객체를 참조하는데 스타일 객체는 일정 조건 아래 공유할 수 있다. 파이어폭스는 스타일 계산을 쉽게 처리하기 위해 규칙 트리와 스타일 문맥 트리라고 하는 두 개의 트리를 더 가지고 있다.
배치
렌더러가 생성되어 트리에 추가될 때 크기의 위치 정보는 없는데 이런 값을 계산하는 것을 배치 또는 리플로라고 부른다.
HTML은 흐름 기반의 배치 모델 사용하는데 이것은 보통 단일 경로를 통해 크기와 위치 정보를 계산할 수 있다는 것을 의미한다. 이런 흐름속에서 나중에 등장하는 요소는 앞서 등장한 요소의 위치와 크기에 영향을 미치지 않기에 배치는 왼쪽에서 오른쪽 혹은 위에서 아래로 흐른다. 단, 표는 하나 이상의 경로를 필요로 하기 때문에 예외가 된다.
배치는 <html> 요소에 해당하는 최상위 렌더러에서 시작한다. 배치는 프레임 계층의 일부 또는 전부를 통해 반복되고 각 렌더러에 필요한 크기와 위치 정보를 계산한다. 최상위 렌더러의 위치는 0,0 이고 브라우저 창의 보이는 영역에 해당하는 뷰포트 만큼의 면적을 갖는다.
모든 렌더러는 "배치" 또는 "리플로" 메서드를 갖는데 각 렌더러는 배치해야 할 자식의 배치 메소드를 불러온다.
더티 비트 체제
작은 변경 때문에 전체를 다시 배치하지 않기 위해 브라우저는 더티 비트 체제를 사용한다. 렌더러는 다시 배치할 필요가 있는 변경요소와 그 자식을 '더티' 라고 표시한다.
'더티'와 '자식이 더티' 이렇게 두 가지 플래그가 있다. 자식이 더티하다는 것은 본인은 괜찮지만 자식 가운데 적어도 하나를 다시 배치할 필요가 있다는 의미이다.
전역 배치와 점증 배치
배치는 렌더러 트리 전체에서 일어날 수 있는데 이것을 '전역' 배치라고 하고 다음과 같은 경우에 발생한다.
- 글꼴 크기 변경과 같이 모든 렌더러에 영향을 주는 전역 스타일 변경
- 화면 크기 변경에 의한 결과
더티 렌더러가 배치되는 경우에만 비동기적으로 점증 배치가 된다. (ex. 네트워크로부터 추가 내용을 받아 DOM 트리에 더해진 다음 새로운 렌더러가 렌더 트리에 붙을 때)
비동기 배치와 동기 배치
점증 배치는 비동기로 실행된다. 'offsetHeight' 같은 스타일 정보를 요청하는 스크립트는 동기적으로 점증 배치를 실행한다. 전역 배치는 보통 동기적으로 실행된다.
때때로 배치는 스크롤 위치 변화와 같은 일부 속성들 때문에 초기 배치 이후 콜백으로 실행된다.
최적화
배치가 '크기 변경' 또는 렌더러 위치 변화 때문에 실행되는 경우 렌더러의 크기는 다시 계산하지 않고 캐시로부터 가져온다.
어떤 경우는 하위 트리만 수정이 되고 최상위로부터 배치가 시작되지 않는 경우도 있다. 이런 경우는 입력 필드에 텍스트를 입력하는 경우와 같이 변화 범위가 한정적이어서 주변에 영향을 미치지 않을 때 발생한다. 만약 입력 필드 바깥쪽에 텍스트가 입력되는 경우라면 배치는 최상단으로부터 시작될 것이다.
배치 과정
배치는 보통 다음과 형태로 진행된다.
- 부모 렌더러가 자신의 너비를 결정
- 부모가 자식을 검토
- 자식 렌더러를 배치 (자식의 x,y 결정)
- (부모와 자식이 더티하거나 전역 배치 등의 이유로) 필요하다면 자식 배치를 호출하여 자식의 높이 계산
- 부모는 자식의 누적된 높이와 여백, 패딩을 사용하여 자신의 높이를 설정. 이 값은 부모 렌더러의 부모가 사용하게 된다.
- 더티 비트 플래그를 제거한다.
그리기
그리기 단계에서는 화면에 내용을 표시하기 위한 렌더 트리가 탐색되고 렌더러의 'paint' 메서드가 호출된다. 그리기는 UI 기반의 구성 요소를 사용한다.
전역과 점증
그리기는 배치와 마찬가지로 전역 또는 점증 방식으로 수행된다.
점증 그리기에서 일부 렌더러는 전체 트리에 영향을 주지 않는 방식으로 변경된다. 변경된 렌더러는 화면 위의 사각형을 무효화하는데 OS 는 이것을 '더티 영역' 으로 보고, 'paint' 이벤트를 발생시킨다.
그리기 순서
- 배경 색
- 배경 이미지
- 테두리
- 자식
- 아웃라인
동적 변경
브라우저는 변경에 대해 최소한의 동작으로 반응하려고 노력한다.
- 요소 색깔 변경 : 해당 요소 리페인팅만 발생
- 요소 위치 변경 : 요소, 자식, 형제의 리페인팅과 재배치 발생
- DOM 노드 추가 : 노드의 리페인팅과 재배치 발생
HTML 요소의 글꼴 크기를 변경하는 것과 같은 큰 변경은 캐시를 무효화하고 트리 전체의 배치와 리페인팅이 발생한다.
렌더링 엔진 스레드
렌더링 엔진은 통신을 제외한 거의 모든 경우에 단일 스레드로 동작한다. 통신의 경우 병렬 연결의 수는 2~6개로 제한된다.
이벤트 순환
브라우저의 주요 스레드는 이벤트 순환으로 처리 과정을 유지하기 위해 무한 순회된다. 배치와 그리기 같은 이벤트를 위해 대기하고 이벤트를 처리한다.
참고 :
https://d2.naver.com/helloworld/59361
프론트엔드 개발자라면 알고 있어야 할 브라우저의 동작 과정 | 요즘IT
프론트엔드 개발자에게 있어 브라우저는 거의 모든 것과도 같습니다. 하지만 그렇다고 해서 프론트엔드 개발자가 브라우저의 모든 원리에 대해 잘 알고 있는 것은 아니지만, 복잡한 웹 어플리
yozm.wishket.com
'독서&그 외' 카테고리의 다른 글
'개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴' 을 읽고 (7장, 주요 디자인 패턴) (0) | 2022.10.12 |
---|---|
'개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴' 을 읽고 (4장~6장) (0) | 2022.10.10 |
'개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴' 을 읽고 (1장~3장) (0) | 2022.10.10 |
'그림으로 배우는 HTTP & Network Basic' 10장~11장 (0) | 2022.10.07 |
'그림으로 배우는 HTTP & Network Basic' 7장~9장 (0) | 2022.10.06 |
댓글