일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 암묵적 타입 변환
- task queue
- Render Queue
- Headless 컴포넌트
- Microtask Queue
- docker
- TypeScript
- type assertion
- linux 배포판
- 주니어개발자
- 프로세스
- Sparkplug
- useEffect
- AJIT
- Custom Hook
- useLayoutEffect
- CS
- 좋은 PR
- prettier-plugin-tailwindcss
- 명시적 타입 변환
- useMemo
- Compound Component
- Dockerfile
- React.memo
- useCallback
- react
- 타입 단언
- Event Loop
- JavaScript
- prettier
- Today
- Total
구리
[Browser] 브라우저 렌더링 과정 본문
브라우저 구성 요소
- 사용자 인터페이스 (User Interface)
- 요청 페이지를 보여주는 창을 제외한 나머지 부분 (주소 표시줄, 이전, 다음 버튼, 북마크 메뉴 등)
- 브라우저 엔진 (Browser Engine)
- UI와 렌더링 엔진을 연결하는 역할
- 사용자가 UI 주소 표시줄에 URI 입력시, URI를 전달받은 브라우저 엔진이 자료 저장소에서 해당 URI에 알맞은 자료를 찾음
- 자료가 있을 경우 → 해당 자료를 렌더링 엔진에게 전달
- 자료가 없을 경우 → URI 값만 렌더링 엔진에게 전달
- 렌더링 엔진 (Rendering Engine)
- 브라우저 엔진으로부터 전달받은 URI를 네트워크 레이어에 전달해 데이터(HTML, CSS, JS)를 받음
- HTML, CSS를 파싱하고 JS 파일은 JS 해석기에게 전달해 파싱된 JS를 받아 render tree 생성해 UI 백엔드에게 전달
- (크롬의 경우, Webkit 기반의 Blink 엔진 사용)
- 통신 (Network)
- 각종 네트워크 요청을 수행
- URI 요청에 해당하는 서버로부터의 응답 데이터를 렌더링 엔진에게 전달
- 자바스크립트 해석기 (JavaScript Interpreter)
- 자바스크립트를 파싱 (크롬의 경우, V8 엔진 사용)
- UI 백엔드 (Display Backend)
- render tree를 browser에 그리는 역할
- 자료 저장소 (Data Persistence)
- 로컬스토리지나 쿠키 등 보조기억장치에 데이터를 저장하는 역할
- 자주 사용되는 자료를 저장해 서버에 반복적으로 요청하는 작업을 줄입 (캐싱이 이뤄지는 곳)
파싱
구문 분석이라고 하는 파싱은 문장을 이루고 있는 구성 성분을 분해하고 분해된 성분의 위계 관계를 분석해 구조를 결정하는 것을 의미합니다. 즉, 데이터를 분해 분석해 원하는 형태로 조립하고 다시 빼내는 프로그램을 의미합니다.
브라우저 로딩 과정
아래는 브라우저 로딩 과정을 간략하게 요약한 과정입니다.
- 사용자가 사용자 인터페이스의 주소표시줄에 URI를 입력하면 브라우저 엔진에게 전달
- 브라우저 엔진은 자료 저장소에서 URI에 해당하는 자료를 찾고, 있으면 해당 자료를 렌더링 엔진에게 전달
- 렌더링 엔진은 브라우저 엔진에게 전달 받은 자료(HTML, CSS, image 등)를 분석하고 URI 데이터를 통신에게 전달하고, 자바스크립트 파일을 JS 해석기에게 전달하며, render tree를 UI 백엔드로 전파 (또한 렌더링 엔진은 통신 레이어에 URI에 대한 추가 데이터(있으면)를 요청하고 응답할 때까지 기다림)
- 응답 받은 데이터에서 HTML, CSS는 렌더링 엔진이 파싱
- 응답 받은 데이터에서 JS는 JS 해석기가 파싱
- JS 해석기는 파싱 결과를 렌더링 엔진에게 전달해 3, 4 과정에서 파싱한 HTML의 결과인 DOM tree를 조작
- 조작이 완료된 DOM node(DOM tree 구성 요소)는 render object(render tree 구성 요소)로 변함
- UI 백엔드는 render object를 브라우저 렌더링 화면에 띄움
Critical Rendering Path
웹 브라우저가 웹 페이지를 출력하기 위해선 서버로부터 HTML 파일을 다운받아 사용자의 화면에 픽셀을 그려내기까지 많은 과정을 거칩니다. 이렇게 웹 브라우저가 웹페이지를 최초로 그리기 위해 필요한 일련의 과정을 Critical Rendering Path(CRP)
라고 합니다.
위 과정은 크게 6단계로 구성됩니다
- DOM tree 생성
- CSSOM tree 생성
- JavaScript 실행
- Render tree 생성
- Layout 구성
- Painting
1. DOM tree 생성
브라우저는 응답받은 바이트형식의 데이터를 문자열로 변환합니다.
이후 렌더링 엔진은 아래와 같은 HTML을 한줄씩 순차적으로 parsing해 브라우저가 이해할 수 있는 트리 형태의 자료구조인 DOM
을 생성하는데 아래 단계를 거치며 진행됩니다.
- 변환된 문자열을 문법적 의미를 갖는 최소 단위인 토큰들로 분해
- 토큰을 객체로 변환해 노드를 구성
- 생성된 노드 객체를 트리 자료구조로 구성
파싱 과정에서 CSS를 로드하는 <link>
, <style>
태그를 만나면 CSS를 파싱하고 CSSOM 생성을 시작합니다.
DOM, CSSOM 생성은 병렬적으로 실행되며, 파싱 과정에서 <script>
태그를 만나면 DOM 생성을 중단하고 자바스크립트를 파싱하고 실행합니다.
HTML을 DOM node로 구성된 DOM tree 형태로 표현하는 이유는 HTML 자체가 Markup Language이기에 정보를 표시하는 역할만 하며 정보가 동적으로 움직이게 하는 건 불가능합니다. DOM tree로 표현하면 JavaScript로 조작이 가능하기에 동적인 표현이 가능하며 사용자와의 상호작용을 할 수 있습니다.
2. CSSOM tree 생성
DOM 생성과 같은 과정을 반복해 CSSOM을 생성합니다.
브라우저의 렌더링 엔진은 DOM, CSSOM 생성을 다른 스레드에 올려 병렬적으로 실행합니다.
그리고 CSSOM은 CSS의 상속을 반영해 생성합니다.
CSS 상속(inheritance)
부모 요소가 자식에게 스타일을 전달하는 기능입니다.
CSSOM 생성 과정에서, 각각의 요소가 어떤 CSS 스타일을 가질지 계산하기 위해서는 해당 요소의 부모요소로부터 상속된 스타일도 고려해야 합니다. 그래서 상속 관계를 반영해 생성됩니다.
추가로 HTML과 CSS는 Render Blocking Resource(렌더 차단 리소스)입니다.
즉, DOM과 CSSOM의 생성이 모두 완료되기 전까지는 절대 화면에 렌더링하지 않는다는 뜻입니다. 이유가 뭘까요?
HTML의 경우, DOM의 생성이 완료되지 않으면 렌더링할 것이 없으니 납득이 되지만 CSS의 경우에는 이해가 되지 않을 수 있습니다.
그 이유는 CSS가 완전히 파싱되어서 CSSOM의 생성이 완료되기 전에 렌더링을 해버리면 오른쪽 화면처럼 스타일이 전혀 적용되지 않은 페이지를 사용자에게 보여주게 됩니다. 사용성이 떨어지기에 웹 브라우저는 CSSOM의 생성이 완료될 때까지 렌더링을 차단하는 것입니다.
3. 자바스크립트 실행
HTML과 CSS를 파싱하다 script 태그를 만나면 진행하던 파싱을 중단합니다. 이유는 자바스크립트가 Parser Blocking Resource(파서 차단 리소스)이기 때문입니다.
렌더링 엔진은 파싱을 중단하고 자바스크립트 파일을 로드한 후 자바스크립트 엔진에게 제어권을 넘겨 자바스크립트 파싱, 실행을 한 후 완료되면 렌더링 엔진에게 다시 제어권을 넘겨 파싱이 중단된 지점부터 DOM과 CSSOM 생성을 재개합니다.
하지만 자바스크립트가 HTML 파싱을 중단하면 2가지 문제가 발생할 수 있습니다.
- JS 파일을 로드 후 실행을 완료할 때까지 사용자의 화면에는 아무것도 렌더링되지 않는 문제
- JS 파일 내부에서 DOM API를 사용하는 경우 참조 에러 발생하는 문제
- JS 파일을 실행하는 시점에선 DOM이 구성되기 전이기에 존재하지 않는 요소에 대한 접근은 에러를 발생시킴
위와 같은 문제를 해결하기 위해 일반적으로 <script> 태그는 body 태그의 마지막에 두거나 HTML5에 추가된 <script>
태그의 async
나 defer
속성을 사용합니다.
script async, defer
async, defer 속성은 HTML 파싱과 병렬로 스크립트를 다운로드합니다.
async - 스크립트 다운로드가 완료되면 즉시 스크립트를 실행합니다. 또한 해당 속성을 사용한 JS 파일이 여러개라면 정의 순서과 상관없이 먼저 다운로드된 파일부터 실행합니다. 따라서 자바스크립트가 순서에 의존적이라면 문제가 될 수 있습니다.
defer - 스크립트 다운로드, HTML 파싱이 모두 완료된 이후 스크립트를 실행합니다. 해당 속성을 사용한 JS 파일이 여러개라면 정의된 순서대로 실행됩니다.
4. 렌더 트리를 생성
DOM, CSSOM이 완성되면 attchment
하여 렌더트리를 생성합니다.
attchment
렌더 트리는 DOM과 CSSOM의 결합체로 렌더링 엔진이 스타일, 레이아웃 정보를 계산해 브라우저 화면에 실제 표시될 요소만 추출한 것입니다.
attchment는 이 렌더 트리를 생성하기 위해 DOM, CSSOM을 연결하는 과정으로, 탐색하면서 일치하는 요소들을 찾아 레이아웃, 스타일 정보를 결합해 최종적으로 렌더 트리를 생성하는 역할을 합니다. 생성된 렌더 트리는 위치, 크키, 스타일 등을 계산해 브라우저에 렌더링됩니다.
5. Layout 구성
완성된 렌더 트리를 기반으로 HTML 요소의 레이아웃을 픽셀 단위로 계산해 렌더 트리에 반영합니다.
렌더 트리의 객체는 자신과 자식 요소가 어떻게 배치하고 그려야 할지 알고 있습니다.
6. Paint
이전 레이아웃 단계에서 계산된 값을 이용해 각 노드를 화면상의 실제 픽셀로 변환합니다.
그리고 픽셀로 변환된 결과는 포토샵의 레이어처럼 생성되어 개별 레이어로 관리됩니다.
페인팅 과정에서 텍스트, 색깔, 그림자 및 버튼이나 이미지 같은 요소를 포함해 모든 요소의 시각적인 부분을 화면에 그리는 작업이 포함됩니다.
7. 합성 & 렌더
페인트 단계에서 생성된 모든 레이어를 합성해 하나의 레이어로 결합하고 스크린을 업데이트합니다. 합성과 렌더 단계가 끝나면 화면에서 웹 페이지를 볼 수 있습니다.
해당 과정은 기존의 Layout, Paint와는 달리 GPU를 이용해 더 빠르게 진행될 수 있습니다.
8. Reflow와 Repaint (추가)
Painting 단계까지 완료되면 웹 페이지의 렌더링이 모두 완료된 것입니다.
하지만 이후 사용자의 이벤트 발생으로 인해 자바스크립트 내의 DOM API가 실행되면 변경된 내용이 DOM, CSSOM에 적용된 후 다시 render tree를 생성하고 layout 및 paint 과정이 반복됩니다.
여기서 layout을 다시 구성하는 과정을 Reflow
라고 하며 다음과 같은 상황에 한해서 실행합니다.
- 자바스크립트에 의한 노드 추가 및 삭제
- 브라우저 창의 리사이징에 의한 viewport 크기 변경
- HTML 요소의 레이아웃에 변경을 발생시키는 스타일의 변경
그리고 paint를 다시 하는 과정을 Repaint
라고 합니다. 이는 재결합된 렌더 트리를 기반으로 다시 Painting 하는 것을 의미합니다.
여기서 중요한 건 Reflow와 Repaint는 항상 동기적으로 같이 동작하지 않습니다. 레이아웃에 영향이 없는 렌더 트리의 변경에는 Repaint만 동작하며 Reflow, Repaint를 포함하는 리렌더링은 성능적으로 많은 리소스를 사용하는 작업이기에 최대한 리렌더링이 발생하지 않도록 관리하는 것이 좋습니다.
다음은 Reflow, Repaint가 일어나는 대표적인 속성들입니다.
- Reflow
- position, width, height, margin, padding, font-size, line-height, overflow
- Repaint
- background, color, text-decoration, border-style
요약
브라우저 렌더링 과정을 간단히 요약하면 다음과 같습니다.
- HTML 마크업을 처리해 DOM tree를 생성 (DOM 파싱)
- CSS 마크업을 처리해 CSSOM tree를 생성 (CSS 파싱)
- DOM 및 CSSOM을 결합해 render tree를 생성 (Attachment)
- render tree에서 레이아웃을 실행해 각 node의 형태를 계산 (Layout)
- 계산된 값을 이용해 각 노드를 화면상의 실제 픽셀로 변환하고, 레이어를 만듦 (Paint)
- 레이어를 합성해 실제 화면에 나타냄 (Composite)
참고 자료
https://d2.naver.com/helloworld/59361
https://product.kyobobook.co.kr/detail/S000001766445
https://ui.toast.com/fe-guide/ko_PERFORMANCE
https://web.dev/critical-rendering-path-render-blocking-css/?hl=ko
https://developer.mozilla.org/ko/docs/Web/Performance/Critical_rendering_path
https://developer.mozilla.org/ko/docs/Web/Performance/How_browsers_work
https://dev.to/coderedjack/critical-rendering-path-web-performance-23ij
엔진별 렌더링 과정
https://webkit.org/blog/116/webcore-rendering-iii-layout-basics/
https://www.chromium.org/developers/the-rendering-critical-path/
브라우저 발전 방향 관련 글
'Web' 카테고리의 다른 글
[브라우저] 자바스크립트 엔진은 어떤 기준으로 코드를 순서대로 실행할까? (0) | 2023.10.03 |
---|---|
[브라우저] 자바스크립트 엔진은 내 코드를 어떻게 실행할까? (0) | 2023.10.01 |
[SEO] JSON-LD, 구조화된 데이터 (0) | 2023.08.31 |
[Browser] 웹성능 최적화 (0) | 2023.06.30 |