브라우저마다 다른 아키텍처로 이루어져 있지만 공통화시킬 수 있는 부분을 합쳐 간략하게 정리
브라우저 아키텍처
- User Interface : 주소 표시줄, 이전, 이후 버튼, 홈버튼, 북마크 버튼 등 페이지 외의 모든 영역
- 브라우저 프로세스 : 사용자 UI와 렌더링 프로세스 사이의 동작 제어 주소 표시줄, 이전, 이후 버튼 등 브라우저 동작 부분을 제어
- 렌더링 프로세스 : 탭 안에서 웹 사이트가 표시되는 모든 영역 제어
- Networking: HTTP/HTTPS 네트워크 요청 처리
- JavaScript Interpreter : 자바스크립트 코드 파싱
- UI Backend : 기본 위젯 그릴 때
- 데이터 저장소(Data Persistence) : 로컬에 저장되는 스토리지 메커니즘
- ex ) 쿠키, LocalStorage, IndexedDB
☑️ 모던 브라우저는 대부분 각각의 탭을 독립적인 프로세스로 처리하여 별도의 렌더링 인스턴스를 유지(process per tab)한다.
렌더링 과정 살펴보기
🖱️ 사용자가 URL을 입력했을 때 브라우저는 어떻게 동작할까?
- URL을 입력하면 브라우저 프로세스에서 제일 먼저 작업
- 최근 등장한 브라우저는 대부분 주소창을 자사 검색창과 동일하게 사용 ex ) 크롬의 구글, Edge의 bing
- UI 스레드에서 텍스트가 검색어인지, URL인지 확인
- 검색어 ? 검색 엔진 URL + 검색어 → 페이지 이동
- URL ? 네트워크 호출 수행
- 브라우저 : 로딩 중 표시 할게
- 네트워크 스레드에서 적절한 프로토콜로 요청 처리
- 응답이 도착하면 네트워크 스레드는 응답이 어떤 타입인지 판단한다.
- HTML 문서야? PDF 파일이야? → 렌더러 프로세스
- 다운로드 파일 → 다운로드 매니저
- 안전한 페이지니? 안전하지 않으면 작업 중단할게!
- 검사가 끝나면 네트워크 스레드는 UI 스레드에게 준비 완료를 알림
- UI 스레드는 웹 페이지를 렌더링할 렌더러 프로세스를 찾음
- 네트워크 요청이 오래 걸릴 수 있어서 URL 요청을 보낼 때 UI 스레드는 렌더링을 수행할 프로세스를 미리 찾아는 식으로 최적화가 되어 있기도 함.
- 데이터와 렌더러 프로세스가 준비되면 페이지 이동이 발생
- 페이지 이동 시점 렌더러 프로세스의 일이 많다.
- HTML 데이터를 수신하여 문서를 로딩
- 브라우저 주소 표시줄, 사이트 제목 같은 UI 갱신
- 탭 이동할 수 있도록 세션 기록 저장
- 렌더링이 완료되면 렌더러 프로세스는 브라우저 프로세스 로딩 완료를 알림
- UI 스레드 로딩 표시 중지
렌더러 프로세스 작업
- 탭 내부에서 발생하는 모든 작업을 담당.
- HTML, CSS, 자바스크립트를 사용자와 상호작용 할 수 있는 웹페이지로 변환한다.
렌더러 프로세스의 작업 과정
- HTML 파싱 → DOM Tree
- CSS 파싱 → CSSOM Tree
- 렌더 트리(Render Tree) 생성
- 레이아웃
- 페인트
- 합성
렌더링 프로세스 작업은 모든 HTML을 파싱할 때까지 기다리지 않고, 레이아웃과 페인트를 우선적으로 처리하는 점진적인 방식으로 내용의 일부를 나타냄.
파싱, 렌더 트리(Render Tree) 생성
파싱(Parsing)
- 일련의 문자열을 의미있는 토큰으로 분해. 토큰 간의 위계 관계 분석하여 구조 결정
- 주로 트리 형태이며, Parse Tree, Parsing Tree, Concrete Syntax 트리 등으로 불린다.
파싱 과정
- 변환 : 문자 가져오기. 지정된 인코딩 방식으로 읽기
- 토큰화 : 표준에 맞춰 지정된 태그를 토큰화.
- 렉싱(lexical analysis) : 토큰을 해당 속성 및 규칙을 정의하는 노드로 만듬
- 트리 생성 : root 노드를 기준으로 생성된 노드들의 계층을 연결
HTML 파싱
- 최상위 노드(root)는 <html>이다.
- 파싱의 최종 결과로 DOM 트리가 만들어진다.
- <img> <link> 태그는 DOM 트리를 파싱할 때 데이터를 가져올 수도 있지만 속도가 느려짐.
- 모던 브라우저는 해당 태그가 있을 때 미리 브라우저 프로세스의 네트워크 스레드로 요청 보냄
- <script>는 예외이며, 문서 중간에 자바스크립트 코드가 있을 경우 HTML 파싱을 중단.
자바스크립트 파싱이 끝난 후 HTML 파싱 재개 - 스크립트에서 문서 조작이 없다면 ? script 태그에 async나 defer 속성 지정하여 비동기적으로 로딩 가능
CSS 파싱
- HTML과 비슷하게 토큰화, 노드 생성 거친 후 CSSOM 트리 구조 가짐.
- 부모 스타일을 상속 받기 때문에 CSSOM은 하향식으로 생성됨
렌더 트리(Render Tree) 만들기
- CSSOM 트리 + DOM 트리
- Gecko 엔진(파이어폭스 등)은 형상 트리(frame tree)라고 함. (node = frame)
- Webkit 엔진(사파리 등)은 DOM 트리와 시각 정보를 결합하는 과정을 attachment라고 부른다.
- 각 노드는 렌더 객체(Render Object)로 이루어져 있다.
렌더 트리 생성 과정
- <html>태그와 <body>를 처리하며 렌더 트리 루트 구성
- DOM 트리 순회. 최상위 노드부터 보여지지 않는 노드(<link>, <script>, <meta>)를 생략
- display:none처럼 CSS로 숨겨지는 노드 또한 트리에서 생략
- float나 position 같은 속성 사용 시 실제 그려지는 위치로 렌더 객체가 이동
- 화면에 나타나는 노드에 CSSOM 규칙을 찾아 일치하는 스타일 적용
레이아웃
- 상대적인 위치, 크기를 찾는 과정
- 렌더 트리의 모든 노드들은 자식 노드들의 레이아웃을 호출하는 layout(Gecko 엔진 reflow)
- 노드 위치는 (x, y) 좌표계 사용. 최상위 위치는 (0,0)
- 배치는 왼쪽 → 오른쪽 → 위 → 아래 흐름 방향 진행
- “이후” 요소는 “이전” 요소에 영향을 주지 않지만,
diplay : table
,display : flex
는 영향 줌
☑️ 초기 레이아웃 이후 DOM Node가 추가되거나 변경되었을 경우 전체를 다시 배치하지 않기 위해 더티 비트(Dirty Bit) 사용 더티(Dirty Bit)란? : 다시 배치할 필요가 있는 변경 요소, 추가된 노드, 자식 노드 등
☑️ 글로벌 레이아웃 : 전체 레이아웃. ex ) font-family, font-size 같이 전역 스타일 변경 혹은 창 리사이즈 로컬 레이아웃 : 일부만 변경 레이아웃. ex) 더티 렌더 객체
레이아웃 생성 과정
- 부모 노드가 자식 노드의 너비 결정. (블록 너비, 렌더 객체 스타일, 박스 모델 고려하여 계산)
- 자식 렌더링 객체 배치
- x, y 값 지정
- 부모 혹은 자식 객체가 더티한 경우 재계산 layout() 메서드를 호출해 높이를 계산
- 자식 렌더링 객체의 높이를 더해 부모 렌더 객체 높이 계산
- 레이아웃 중인 렌더 객체의 더티 플래그 제거
페인트
- 렌더 트리를 순회하며 레이어를 만들고 레이어의 배경, 테두리, 텍스트 그려지는 순서, 레이어 간의 순서 등의 과정 기록
- z-index, float, position 등
합성(Compositing)
- 레이어를 분리해서 래스터화 한 뒤, 브라우저에서 페이지의 크기, 뷰포트에 맞게 합성해 화면으로 나타냄.
- 래스터화(rasterizing) : 화면을 픽셀로 변환는 것
- 합성 단계에 그려질 요소의 순서, 스타일, 위치 등을 모두 알고 있음
- 모던 브라우저의 경우 ) 레이어 래스터화 후 조각화한 타일로 만들어 저장 → 뷰포트 변경 시 타일 변경 (속도 빠름)
- HTML, CSS, JavaScript 화면의 픽셀로 나타나기 위한 과정을 주요 렌더링 경로 (Critical Rendering Path, CRP)라고 부른다.
- 레이아웃 수행, 페인트 진행 포함
- CRP는 성능에 큰 영향을 미친다.