본문 바로가기

Language/Javascript

[Javascript] DOM 렌더링 시점

DOM 렌더링
  • 브라우저가 HTML 문서를 파싱하여 DOM 트리를 생성하고 이를 기반으로 화면에 문서를 그리는 과정을 의미
  • HTML 문서의 생명주기엔 다음과 같은 3가지 주요 이벤트가 관여
    • DOMContentLoaded
      • 브라우저가 HTML을 전부 읽고 DOM 트리를 완성하는 즉시 발생
      • 이미지 파일(<img>)이나 스타일시트 등의 기타 자원은 기다리지 않음
      • 단, HTML 문서에 <script> 태그가 포함되어 있고 defer나 async가 설정되지 않은 경우 스크립트 실행이 완료된 이후에 발생
    • load
      • HTML로 DOM 트리를 만드는 게 완성되었을 뿐만 아니라 이미지, 스타일시트 같은 외부 자원도 모두 불러오는 것이 끝났을 때 발생
      • 일반적으로 이 이벤트는 페이지가 완전히 렌더링된 후 수행해야 할 초기화 작업에 사용
    • beforeunload/unload
      • 사용자가 페이지를 떠날 때 발생
생명주기 이벤트 사용 상황
  • DOMContentLoaded
    • DOM이 준비된 것을 확인한 후 원하는 DOM 노드를 찾아 핸들러를 등록해 인터페이스를 초기화할 때
  • load
    • 이미지 사이즈를 확인할 때 등. 외부 자원이 로드된 후이기 때문에 스타일이 적용된 상태이므로 화면에 뿌려지는 요소의 실제 크기를 확인 가능
  • beforeunload
    • 사용자가 사이트를 떠나려 할 때, 변경되지 않은 사항들을 저장했는지 확인시켜줄 때
  • unload
    • 사용자가 진짜 떠나기 전에 사용자 분석 정보를 담은 통계자료를 전송하고자 할 때

DOMContentLoaded
  • DOMContentLoaded 이벤트는 document 객체에서 발생
  • 따라서 이 이벤트를 다루려면 addEventListener 사용
예제 코드
  • DOMContentLoaded 핸들러는 문서가 로드되었을 때 실행되기 때문에 핸들러 아래쪽에 위치한 <img> 태그 뿐만 아니라 모든 요소에 접근 가능
  • 하지만, 이미지 로드 되는 것을 기다리는 것이 아니기 때문에 사이즈가 0x0으로 표시 됨
  • 실행 순서
    1. DOM이 준비되었습니다!
    2. 이미지 사이즈 : 0x0
    3. 이미지 로드
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <script>
      function ready() {
        alert('DOM이 준비되었습니다!');
    
        // 이미지가 로드되지 않은 상태이기 때문에 사이즈는 0x0입니다.
        alert(`이미지 사이즈: ${img.offsetWidth}x${img.offsetHeight}`);
      }
    
      document.addEventListener("DOMContentLoaded", ready);
    </script>
    
    <img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
    </body>
</html>

 

DOMContentLoaded와 scripts
  • 브라우저는 HTML 문서를 처리하는 도중에 <script> 태그를 만나면 DOM 트리 구성을 멈추고 <script>를 실행
  • 스크립트 실행이 끝난 후에야 나머지 HTML 문서를 처리
  • <script>에 있는 스크립트가 DOM 조작 관련 로직을 담고 있을 수 있기 때문에 이런 방지책이 만들어졌음
  • 따라서 DOMContentLoaded 이벤트 역시 <script> 안에 있는 스크립트가 처리되고 난 후에 발생
  • 실행 순서
    1. 브라우저가 <script> 태그를 만나면 DOM 구성을 멈추고 해당 스크립트를 실행
    2. 첫 번째 <script>
      • DOMContentLoaded 이벤트 리스너를 등록하지만, 아직 실행되지 않음
      • 이 이벤트는 DOM 구성이 끝난 후 발생
    3. 두 번째 <script>
      • 외부 스크립트 파일(lodash.js)을 다운로드하고 실행
      • 다운로드가 완료될 때까지 다음 스크립트는 실행되지 않음
    4. 세 번째 <script>
      • 외부 스크립트 실행 후 실행
      • "라이브러리 로딩이 끝나고 인라인 스크립트가 실행되었습니다."라는 알림 발생
    5. DOM 구성이 완료되면 DOMContentLoaded 이벤트가 발생
      • "DOM이 준비되었습니다!"라는 알림 발생
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        document.addEventListener("DOMContentLoaded", () => {
          alert("DOM이 준비되었습니다!");
        });
      </script>
      
      <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"></script>
      
      <script>
        alert("라이브러리 로딩이 끝나고 인라인 스크립트가 실행되었습니다.");
      </script>
</head>
<body>
</body>
</html>

 

DOMContentLoaded와 styles
  • 외부 스타일시트는 DOM에 영향을 주지 않기 때문에 DOMContentLoaded는 외부 스타일시트가 로드되기를 기다리지 않지만 한 가지 예외가 존재
  • 스타일시트를 불러오는 태그(<link>) 바로 다음에 스크립트(<script>)가 위치하면 이 스크립트는 스타일시트가 로드되기 전까지 실행되지 않음
    • 스크립트에서 스타일(예: getComputedStyle)에 의존하는 작업을 수행할 가능성이 있기 때문에 발생하는 제약
  • 스타일시트 로드와는 무관하게, 모든 스크립트가 실행된 후에야 DOMContentLoaded 이벤트가 발생
  • 아래 예제와 같은 상황에서는 스타일시트가 로드되길 기다리므로, 자연스럽게 DOMContentLoaded도 스타일 로드 완료 이후에 발생
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
    <link type="text/css" rel="stylesheet" href="style.css">
    <script>
        alert(getComputedStyle(document.body).marginTop);
    </script>
</body>
</html>

 

브라우저 내장 자동완성
  • Firefox, Chrome, Opera의 폼 자동완성(form autofill)은 DOM 트리가 완성된 후 (DOMContentLoaded) 자동 완성 처리
  • 페이지에 아이디와 비밀번호를 적는 폼이 있고, 브라우저에 아이디, 비밀번호 정보가 저장되어 있다면 DOMContentLoaded 이벤트가 발생할 때 인증 정보가 자동으로 채워짐
    → 사용자가 자동 완성을 허용했을 경우
  • 따라서 실행해야 할 스크립트가 길어서 DOMContentLoaded 이벤트가 지연된다면 자동완성 역시 뒤늦게 처리
  • 브라우저 자동 완성 기능을 켜 놓은 사용자라면 특정 사이트에서 자동 완성이 늦게 처리되는 걸 경험 해보았을 텐데 이런 사이트에선 페이지 로딩이 다 끝난 후에야 아이디나 패스워드 같은 브라우저에 저장한 정보가 폼에 뜸
  • 이런 지연이 발생하는 이유는 DOMContentLoaded 이벤트가 실행되는 시점 때문

window.onload
  • window 객체의 load 이벤트스타일, 이미지 등의 리소스들이 모두 로드되었을 때 실행
  • load 이벤트는 onload 프로퍼티를 통해서도 사용 가능
  • 다음 예시에서 window.onload는 이미지가 모두 로드되고 난 후 실행되기 때문에 이미지 사이즈가 제대로 출력되는 것을 확인 가능
  • 실행 순서
    1. 이미지 로드
    2. 페이지 전체가 로드되었습니다.
    3. 사이즈 : 177 x 160
<!DOCTYPE html>
<script>
    window.onload = function() { // window.addEventListener('load', (event) => {와 동일합니다.
      alert('페이지 전체가 로드되었습니다.');
  
      // 이번엔 이미지가 제대로 불러와 진 후에 얼럿창이 실행됩니다.
      alert(`이미지 사이즈: ${img.offsetWidth}x${img.offsetHeight}`);
    };
  </script>
  
  <img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0">
</html>

 

window.onunload
  • window 객체의 unload 이벤트는 사용자가 페이지를 떠날 때 즉, 페이지가 완전히 닫히거나 새 페이지로 이동할 때 실행
  • 이벤트에서 실행할 수 있는 작업은 제한적
    Ex) 팝업창을 닫거나 자원을 정리하는 등의 간단한 작업을 수행
  • 비동기 작업(예: AJAX 요청)은 완료되지 않을 수 있기 때문에, 페이지가 완전히 떠나기 전에 데이터를 보내는 데 적합하지 않음
  • navigator.sendBeacon() 메서드는 비동기적으로 데이터를 서버로 전송하는 데 사용
    • 이는 페이지 이동이나 종료 중에도 데이터를 서버로 안전하게 전송 가능하게 해줌
  • sendBeacon()은 데이터를 백그라운드에서 보내므로 페이지가 종료되는 데 딜레이를 주지 않음
  • 이 메서드는 정확하게 전송할 수 있는 데이터를 최대 64KB까지 보낼 수 있으며, 주로 분석 데이터를 보내는 데 유용
let analyticsData = { /* 분석 정보가 담긴 객체 */ };

window.addEventListener("unload", function() {
  navigator.sendBeacon("/analytics", JSON.stringify(analyticsData));
};
  • 요청은 POST 메서드로 전송
  • 요청 시 문자열뿐만 아니라 폼이나 fetch에서 설명하는 기타 포맷들도 보낼 수 있음
    대개는 문자열 형태의 객체가 전송
  • 전송 데이터는 64kb를 넘을 수 없음

 

window.onbeforeunload
  • 사용자가 페이지를 떠나기 전에 발생하는 이벤트
  • 이 이벤트는 사용자가 페이지를 닫거나 새로 고침할 때, 또는 다른 페이지로 이동할 때 트리거
  • 이 이벤트는 주로 사용자가 아직 저장되지 않은 데이터를 잃어버릴 수 있는 경우 경고를 표시하는 데 사용
<body>
    <h1>페이지를 떠나기 전에 경고하기</h1>

    <script>
        window.onbeforeunload = function(event) {
            // 사용자에게 경고 메시지 표시
            event.returnValue = "페이지를 떠나면 저장되지 않은 변경 사항이 사라집니다. 정말 떠나시겠습니까?";
            // returnValue를 설정해야 브라우저가 경고 메시지를 표시
        };
    </script>
</body>
  • 브라우저는 onbeforeunload 이벤트에서 event.returnValue를 설정하면, 사용자가 페이지를 떠날 때 페이지를 떠나면 변경 사항이 저장되지 않음을 알리는 기본 경고 메시지를 자동으로 표시
  • Chrome, Firefox, Edge와 같은 주요 브라우저는 사용자가 페이지를 떠날 때 onbeforeunload 이벤트를 처리하면 기본적으로 커스터마이즈된 메시지를 무시하고, 자체적으로 표시하는 경고 메시지를 사용
  • 이는 보안 및 사용자 경험을 고려한 브라우저의 제한 사항