브라우저 렌더링 과정 이해하기
2025. 7. 19.
개요
브라우저의 렌더링 과정을 이해하는 것은 웹 성능 최적화와 직결된다. 브라우저의 내부 메커니즘을 알면 제한된 자원 내에서 렌더링 시간을 단축하고, 사용자에게 끊김 없는 경험을 제공할 수 있다.
웹 로딩의 병목은 크게 두 가지 지점에서 발생한다.
1. 네트워크 지연 시간 (Latency)
서버에서 데이터를 가져오는 과정에서 발생하는 물리적 거리나 환경에 따른 시간이다. 최적화의 핵심은 데이터 전송 시간을 최소화하여, 웹페이지를 사용자에게 최대한 빠르게 보여주는 것이다.
2. 메인 스레드 점유 (Single Thread)
가장 중요한 점은 브라우저가 대부분 싱글 스레드(Single Thread)로 동작한다는 사실이다.
- 화면을 그리는 렌더링 작업과 사용자의 클릭·스크롤에 반응하는 작업은 모두 하나의 '메인 스레드'에서 처리된다.
- 만약 렌더링 과정이 비효율적이라 메인 스레드를 오래 점유하게 되면, 사용자는 화면이 멈추거나 반응이 느리다고 느끼게 된다.
결국 브라우저의 내부 메커니즘을 이해한다는 것은, 제한된 자원(싱글 스레드) 내에서 렌더링 시간을 최소화하여 메인 스레드에 자유를 주는 방법을 배우는 것이다. 이를 통해 우리는 부드러운 스크롤과 즉각적인 응답성을 갖춘, 수준 높은 웹 경험을 만들어낼 수 있다.
브라우저 렌더링 과정
1. 탐색(Navigation)
탐색은 사용자가 주소창에 URL을 입력하거나, 링크를 클릭하고, 폼(form)을 제출하는 등의 동작을 하는 순간 시작되는 웹페이지 로딩의 첫 관문이다. 이 단계의 목표는 최단 시간 내에 서버에 도달하는 것이다.
1.1 DNS 조회(DNS Lookup)
웹사이트의 주소(example.com)는 인간을 위한 이름일 뿐, 실제 데이터가 있는 곳은 IP 주소(93.184.216.34)이다.
- 과정: 브라우저가 이름 서버에 IP 주소를 묻고 응답을 받는다. 처음 방문 시에는 반드시 거쳐야 하지만, 이후에는 캐시를 통해 속도를 높인다.
- 지연 요인: 페이지 내에 서로 다른 호스트(글꼴, 광고, 스크립트 등)가 많다면 각각 DNS 조회가 필요하다. 특히 모바일 환경에서는 셀 타워를 거쳐야 하므로 물리적 거리에 따른 지연이 성능에 큰 영향을 미친다.
1.2 TCP 핸드셰이크 (TCP Handshake)
IP 주소를 알아냈다면 이제 서버와 신뢰할 수 있는 통로를 만들어야 한다. 이를 TCP 3-way Handshake라고 한다.
- 과정 (SYN-SYN-ACK): 클라이언트가 SYN을 보내고, 서버가 SYN-ACK으로 화답하며, 마지막으로 클라이언트가 ACK을 보냄으로써 연결이 성립된다.
- 의미: 실제 데이터를 요청하기도 전에 이미 3번의 메시지가 오가며 시간을 소모하게 된다.
1.3 TLS 협상 (TLS Negotiation)
보안 연결(HTTPS)을 사용하는 현대 웹에서는 추가적인 핸드셰이크가 필요하다.
- 역할: 통신 암호화에 쓰일 암호 알고리즘을 결정하고 서버의 신원을 확인한다.
- 비용: 이 과정을 위해 클라이언트와 서버 사이를 3번 더 왕복해야 한다. (※ 최신 표준인 TLS 1.3에서는 이 과정을 1번의 왕복(1-RTT)으로 줄였지만, 여전히 초기 연결에는 추가적인 시간이 소요) 비록 지연시간이라는 비용이 발생하지만, 데이터 가로채기를 방지하기 위한 필수적인 단계이다.
2. 응답(Response)
무려 8번의 왕복(DNS, TCP, TLS) 끝에 드디어 브라우저는 서버에 실제 데이터를 요청할 수 있게 된다.
-
TTFB와 초기 데이터 전송
- TTFB (Time to First Byte): 요청 후 HTML의 첫 번째 패킷(첫 바이트)을 받기까지 걸린 시간이다.
- 14kb의 마법: 일반적으로 초기 응답의 콘텐츠 청크는 약 14kb 크기이다. 이 작은 패킷 안에 페이지의 핵심 구조가 담겨 있어야 브라우저가 빠르게 화면을 그리기 시작할 수 있다.
-
혼잡 제어 (Congrestion Control) & TCP 슬로우 스타트
네트워크는 한 번에 보낼 수 있는 데이터 양에 한계가 있다. 이를 효율적으로 관리하기 위해 TCP 슬로우 스타트(Slow Start) 알고리즘이 동작한다.- 작동 원리: 처음에는 아주 적은 양(예: 10MSS)의 데이터만 보낸다. 클라이언트로부터 수신 확인(ACK)을 받으면 전송량을 2배씩 늘려간다.
- 최적화 포인트: 만약 네트워크 혼잡으로 ACK이 오지 않으면 전송량을 다시 절반으로 줄인다. 이 메커니즘 때문에 초기 HTML 파일이 너무 크면 여러 번의 왕복이 필요하게 되어 로딩이 급격히 느려진다.
3. 구문 분석 (Parsing)
브라우저가 네트워크를 통해 데이터 청크를 받기 시작하면, 이를 즉시 DOM(Document Object Model)과 CSSOM(CSS Object Model)으로 변환하는 구문 분석(Parsing)에 돌입한다. 브라우저는 서버로부터 받은 바이트 데이터를 문자열로 변환하고, 이를 다시 토큰화하여 트리 구조의 객체 모델을 생성한다.
HTML 전체가 도착할 때까지 기다리지 않는다. 브라우저는 첫 패킷(약 14kb)이 도착하는 즉시 파싱을 시작한다. 따라서 첫 렌더링에 필요한 핵심 HTML과 CSS는 반드시 이 첫 14kb 안에 포함되어야 사용자에게 더 빠른 첫 화면을 선사할 수 있다.
DOM 트리 구축 (Building the DOM tree)
- 토큰화와 트리 생성: HTML 태그를 분석하여 토큰을 만들고, 이를 노드로 변환해 트리 구조를 형성한다. 이 루트 노드가 되며, 태그 간의 계층 관계가 반영된다.
- 논-블로킹 자원: 이미지나 CSS 같은 자원은 파싱을 중단하지 않는다. 브라우저는 요청만 보내놓고 파싱을 계속한다.
- 블로킹 자원 (Script): async나 defer가 없는
- 프리로드 스캐너 (Preload Scanner): 메인 스레드가 파싱에 집중하는 동안, 뒤에서 외부 자원(JS, CSS, 폰트 등)을 미리 찾아 요청하여 블로킹 시간을 줄여준다.
CSSOM 구축 (Building the CSSOM)
- 스타일 맵 변환: CSS는 '캐스케이드(Cascade)' 규칙 때문에 점진적으로 처리할 수 없다. 후속 규칙이 이전 규칙을 덮어쓸 수 있기 때문이다.
- 렌더링 차단: 브라우저는 모든 CSS를 수신하고 처리할 때까지 페이지 렌더링을 막는다. (Render-blocking) 따라서 CSS는 최대한 가볍게 유지해야 한다.
DOM과 CSSOM은 서로 독립적인 데이터 구조이므로, 브라우저는 HTML과 CSS를 각각 파싱하여 두 트리를 동시에 생성한다. 다음 과정인 렌더 트리를 형성하기 위해서는 두 결과물 모두 준비되어야 하므로 어느 한쪽의 지연이 전체 렌더링 성능에 영향을 주기도 한다.
4. 렌더(Render)
파싱된 DOM과 CSSOM이 합쳐져 화면에 실제 픽셀로 나타나는 과정이다.
스타일 (Style: Render Tree 생성)
- 결합: DOM 트리의 루트부터 순회하며 눈에 보이는 노드에만 CSSOM 규칙을 적용해 렌더 트리(Render Tree)를 만든다.
- 제외 대상:
display: none속성을 가진 요소나<head>,<script>태그 등은 렌더 트리에 포함되지 않는다. 반면, visibility: hidden은 공간을 차지하므로 포함된다.
레이아웃 (Layout / Reflow)
- 기하학적 계산: 렌더 트리를 기반으로 각 노드의 정확한 너비, 높이, 좌표를 계산한다.
- 뷰포트(Viewport) 고려: 기기의 화면 크기에 따라 박스 모델 속성을 계산한다. 뷰포트 메타 태그(
width=device-width)가 이 단계에서 중요한 역할을 한다. - 리플로우: 이후에 노드 크기나 위치가 변경되어 다시 계산하는 것을 리플로우라고 하며, 이는 성능에 큰 부담을 준다.
페인트 (Paint)
- 픽셀 변환: 레이아웃에서 계산된 박스들을 실제 화면의 픽셀로 그린다. 텍스트, 색상, 접경, 그림자 등 시각적인 모든 요소가 여기서 처리된다.
- 첫 번째 의미 있는 페인트(FMP): 사용자에게 콘텐츠가 보이기 시작하는 중요한 지점이다.
합성 (Compositing)
- 레이어 분리: 효율적인 렌더링을 위해 특정 요소들(, 3D transform 등)을 별도의 레이어로 분리해 CPU가 아닌 GPU에서 처리하게 한다.
- 최종 조립: 분리된 레이어들을 올바른 순서로 겹쳐서 화면을 완성한다. 레이어를 적절히 활용하면 리플로우가 발생해도 해당 레이어만 다시 그리면 되므로 성능이 비약적으로 향상된다.
5. 상호작용(Interactivity)
화면에 픽셀이 뿌리는 것 뿐만 아니라 사용자가 버튼을 클릭하거나 스크롤을 내릴 때, 브라우저가 즉각적으로 반응해야 비로소 로딩이 완료되었다고 할 수 있다.
TTI (Time to Interactive)
상호작용 가능한 시점(TTI)은 DNS 조회부터 시작해 페이지가 사용자의 입력에 50ms 이내로 응답할 수 있게 될 때까지의 시간을 의미한다.
- 응답의 조건: 첫 번째 콘텐츠가 포함된 페인트(FCP)가 발생한 이후, 메인 스레드가 완전히 자유로워져서 사용자의 조작에 즉각 반응할 수 있어야 한다.
- 사용자 경험의 괴리: 화면은 완벽하게 렌더링되어 있는데, 정작 클릭이나 스크롤이 먹통인 상태를 흔히 경험한다. 이는 사용자에게 큰 불쾌감을 주는 '성능의 함정'이다.
메인 스레드의 점유와 병목
브라우저의 메인 스레드는 렌더링뿐만 아니라 JavaScript의 구문 분석, 컴파일, 실행까지 모두 담당한다.
- JavaScript의 무거운 짐: 예를 들어 2MB 크기의 거대한 anotherscript.js가 뒤늦게 다운로드되어 실행된다면, 메인 스레드는 이 코드를 처리하느라 다른 일을 할 수 없게 된다.
- 버벅임(Jank): 메인 스레드가 자바스크립트 실행에 묶여 있으면 스크롤은 끊기고, 버튼 클릭은 무시된다.
정리
사용자가 URL을 입력하고 화면과 상호작용하기까지의 과정은 **'데이터 확보 → 구조화 → 시각화'**의 연속이다. 각 단계에서 발생하는 지연 시간을 줄이는 것이 웹 성능 최적화의 핵심이다.
- 렌더링 과정 요약
| 단계 | 핵심 동작 | 주요 특징 |
|---|---|---|
| 1. 탐색 (Navigation) | DNS 조회, TCP/TLS 핸드셰이크 | 서버와의 신뢰 구축을 위한 여러 번의 왕복(Round Trip) 발생 |
| 2. 응답 (Response) | HTTP 요청 및 데이터 수신 | 초기 14kb 패킷의 중요성, TCP 슬로우 스타트 알고리즘 작동 |
| 3. 구문 분석 (Parsing) | DOM & CSSOM 트리 구축 | HTML은 점진적 파싱, CSS는 전체 수신 전까지 렌더링 차단 |
| 4. 렌더 (Render) | 레이아웃, 페인트, 합성 | 렌더 트리 생성 후 기하학적 계산 및 픽셀 출력 (GPU 활용) |
| 5. 상호작용 (Interactivity) | TTI 확보 및 이벤트 반응 | 메인 스레드가 자유로워져 사용자 입력에 50ms 내 응답 가능 |
- 성능 최적화
- 초기 14kb의 승부: 첫 번째 응답 패킷에 페이지 구동에 필수적인 HTML 구조와 크리티컬 CSS를 포함시켜야 첫 화면(FCP)이 빨라진다.
- CRP(Critical Rendering Path) 단축
- 중요하지 않은 자원 다운로드를 미루거나 비동기로 적용하거나 제거(중요 자원 수 최소화)
- 각 요청에 대한 파일 사이즈를 줄이고, 필요한 요청 횟수 관리(요청 횟수 최적화)
- 다운로드할 중요 자원에 우선 순위를 부여하여, 렌더링에 필요한 자원이 가장 먼저 로드되도록 관리하기(중요한 자원을 불러오는 순서를 최적화)
- 메인 스레드 방어: 브라우저는 싱글 스레드로 동작하므로, 무거운 자바스크립트 실행이 렌더링이나 사용자 반응을 방해하지 않도록
defer나async를 적절히 활용해야 한다.