브라우저는 단순히 HTML 파일을 보여주는 도구가 아니다.
네트워크, 렌더링 엔진, 자바스크립트 엔진이 유기적으로 협력해 웹페이지를 보이는 형태로 만드는 복잡한 시스템이다.
1. 브라우저의 렌더링 프로세스
웹페이지가 화면에 표시되기까지의 과정은 다음과 같다.
1. 리소스 요청과 응답
브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받는다. 이 과정은 주소창에서 시작된다. 브라우저의 주소창에 URL을 입력하고 엔터를 누르면, URL의 호스트 이름이 DNS를 통해 IP 주소로 변환되고, 이 IP 주소를 가진 서버로 요청이 전송된다.
요청 전송 후 응답을 받아오기까지 네트워크에서 일어나는 일들 알아보기
2. HTML, CSS 파싱과 렌더 트리(Render Tree) 생성
렌더링 엔진은 서버로부터 받은 HTML과 CSS를 파싱하여 DOM(Document Object Model) 과 CSSOM(CSS Object Model) 을 생성하고, 이를 결합해 렌더 트리(Render Tree) 를 만든다.
(1) HTML 파싱 → DOM 생성

HTML 문서는 단순한 문자열이다. 브라우저가 이를 이해하려면 다음 단계를 거쳐 자료구조로 변환해야 한다.
바이트 → 문자 → 토큰 → 노드 → DOM
이 과정에서 브라우저는 토큰(Token) 을 생성한다. 이 토큰은 HTML 파서가 문자를 읽어 들이며 만들어내는, 문법적으로 의미 있는 최소 단위다. 각 토큰은 노드로 변환되고, 이 노드들이 모여 DOM 트리를 구성한다. 즉, DOM은 HTML 문서를 파싱한 결과물이다.
(2) CSS 파싱 → CSSOM 생성

HTML 파싱과 유사하게, CSS도 파싱 과정을 거쳐 CSSOM으로 변환된다. CSS 파일은 link 태그의 href 속성을 통해 로드하거나, style 태그 내부의 CSS를 직접 파싱하여 처리한다.
바이트 → 문자 → 토큰 → 노드 → CSSOM
(3) 렌더 트리 생성 (DOM + CSSOM)
렌더 트리는 각 HTML 요소의 레이아웃(위치와 크기) 계산과 브라우저가 실제로 화면에 그리는 페인팅(Painting) 단계의 기초가 된다.

이때 렌더 트리를 구성하기 위해선 각 요소의 스타일 계산이 필요하다. 요소는 인라인 스타일, CSS 파일, HTML 속성(bgcolor 등)에서 스타일을 가져올 수 있으며, 모두 CSS 속성으로 변환되어 계산된다.
하지만 이 과정은 복잡하다. 규칙이 많고, 계층 구조를 고려해야 하며, 잘못된 선택자 탐색은 성능 저하로 이어진다. 예를 들어, div div div div { ... } 같은 복합 선택자는 깊은 탐색을 유발한다. 이를 해결하기 위해 브라우저 엔진은 *스타일 공유 최적화를 한다.
*스타일 공유 최적화
브라우저는 화면을 그릴 때, 모든 요소에 대해 ‘스타일 계산’을 수행한다. 예를 들어 다음처럼 <li>가 100개 있는 경우를 생각해보자.
<ul>
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
...
</ul>
모든 <li>가 같은 클래스 .item을 가지고, 같은 CSS 규칙을 적용받는다면 사실상 100개의 <li>는 동일한 스타일 정보를 공유할 수 있다.
이때 매번 같은 스타일을 다시 계산하면 불필요한 연산과 메모리 낭비가 발생한다.
그래서 브라우저 엔진(WebKit, Blink 등)은 이를 최적화하기 위해 “RenderStyle 객체”를 공유하는 방식을 사용한다.
RenderStyle 객체란?
RenderStyle은 브라우저가 실제로 화면을 그릴 때 참조하는 스타일 데이터 구조다. 즉, CSS 규칙이 해석된 후 최종적으로 “이 요소는 어떤 색상, 폰트, 마진을 가진다” 라는 계산 결과를 담고 있는 객체라고 볼 수 있다.
공유 조건
브라우저는 아래 조건이 모두 일치하면 RenderStyle을 공유한다.
| 조건 | 의미 |
| 동일한 마우스 반응 상태 | 예: 둘 다 :hover 상태가 아님 |
| ID가 없음 | ID는 고유해야 하므로 공유 불가 |
| 태그명, 클래스, 속성이 동일 | 예: <div class="box" data-x="1"> 같은 구조 |
| 인라인 스타일 없음 | <div style="color:red">처럼 개별 스타일은 공유 불가 |
| 형제 선택자(+, :first-child, :last-child)가 없음 | 형제 관계에 따라 다른 스타일이 적용될 수 있기 때문 |
왜 중요한가?
동일한 요소끼리는 한 번 계산한 스타일을 재사용할 수 있다. 그 결과 렌더링 속도가 빨라지고, 메모리 사용량이 줄어든다.
3. 레이아웃과 페인팅
렌더 트리를 기반으로 요소의 위치(Layout) 와 크기(Size) 를 계산한 뒤, 화면에 픽셀을 그리는 페인팅(Painting) 단계를 거친다.
리플로우(Reflow)와 리페인트(Repaint)
자바스크립트가 DOM API 를 이용해 스타일을 변경할 때 발생한다(4.자바스크립트 파싱과 실행 내용 참고).
- 리플로우: 리플로우는 요소의 크기나 위치 변경, 노드 추가/삭제 등으로 레이아웃을 다시 계산하는 과정으로, 연쇄적으로 다른 요소까지 영향을 미쳐 비용이 크다.
- 리페인트: 리페인트는 시각적 스타일 변경 시 화면을 다시 그리는 과정으로, 반복되면 렌더링 속도를 늦추고 CPU/GPU 부담을 늘린다.

이처럼 리플로우와 리페인트는 모두 성능에 영향을 주기 때문에 최대한 횟수를 줄이는 것이 중요하다.
잦은 리플로우와 리페인트는 페이지 로딩 지연, 스크롤 끊김, 애니메이션 지연 등 사용자 경험 저하로 이어진다. 따라서 최소화하면 페이지가 빠르고 부드럽게 동작한다.
그리고 리플로우가 리페인트보다 비용이 더 큰데, 이유는 리플로우는 레이아웃 계산을 다시 수행해야 하기 때문에, 변경된 요소뿐만 아니라 그 자식 요소와 형제 요소까지 영향을 줄 수 있다. 반면 리페인트는 단순히 요소의 스타일이나 색상 등 시각적 속성을 다시 그리는 작업이므로 계산 범위가 제한적이다.
리플로우와 리페인트를 최소화하는 방법
리플로우 최소화
- 여러 DOM 변경은 한 번에 적용: DocumentFragment 사용하거나 변수에 모아 한 번에 추가/수정
- DOM 구조 변경 최소화: 노드 추가/삭제를 반복하지 않고 한 번에 처리
- 레이아웃 변경 속성 사용 최소화: width, height, top, left 같은 속성 변경 자제
- CSS 클래스 토글 활용: 인라인 스타일보다는 클래스 변경으로 스타일 적용
리페인트 최소화
- 시각적 스타일 변경을 최소화: 색상, 배경 등 변경 시 범위를 좁게 적용
- 투명한 요소나 화면 외부 요소 변경 피하기
- GPU 가속 활용: transform, opacity 속성을 이용해 애니메이션 처리
즉, DOM 구조 변경과 스타일 변경을 모아서 한 번에 처리하고, 인라인 스타일보다는 클래스나 GPU 가속 속성을 활용하면 두 비용을 모두 줄일 수 있다.
주의: 자바스크립트 실행 시점
자바스크립트가 실행될 때는 이미 렌더링 엔진이 HTML을 모두 파싱해 DOM 생성을 끝낸 상태여야 한다. 그렇지 않으면 DOM 조작 시 에러가 발생할 수 있다. 또한 DOM이 완성된 후 스크립트를 실행하면 렌더링이 지연되지 않아 페이지 로딩 속도가 빨라지는 장점이 있다.
4. 자바스크립트 파싱과 실행
자바스크립트 엔진은 서버에서 받은 스크립트를 파싱 → 바이트코드 변환 → 실행한다.
- 자바스크립트를 파싱하여 추상 구문 트리(AST, Abstract Syntax Tree) 를 만든다.
- AST를 인터프리터가 실행할 수 있는 중간 코드(바이트코드) 로 변환한다.
- 이 과정에서 DOM API를 사용해 DOM이나 CSSOM을 조작할 수 있다(리플로우 또는 리페인트 발생).
- 변경된 내용은 다시 렌더 트리에 반영된다.

| 용어 | 설명 |
| 토크나이징 (Tokenizing) | 자바스크립트 소스코드를 어휘 분석하여 문법적으로 의미 있는 최소 단위(토큰) 로 분해하는 과정 |
| 파싱 (Parsing) | 토큰 집합을 구문 분석하여 AST(Abstract Syntax Tree) 를 생성하는 과정 |
| 바이트코드 변환 | AST를 인터프리터가 실행할 수 있는 중간 코드(바이트코드) 로 변환하는 과정 |
2. 정리 및 마무리
브라우저 동작의 구조와 흐름을 이해하면 웹 애플리케이션 성능을 개선하고, 예상치 못한 동작을 디버깅하는 데 큰 도움이 된다. 결국 브라우저 내부 동작을 이해하는 것은 단순히 페이지를 띄우는 것을 넘어, 효율적이고 사용자 친화적인 웹 개발의 핵심 기반이 된다.
'Computer Science > Web Development' 카테고리의 다른 글
| JWT(Json Web Token) (0) | 2025.10.17 |
|---|---|
| SPA, CSR, SSR, 그리고 Hybrid Rendering — 웹 렌더링의 진화 (0) | 2025.10.17 |
| z-index가 같아도 겹치는 순서가 달라지는 이유 (0) | 2025.10.16 |
| Document Object Model(DOM) (0) | 2025.10.16 |
| 브라우저의 구조와 웹 스토리지 (0) | 2025.10.15 |