Javascript

브라우저는 HTML을 어떻게 불러올까? DOM 생성부터 페이지 종료까지 전 과정을 알아봅니다

드리프트2 2025. 4. 12. 11:21

 

브라우저는 HTML을 어떻게 불러올까? DOM 생성부터 페이지 종료까지 전 과정을 알아봅니다

 

웹페이지 하나가 눈앞에 보이기까지, 브라우저 안에서는 정말 많은 일이 일어나고 있습니다.

 

단순히 HTML 파일 하나를 불러오는 게 아니라, 내부에 있는 수많은 요소들과 외부 리소스를 파싱하고 실행하면서 페이지가 완성되는데요. 이 복잡한 과정을 HTML의 생명주기(Lifecycle)라고 부를 수 있습니다.

 

HTML 자체는 자바스크립트처럼 생명주기 훅(hook)을 직접 제공하지 않지만, 브라우저가 HTML을 어떻게 읽고 처리하는지, 그리고 어떤 이벤트가 발생하는지를 이해하면 더 나은 사용자 경험과 성능을 만들 수 있습니다.

 

이번 글에서는 HTML이 브라우저에서 어떤 식으로 처리되는지 단계별로 알아보겠습니다.

 

1. HTML 파싱: 브라우저는 HTML을 어떻게 읽을까?

웹페이지가 처음 로딩될 때, 브라우저는 서버에서 HTML 파일을 받아와서 파싱(parsing)을 시작합니다.

 

이 과정에서 DOM 트리(DOM Tree) 라는 구조가 만들어지는데요.

 

이 트리는 JavaScript나 CSS가 HTML 요소를 조작할 수 있도록 브라우저가 내부적으로 HTML을 객체처럼 변환한 결과입니다.

 

이 단계의 특징은요:

  • 아직 외부 리소스(CSS, 이미지 등)는 완전히 불러오지 않은 상태입니다.
  • JavaScript로 이 파싱 과정을 직접 감지하거나 중단시킬 수는 없습니다.
  • 하지만 HTML 구조를 깔끔하게 정리하고, 불필요한 blocking 리소스를 줄이면 전체 파싱 속도를 개선할 수 있습니다.

 

2. 외부 리소스 로딩: CSS와 JavaScript는 언제 불러올까?

브라우저가 HTML을 읽다가 <link><script> 같은 외부 리소스를 만나면, 각각의 리소스 종류에 따라 다른 방식으로 처리합니다.

CSS 로딩

  • <link> 태그를 만나면 CSS 파일을 불러오고 파싱할 때까지 렌더링을 중단합니다.
  • CSS는 스타일을 적용하는 데 필수이기 때문에, 안 불러오면 페이지가 망가질 수 있죠.
  • 그래서 CSS는 렌더링 차단 요소(render-blocking resource) 로 분류됩니다.

JavaScript 로딩

  • <script> 태그는 기본적으로 HTML 파싱을 멈추고 스크립트를 먼저 불러와 실행합니다.
  • 이 동작을 동기적(synchronous) 이라고 하는데요, 이 때문에 DOMContentLoaded 이벤트가 느려질 수 있습니다.
  • <script defer><script async>를 사용하면 이 지연을 줄일 수 있습니다.

이처럼 외부 리소스가 어떤 방식으로 불러와지는지에 따라 페이지 전체의 로딩 속도와 반응성이 크게 달라집니다.

 

3. readyState와 readystatechange: 문서 상태 확인하기

HTML 문서나 AJAX 요청이 어느 단계에 있는지 확인하려면 document.readyState 속성과 readystatechange 이벤트를 활용할 수 있습니다.

document.readyState의 값

  • loading: HTML이 아직 파싱 중일 때
  • interactive: HTML 파싱이 끝나고 DOM 트리 생성은 완료된 상태 (이미지나 스타일시트는 불완전할 수 있음)
  • complete: 모든 HTML, CSS, JavaScript, 이미지까지 전부 로딩 완료
if (document.readyState === 'complete') {
  // 페이지가 완전히 로딩된 상태
}

readystatechange 이벤트

이 이벤트는 readyState 값이 바뀔 때마다 발생합니다.

 

이를 통해 DOM 조작 타이밍을 조절할 수 있죠.

document.addEventListener('readystatechange', function () {
  if (document.readyState === 'interactive') {
    console.log('DOM 트리 생성 완료');
  } else if (document.readyState === 'complete') {
    console.log('모든 리소스 로딩 완료');
  }
});

 

 

4. DOMContentLoaded: DOM이 준비됐을 때 실행되는 이벤트

DOMContentLoaded는 HTML 파싱이 끝나고 DOM 트리가 생성됐을 때 발생하는 이벤트입니다.

 

이 시점에서는 이미지나 스타일시트 같은 외부 리소스는 아직 다 불러오지 않았을 수 있습니다.

document.addEventListener('DOMContentLoaded', () => {
  console.log('DOM이 준비됐습니다.');
});

예시: 이미지가 있는 페이지에서 DOMContentLoaded 이벤트는 이미지가 다 불러지기 전에 발생합니다.

function ready() {
  const img = document.querySelector('#img');
  console.log(`이미지 크기: ${img.offsetWidth}x${img.offsetHeight}`);
}

document.addEventListener('DOMContentLoaded', ready);

 

이 코드 실행 시점에는 이미지가 아직 로딩되지 않았기 때문에, 이미지 크기는 0x0으로 출력될 수 있습니다.

 

5. window.onload: 모든 리소스가 다 불러졌을 때 실행되는 이벤트

window.onload는 HTML뿐 아니라 CSS, 이미지, 폰트, 비디오 등 모든 외부 자원까지 다 불러진 다음에 발생합니다.

window.onload = function () {
  console.log('페이지 로딩 완료');
  console.log(`이미지 크기: ${img.offsetWidth}x${img.offsetHeight}`);
};

 

이 경우에는 이미지도 완전히 로딩된 상태이기 때문에, 실제 크기가 정확히 출력됩니다.

 

6. beforeunload 이벤트: 페이지를 떠나기 전 사용자에게 알림

 

beforeunload 이벤트는 사용자가 페이지를 떠나기 직전에 발생합니다.

 

예를 들어, 작성 중인 글을 저장하지 않고 나가려고 할 때 경고 메시지를 띄울 수 있죠.

window.onbeforeunload = function () {
  return false; // 사용자에게 떠날 건지 확인하는 메시지 표시
};

 

현대 브라우저는 보안과 사용자 경험을 위해 사용자 정의 메시지를 무시하고, 표준화된 경고창만 보여줍니다.

 

너무 자주 사용하면 오히려 불편함을 줄 수 있으니, 꼭 필요한 경우에만 쓰는 게 좋습니다.

 

7. unload 이벤트: 페이지가 완전히 종료될 때

 

unload 이벤트는 페이지가 완전히 닫히거나 다른 페이지로 이동할 때 발생합니다.

 

이 이벤트는 사용자에게 메시지를 띄울 수 없고, 단순히 정리(clean-up) 작업에 사용됩니다.

사용 예시:

  • 비동기 요청 취소
  • 메모리 정리
  • 웹소켓 연결 종료
  • localStorage 저장
  • 분석 데이터 전송
window.addEventListener('unload', function () {
  navigator.sendBeacon('/analytics', JSON.stringify(analyticsData));
});

 

sendBeacon()은 페이지가 종료되는 순간에도 데이터를 서버로 전송할 수 있어, 분석 도구에서 자주 사용됩니다.

 

정리하며

 

브라우저가 HTML 문서를 불러오고 사용자에게 보여주기까지는 다양한 단계가 있는데요.

 

이를 정리해보면 다음과 같습니다:

  • HTML 파싱: DOM 트리 생성
  • DOMContentLoaded: DOM 생성 후 발생, 외부 리소스는 아직 로딩 중일 수 있음
  • load: 모든 리소스 로딩 완료 후 발생
  • beforeunload: 페이지 이탈 직전 사용자에게 경고
  • unload: 페이지 완전히 종료되며 정리 작업 진행

이 생명주기를 이해하고 활용하면, 사용자에게 더 빠르고 안정적인 웹 경험을 제공할 수 있습니다.

 

특히 DOMContentLoaded와 load 이벤트를 잘 구분하고, 외부 리소스 로딩 방식을 최적화하면 성능 차이가 크게 나타날 수 있습니다.