문서와 리소스 로딩

345·2023년 7월 27일

✅ 문서 로딩과 이벤트

브라우저는 다음과 같은 과정을 거쳐 HTML 문서를 보여줍니다.

  1. 브라우저가 HTML 을 읽고 DOM 트리 구성
  2. DOM 트리를 다 완성하면 이미지나 스타일시트 같은 외부 자원 불러옴
  3. 자원을 다 불러오면 문서 로딩 완료!

이 과정 중에 발생하는 이벤트는 다음과 같습니다.

  • DOMContentLoaded: DOM 트리 완성 즉시 발생, 기타 자원의 로딩은 기다리지 ❌
  • load: DOM 트리도 완성되고, 외부 자원도 모두 불러오는 게 끝나면 발생
  • beforeunload: 사용자가 다른 페이지로 이동하려 하거나 창을 닫으려 할 때 발생
  • unload: 사용자가 페이지를 떠날 때(문서를 완전히 닫을 때) 발생

각 이벤트는 다음과 같은 의미를 가집니다.

  • DOMContentLoaded: DOM 이 다 만들어졌으므로 원하는 DOM 노드 참조 가능
  • load: 스타일이 적용된 상태이므로 화면이 나타난 요소의 실제 크기 확인 가능
  • beforeunload: 사용자에게 변경 사항 저장 등을 확인함
  • unload: 사용자 분석 정보를 전송할 때 사용

document 의 DOMContentLoaded

DOMContentLoaded 이벤트는 document 객체에서 발생하며
문서가 로드되었을 때 핸들러가 실행됩니다.

DOMContentLoaded 이벤트에는 몇 가지 특징이 있습니다.

  • <script> 안 스크립트가 처리되고 난 후에 이벤트 발생 (스크립트가 이벤트를 막음)
    • 브라우저가 <script> 를 만나면 DOM 트리 구성을 멈추기 때문
  • 외부 스타일시트의 로드를 기다리지 않음
    • 스타일시트는 DOM 에 영향을 주지 않기 때문
    • 중간에 <script> 가 있는 경우, 스크립트에서 스타일을 사용할 수도 있으므로 스타일시트가 다 로드되어야 스크립트 실행
  • 브라우저의 자동 완성 기능은 DOMContentLoaded 이벤트가 발생할 때 실행됨

readyState

문서가 완전히 로드된 후에 설정된 DOMContentLoaded 핸들러는 실행되지 않습니다.
DOMContentLoaded 이벤트가 발생하지 않기 때문이죠.

하지만 문서가 로드되었는지 아닌지 상태는 document.readyState 프로퍼티로 확인할 수 있습니다.
프로퍼티는 다음과 같은 세 종류의 값을 가집니다.

  • loading: 문서를 불러오는 중일 때
  • interactive: 문서가 완전히 불러와졌을 때
  • complete: 문서를 비롯한 이미지 등의 리소스들도 모두 불러와졌을 때

이렇게 프로퍼티의 값을 읽고 상황에 따라 동작을 제시하면 됩니다.

function work() { /*...*/ }

if (document.readyState == 'loading') {
  // 아직 로딩 중이므로 이벤트를 기다립니다.
  document.addEventListener('DOMContentLoaded', work);
} else {
  // DOM이 완성되었습니다!
  work();
}

window 의 load, unload, beforeunload

load, unload, beforeunload 이벤트는 window 객체에서 발생합니다.
onload / onunload / onbeforeunload 프로퍼티로 핸들러를 등록할 수 있습니다.

window.onbeforeunload

window.onbeforeunload = function() {
  return false;
};

이렇게 false 를 반환하거나 addEventListener 로 등록한 핸들러에서 event.preventDefault() 를 호출하면 사용자에게 떠날 것인지 묻는 확인 메시지를 표시합니다.


⚡ defer, async 스크립트

스크립트들은 HTML 보다 양이 많아 다운로드에 시간이 오래 걸리죠.
브라우저가 <script> 태그를 만나면 DOM 생성을 멈춰야 한다는 점에서 다음과 같은 문제가 발생합니다.

  • 스크립트는 스크립트 이후의 DOM 요소(아직 DOM 트리에 포함되지 않음)에 접근 ❌
  • 용량이 큰 스크립트가 중간에 있는 경우 페이지 로딩이 오래 걸림 (사용자가 계속 기다려...)

이런 문제를 피하기 위한 가장 간단한 방법은 스크립트를 body 의 맨 아래에 놓는 것입니다.

<body>
  ... 스크립트 위 콘텐츠들 ...

  <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>

이러면 스크립트 내에서 다른 DOM 요소에 자유롭게 접근할 수 있고, 스크립트를 다운받기 전에 페이지를 보여줄 수 있으니까요.
하지만 문서 다운 이후 또 스크립트 다운 과정을 거치느라 전체 리소스 다운까지 완료되는 시간이 매우 길어질 겁니다.

이런 문제는 <script> 태그에 deferasync 속성을 추가하여 해결합니다.


defer

브라우저는 defer 속성이 있는 스크립트는 백그라운드에서 다운합니다.
따라서 스크립트 다운 중에서 HTML 파싱을 멈추지 않습니다.
다만 스크립트는 페이지 구성이 끝난 후 실행됩니다. (그 전까진 실행 지연)

<script defer src="..."></script>

이를 지연 스크립트라고 하며 DOM 이 준비된 후, DOMContentLoaded 이벤트 발생 전에 실행됩니다.
DOMContentLoaded 이벤트가 지연 스크립트가 실행되길 기다리기 때문입니다.
DOM 트리가 완성되고 지연 스크립트가 실행된 후에 DOMContentLoaded 이벤트가 발생합니다.

  • DOM 생성을 막지 않음
  • DOM 완료 후에 실행됨, 그 전까진 실행 지연
  • DOMContentLoaded 이벤트는 DOM 완료, 지연 스크립트 실행 후에 발생
  • 스크립트는 문서 내 순서대로 실행

async

async 속성이 붙은 스크립트는 비동기 스크립트로, 역시나 백그라운드에서 다운로드됩니다.
따라서 브라우저는 비동기 스크립트가 다운되는 동안 DOM 을 생성하고 콘텐츠를 처리, 출력할 수 있습니다.

defer 와 달리, async 스크립트와 DOMContentLoaded 는 서로를 기다리지 않습니다.
비동기 스크립트 실행 전에 이벤트가 발생할 수도, 실행 후에 발생할 수도 있죠.

  • DOM 생성을 막지 않음
  • DOM 이 완료되지 않아도, 스크립트 다운 완료시 즉시 실행
  • DOMContentLoaded 이벤트 발생 전, 후에 상관 없이 실행
  • 먼저 다운로드된 스크립트를 먼저 실행

profile
기록용 블로그 + 오류가 있을 수 있습니다🔥

0개의 댓글