웹 개발자라면 한 번쯤 "브라우저가 어떻게 웹페이지를 렌더링할까?"라는 질문을 받아본 적이 있을 것이다.
브라우저의 렌더링 과정에 대해 자세히 살펴보며, 웹페이지가 사용자에게 어떻게 표시되는지를 정리해보자!
📌브라우저와 렌더링 엔진
브라우저는 사용자가 요청한 자원을 서버에서 받아와 화면에 표시하는 역할을 한다.
이 자원들은 HTML 문서, 이미지, PDF 파일 등 다양한 형태를 가질 수 있으며, 브라우저는 이러한 자원을 렌더링 엔진을 통해 화면에 표시한다.
각 브라우저는 고유한 렌더링 엔진을 사용하며, 예를 들어 크롬은 블링크(Blink), 사파리는 웹킷(Webkit), 파이어폭스는 게코(Gecko)라는 렌더링 엔진을 사용한다.
렌더링 엔진 HTML, CSS, JavaScript를 파싱하고, 이를 화면에 표시 가능한 형태로 변환하는 엔진이다.
📌렌더링의 주요 단계
브라우저가 웹페이지를 렌더링하는 과정은 여러 단계로 나눌 수 있다.
각 단계는 웹페이지를 최종적으로 사용자에게 표시하기 위해 중요한 역할을 한다!
1️⃣리소스 불러오기
브라우저는 서버로부터 HTML, CSS, JavaScript 등의 파일을 요청하고, 이를 로드한다. 이 과정에서 로더가 서버로부터 전달받은 리소스 스트림을 읽는다.
리소스 스트림이란?
리소스 스트림은 서버로부터 데이터를 순차적으로 받아오는 과정이다. 브라우저는 리소스를 모두 받아온 후 처리하는 것이 아니라, 받아오는 즉시 파싱을 시작하여 페이지 로딩 속도를 높인다.
프리로드 스캐너(Preload Scanner) 프리로드 스캐너는 브라우저가 아직 도달하지 않은 자원을 미리 요청하여, 렌더링 속도를 개선하는 데 기여한다. 예를 들어, HTML을 파싱하는 도중 CSS 파일이나 이미지를 참조하는 링크를 만나면, 이를 즉시 로드해 파싱이 완료되기 전에 준비를 마칠 수 있다.
2️⃣ DOM, CSSOM 생성
사용자가 브라우저를 통해 웹 사이트에 접속하면, 서버로부터 HTML, CSS 등 웹 사이트에 필요한 리소스를 다운로드 받는다.
브라우저가 페이지를 렌더링하려면 먼저 HTML 코드는 DOM 트리로, CSS는 CSSOM 트리로 변환되어야 한다.
DOM 트리 생성
HTML 코드를 DOM 트리로 변환하는 과정은 다음과 같다:
변환: 브라우저가 HTML의 원시 바이트를 읽어와서, HTML에 정의된 인코딩(예: UTF-8)에 따라 개별 문자로 변환함
토큰화: 브라우저가 문자열을 W3C 표준에 지정된 고유 토큰으로 변환함
렉싱: 방출된 토큰은 해당 속성 및 규칙을 정의하는 객체로 변환함
DOM 생성: 마지막으로 HTML 마크업에 정의된 여러 태그 간의 관계를 해석해서 트리 구조로 연결됨
CSSOM 트리 생성
CSS도 HTML과 유사하게 CSSOM 트리로 변환된다. HTML 마크업 내에 직접(inline) 스타일을 선언할 수도 있지만, head 태그에 외부(external) CSS 파일을 참조하거나, head 태그에 style 태그(internal)를 정의할 수 있다.
브라우저는 이러한 CSS를 처리하여 CSSOM 트리를 생성.
3️⃣ 렌더 트리 생성
DOM 트리와 CSSOM 트리가 생성되면, 이 둘을 결합하여 렌더 트리를 생성한다. 렌더 트리에는 페이지를 렌더링하는데 필요한 노드만 포함된다.
4️⃣ 레이아웃 계산
레이아웃 단계에서는 뷰포트 내에서 각 요소의 정확한 위치와 크기를 캡처하는 Box 모델이 출력된다. 모든 상대적인 측정값은 화면에서 절대적인 픽셀로 변환된다.
5️⃣ 페인트(Painting)
최종적으로 렌더 트리에 있는 노드들이 픽셀로 변환되어 화면에 표시된다.
이 과정은 실제로 화면에 요소들을 그리는 작업을 의미한다.
📌리플로우(Reflow)와 리페인트(Repaint)
사용자가 웹 페이지에 처음 접속하면, 렌더링 과정을 거쳐 화면에 모든 요소가 그려진다. 이후에 사용자는 다양한 액션을 수행하게 되고, 여기서 발생하는 이벤트로 인해 새로운 HTML 요소가 추가되거나 기존 요소의 스타일이 바뀌는 변경이 발생하게 된다.
=> 이러한 변경이 발생하면 리플로우(Reflow)와 리페인트(Repaint) 과정이 실행된다!
리플로우(Reflow): 레이아웃 계산을 다시 하는 과정으로, DOM 구조의 변경이나 스타일 변경 시 발생 리페인트(Repaint): 리플로우 후 다시 화면에 그리는 작업으로, 페이지의 시각적 요소가 변경될 때 발생
리플로우가 발생하는 속성들: position, width, height, margin, padding, border, font-size 등 리페인트만 발생하는 속성들: background, color, text-decoration, border-style, border-radius 등
📌브라우저 렌더링의 반복과 최적화
브라우저 렌더링은 한 번만 실행되는 것이 아니라, 자바스크립트로 DOM이 변경되거나 창 크기가 조정되는 등의 이벤트에 따라 반복된다. 이를리렌더링이라고 하며, 성능에 중요한 영향을 미친다.
불필요한 리렌더링은 페이지의 성능을 저하시킬 수 있으므로, 최적화가 필요하다!
리액트를 사용하는 이유: 성능 최적화
리액트(React)는 이러한 브라우저 렌더링의 반복 문제를 효과적으로 관리할 수 있는 도구이다. 리액트의 핵심 개념 중 하나는 Virtual DOM이다.
Virtual DOM 브라우저의 실제 DOM을 조작하기 전에 메모리 상에서 가상으로 DOM 구조를 관리하는 방식
리액트를 사용하는 이유
효율적인 리렌더링 관리: 리액트는 상태나 props가 변경될 때마다 Virtual DOM에서 먼저 변경 사항을 적용한 후, 실제 DOM과 비교(diffing)하여 필요한 부분만 업데이트한다. 이를 통해 불필요한 리렌더링을 줄여 성능을 최적화할 수 있다.
리플로우와 리페인트 최소화: 리액트의 Virtual DOM은 변경 사항을 효율적으로 계산하여 실제 DOM에 최소한의 업데이트만 수행한다. 이로 인해 리플로우와 리페인트를 최소화하여 성능을 향상시킨다.
컴포넌트 기반 설계: 리액트는 컴포넌트 단위로 UI를 구성할 수 있어, 특정 부분의 변경이 다른 부분에 불필요하게 영향을 미치지 않도록 한다. 이로 인해 더 효율적인 UI 업데이트가 가능하다
📌자바스크립트와 렌더링
자바스크립트는 DOM과 CSSOM을 동적으로 변경할 수 있으며, 이는 렌더 트리에 영향을 미친다.
자바스크립트의 파싱과 실행 과정에서 HTML 파싱이 중단될 수 있으므로, 스크립트의 위치나 비동기 로딩 방법을 신중히 고려해야 한다.
동기(Synchronous)와 비동기(Asynchronous)
동기 방식에서는 스크립트 실행이 완료될 때까지 다른 작업이 중단된다. 반면, 비동기 방식에서는 작업이 동시에 진행될 수 있다. 스크립트를 defer 또는 async 속성을 사용해 비동기로 처리하면 페이지 로딩 속도를 개선할 수 있다