네이버 D2의 브라우저는 어떻게 동작하는가? 와 Mdn web docs의 글을 학습한 후 정리한 글입니다.
브라우저의 주 기능은 사용자가 선택한 자원을 서버에 요청하고 브라우저에 렌더링하는 것이다. 보통 HTML 문서이며, PDF나 이미지 형태의 파일도 표현할 수 있다. 브라우저는 주소 표시줄, 이전/다음 페이지 등으로 이루어진 사용자 인터페이스 (프레임이라 생각해도 될 듯), 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어하는 브라우저 엔진, 요청한 콘텐츠를 뷰포트에 출력하는 렌더링 엔진, 백엔드에서 실행되는 통신, 이를 위한 백엔드 UI, 자바스크립트 해석기, 자원 저장소로 구성된다.
브라우저의 전반적인 동작 흐름은 탐색 > 응답 > 파싱 > 렌더로 이루어진다.
1. 탐색
1-1. DNS 조회
웹페이지를 로딩하는 첫 단계이다. 사용자가 URL을 입력하여, 요청을 발생시키면 DNS 조회부터 시작한다. 해당 페이지의 자원이 어디에 위치하는지 찾는 것이다. 브라우저가 서버에 DNS 조회를 요청하면 IP주소를 반환하게 되고, 일정 기간 동안 캐싱된다.
1-2. TCP 핸드셰이크
IP주소를 알았다면 브라우저는 서버와 TCP-3방향 핸드셰이크를 통해 연결을 설정한다. 데이터 전송 전에 TCP 소켓 연결을 위한 매개변수를 주고 받기 위함이다. HTTPS의 연결에서는 추가적인 핸드셰이크를 요구한다. 이 과정에서는 통신 암호를 결정하고, 서버를 확인하여 안전한 연결이 이루어지도록 한다. 이러한 과정은 페이지의 로딩을 늦추지만, 보안성있는 연결은 지연시간이라는 기회비용을 낼 가치가 있다.
2. 응답
웹 서버로 연결이 성립되면, 브라우저는 유저 대신에 GET 요청을 보낸다. 대개 HTML을 요청하며, 관련 응답 헤더와 함께 HTML의 내용을 반환받는다. 첫 응답 패킷은 14kb이다. TCP 슬로우 스타트 / 14kb Rule 이라고도 하는 규약은 네트워크 통신의 속도를 조절하기 위해 정해졌다. 그 후 점진적으로 (2배) 전송량을 증가시킨다. 따라서, 웹 최적화 간 이를 염두하여 첫 14kb 데이터에 빨리 표시되어야 하는 정보를 담는 것을 권장한다고 한다.
렌더링 엔진의 주요 흐름
CSS를 사용하면서 -webkit-이라는 단어를 만난 적이 있을 것이다. Webkit는 크롬과 사파리의 데이터를 화면에 출력하는 렌더링 엔진이다.
- DOM 생성을 위한 HTML 파싱
- CSSOM 트리 생성을 위한 외부 요소 파싱
- 렌더 트리 구축
- 렌더 트리 배치 (레이아웃)
- 렌더 트리 그리기 (페인팅)
먼저 렌더링 엔진은 HTML 문서를 파싱하고, 'HTML 콘텐츠 트리'내부의 태그들을 DOM 노드로 변환한다. 그 후 외부 CSS 파일과 함께 스타일 요소도 파싱한다. (스타일 규칙 모델, CSSOM) 이를 통해 '렌더 트리'가 생성되며, 정해진 우선순위에 따라 화면에 출력(배치)된다. 빠르게 내용을 표시하기 위해 나머지 데이터가 전부 수신되지 않아도 화면에 먼저 표시한다.
3. 파싱
정해진 구문 규칙에 따라 문서에 작성된 언어를 번역한다. 단어 단위의 유효한 토큰으로 분석하는 어휘 분석기와, 문서의 구조를 분석하여 파싱 트리를 생성하는 파서로 구성된다. 그렇게 생성된 파싱 트리는 컴파일을 통해 최종적인 기계코드로 변환된다. (문서 > 어휘분석 > 구문분석 > 파싱트리->변환->기계언어)
3-1. HTML 파싱 (DOM)
문맥 자유 문법이라는 특징을 갖는 XML에서의 기존 파싱 방식은 상위 구조로부터 일치하는 부분을 찾는 하향식 파서와, 그 반대의 상향식 파서가 존재한다. 그러나 HTML은 문맥 자유 문법이 아니기 때문에, 전통적인 파싱 방식을 적용할 수 없다. 원인으로는 언어의 너그러운 속성과 관용, 추가된 토큰에 의한 재파싱 등이 있다. 따라서 HTML을 파싱하기 위해 별도의 파서 방식이 구현되었는데, 이는 토큰화와 DOM 트리 구축 두 단계로 이루어진다.
토큰화
토큰화 단계는 어휘 분석 단계로, 입력 값을 토큰(시작 태그, 종료 태그, 속성 이름, 속성 값)으로 파싱한다. 이를 인지하여 트리 생성자로 넘기는 과정을 반복하여 단위별 토큰을 생성한다.
DOM 트리 구축
토큰화된 입력을 분석하여 DOM 트리를 만든다. 이 때 프로세스는 메인 쓰레드를 차지한다.
3-2. CSS 파싱 (CSSOM)
HTML과 달리 CSS는 문맥 자유 문법이다. 따라서 정규 표현식과 룰셋에 따라 파싱이 가능하다. HTML5로 넘어오면서, 스크립트를 비동기적으로 처리하는 속성이 추가되었기 때문에 DOM 트리 파싱과 관계없이 별도로 파싱과 CSSOM의 구축이 이루어진다.
CSSOM 트리 구축
CSSOM은 DOM과 같이 트리구조이며, 각각은 독립적이다. 브라우저는 가장 일반적인 규칙부터 시작해 스타일을 적용해나간다. 그리고 재귀적으로 계산된 스타일을 Cascading하게 수정해나간다.
3-3. Javascript 컴파일
CSS가 분석되고 CSSOM이 생성되는 동안, 다른 자원들도 다운로드되며 해석, 컴파일, 추상 구문 트리에 의한 구문 분석(파싱)이 이루어진다.
4. 렌더링
4-1. 렌더 트리 구축
CSSOM과 DOM 트리가 구축되는 동안 브라우저는 이들을 합성하여 렌더 트리를 구축한다. DOM 트리의 루트부터 시작해 노드를 추가하면 렌더 'attach' 메서드를 통해 렌더 트리로 추가된다. 단 display=none이거나, Head 요소와 같은 비시각적 DOM 요소는 렌더 트리에 추가되지 않는다. 지난 번 공부한 DOM에서도 나와 있는 내용이다. CSSOM 트리 또한 규칙에 의해 계산되어 처리된다.
4-2. 레이아웃 배치
렌더트리가 생성된 후, 각 노드의 도형 값을 계산하기 위한 레이아웃이 실행된다. 이를 통해 모든 노드의 너비, 높이, 위치가 결정된다. 처음 결정되는 것만 해당하며, 이후 수정되는 것들은 리플로우라고 한다.
4-3. 페인팅
레이아웃 단계에서 계산된 박스들을 실제 화면의 픽셀로 변환한다. 추가적으로 텍스트, 색깔, 경계, 그림자 같은 모든 요소의 시각적인 부분을 화면에 그린다. 레이아웃 트리의 노드 중 <Video>나 <Canvas> 등은 레이어로 분리하여 GPU 단계에서 그려진다. 이를 통해 페인팅 성능을 향상시킨다.
위의 DOM - CSSOM - 레이아웃 - 페인팅 과정을 합쳐 CRP라고 부르기도 한다. (Critical Rendering Path, 중요 렌더링 경로)
D2의 글은 구성 요소에 의한 렌더링 과정에 초점을 맞추어 자세히 설명하고 있고, MDN의 글은 전반적인 단계들을 균형있게 설명해주고 있기에 둘을 조합하여 정리하였습니다. D2의 CSS 렌더 트리 구축 부분은 몇 번 더 읽어본 후 정리할 필요가 있을 듯 합니다. 읽어주셔서 감사합니다.