Index
① 렌더링 과정
② 자바스크립트 파싱과 HTML 파싱 중단
🧐 Q. 브라우저는 어떠한 과정을 거쳐서 렌더링을 진행할까?
- 브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 파일 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답받는다.
- 브라우저 렌더링 엔진은 서버로부터 응답된 HTML, CSS를 파싱하여 DOM과 CSSOM을 생성하고 이들을 결합하여 렌더트리를 생성한다.
- 브라우저 자바스크립트 엔진은 서버로부터 응답된 자바스크립트를 파싱하여 AST(Abstract Syntax Tree)를 생성하고 바이트코드로 변환하여 실행한다. 이때 자바스크립트는 DOM API를 통해 DOM이나 CSSOM을 변경할 수 있다. 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합된다.
- 렌더 트리를 기반으로 HTML 요소의 레이아웃(위치와 크기)을 계산하고 브라우저 화면에 HTML 요소를 페인팅한다.
1. 요청과 응답
- 서버는 어떤 창고이다. 창고 안에는
index.html,style.css,logo.png같은 파일들이 선반에 잘 정리되어 있다. - 우리는(브라우저를 쓰는 사용자)은 그 창고에 “이 주소의 상품(파일) 주세요!”하고 요청을 보낸다.
- 서버는 선반에서 그 파일을 꺼내서 우리한테 보여준다.
2. HTTP 1.0과 HTTP 2.0
HTTP(HyperText Transfer Protocol)는 웹에서 브라우저와 서버가 통신하기 위한 프로토콜(규약)이다.
Difference of HTTP
Ungmo Lee. (2020). Modern Javascript DeepDive. wikibooks. p.664.
3. 렌더트리 생성
Creation of RenderTree
Ungmo Lee. (2020). Modern Javascript DeepDive. wikibooks. p.669.
브라우저의 렌더링 과정은 반복해서 실행될 수 있다. 예를 들어, 다음과 같은 경우 반복해서 레이아웃 계산과 페인팅이 재차 실행된다.
- 자바스크립트에 의한 노드 추가 또는 삭제
- 브라우저 창의 리사이징에 의한 뷰포트(viewport) 크기 변경
- HTML 요소의 레이아웃(위치, 크기)에 변경을 발생시키는
width,height,margin,padding,border,display,position top/right/bottom/left
4. 자바스크립트 파싱에 의한 HTML 파싱 중단
4.1 DOMContentLoaded 이벤트를 사용한 방식
스크립트가 <head>에 있어 HTML 요소가 아직 파싱되기 전 실행됨. getElementById('apple')가 null을 반환해 에러 발생.
<head>
<script>
const $apple = document.getElementById('apple');
$apple.style.color = 'red'; // ❌ 오류 발생!
</script>
</head>
<body>
<ul>
<li id="apple">Apple</li>
...
</ul>
</body>
4.2 <script>를 <body> 끝에 배치하는 방식
스크립트가 <body> 끝에 있어 HTML 요소 파싱이 끝난 뒤 실행됨. 요소를 정상적으로 가져와 스타일 변경이 성공함.
<body>
<ul>
<li id="apple">Apple</li>
...
</ul>
<script>
const $apple = document.getElementById('apple');
$apple.style.color = 'red'; // ✅ 정상 동작!
</script>
</body>
4.3 async 어트리뷰트
HTML 파싱 중에도 JS를 병렬로 다운로드하고, 다운로드가 끝나면 즉시 실행 (HTML 파싱이 중단됨) → DOM 조작에는 위험 / 순서 보장 없음
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="async-script.js" async></script>
</head>
<body>
<h1>Async Script Example</h1>
<p id="message">Hello</p>
</body>
</html>
📂 async-script.js:
// 이 스크립트는 HTML이 아직 파싱 중일 수도 있는 시점에 실행됨
document.getElementById('message').textContent = 'Changed by async script';
❌ 문제 가능성: #message 요소가 아직 파싱되지 않았다면 null 에러 발생
4.4 defer 어트리뷰트
HTML 파싱과 동시에 JS를 비동기로 다운로드, DOM 생성이 완료된 후 실행 → DOM 접근 안전함 / 스크립트 순서 보장
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="defer-script.js" defer></script>
</head>
<body>
<h1>Defer Script Example</h1>
<p id="message">Hello</p>
</body>
</html>
📂 defer-script.js:
// 이 시점에서는 DOM이 모두 파싱 완료되어 안전하게 요소에 접근 가능
document.getElementById('message').textContent = 'Changed by defer script';
✅ 안전함: DOM이 완성된 후 실행되므로 에러 없이 실행됨