DOM 크기가 크면 생각보다 상호작용에 더 큰 영향을 미칩니다. 이 가이드에서는 이유와 해결 방법을 설명합니다.
웹페이지를 빌드할 때 페이지에는 문서 객체 모델 (DOM)이 있습니다. DOM은 페이지의 HTML 구조를 나타내며 JavaScript와 CSS가 페이지의 구조와 콘텐츠에 액세스할 수 있도록 합니다.
하지만 DOM의 크기는 브라우저가 페이지를 빠르고 효율적으로 렌더링하는 기능에 영향을 미칩니다. 일반적으로 DOM이 클수록 페이지를 처음 렌더링하고 페이지 수명 주기에서 나중에 렌더링을 업데이트하는 데 비용이 많이 듭니다.
DOM을 수정하거나 업데이트하는 상호작용이 페이지의 빠른 응답 능력에 영향을 미치는 비용이 많이 드는 레이아웃 작업을 트리거하는 매우 큰 DOM이 있는 페이지에서는 문제가 됩니다. 비용이 많이 드는 레이아웃 작업은 페이지의 다음 페인트까지의 상호작용 (INP)에 영향을 줄 수 있습니다. 페이지가 사용자 상호작용에 빠르게 반응하도록 하려면 DOM 크기가 필요한 만큼만 커야 합니다.
페이지의 DOM이 너무 큰 경우는 언제인가요?
Lighthouse에 따르면 페이지의 DOM 크기가 1,400개 노드를 초과하면 과도한 것입니다. 페이지의 DOM이 800개 노드를 초과하면 Lighthouse에서 경고를 표시합니다. 다음 HTML을 예로 들어 보겠습니다.
<ul>
<li>List item one.</li>
<li>List item two.</li>
<li>List item three.</li>
</ul>
위의 코드에는 <ul>
요소와 세 개의 <li>
하위 요소 등 네 개의 DOM 요소가 있습니다. 웹페이지에는 이보다 훨씬 많은 노드가 있을 가능성이 높으므로 DOM 크기를 적절하게 유지하기 위해 할 수 있는 작업과 페이지의 DOM을 최대한 작게 만든 후 렌더링 작업을 최적화하는 다른 전략을 이해하는 것이 중요합니다.
큰 DOM이 페이지 성능에 미치는 영향
큰 DOM은 다음과 같은 몇 가지 방식으로 페이지 성능에 영향을 미칩니다.
- 페이지의 초기 렌더링 중 CSS가 페이지에 적용되면 CSS 객체 모델 (CSSOM)이라고 하는 DOM과 유사한 구조가 생성됩니다. CSS 선택자의 구체성이 증가하면 CSSOM이 더 복잡해지고 웹페이지를 화면에 그리는 데 필요한 레이아웃, 스타일 지정, 컴포지션, 페인트 작업을 실행하는 데 더 많은 시간이 필요합니다. 이러한 추가 작업으로 인해 페이지 로드 초기에 발생하는 상호작용의 상호작용 지연 시간이 늘어납니다.
- 상호작용으로 인해 요소 삽입 또는 삭제를 통해 DOM이 수정되거나 DOM 콘텐츠와 스타일이 수정되면 업데이트를 렌더링하는 데 필요한 작업으로 인해 매우 비용이 많이 드는 레이아웃, 스타일 지정, 컴포지션, 페인트 작업이 발생할 수 있습니다. 페이지의 초기 렌더링과 마찬가지로 CSS 선택기 명시도가 높아지면 상호작용의 결과로 HTML 요소가 DOM에 삽입될 때 렌더링 작업이 추가될 수 있습니다.
- JavaScript가 DOM을 쿼리하면 DOM 요소에 대한 참조가 메모리에 저장됩니다. 예를 들어 페이지에서 모든
<div>
요소를 선택하기 위해document.querySelectorAll
를 호출하는 경우 결과에서 많은 수의 DOM 요소를 반환하면 메모리 비용이 상당할 수 있습니다.
이러한 요소는 모두 상호작용에 영향을 줄 수 있지만 위의 목록에서 두 번째 항목이 특히 중요합니다. 상호작용으로 인해 DOM이 변경되면 페이지의 INP가 좋지 않은 데 기여할 수 있는 많은 작업이 시작될 수 있습니다.
DOM 크기를 측정하려면 어떻게 해야 하나요?
DOM 크기는 몇 가지 방법으로 측정할 수 있습니다. 첫 번째 방법은 Lighthouse를 사용합니다. 감사를 실행하면 현재 페이지의 DOM에 관한 통계가 '진단' 제목 아래의 '과도한 DOM 크기 방지' 감사에 표시됩니다. 이 섹션에서는 총 DOM 요소 수, 하위 요소가 가장 많이 포함된 DOM 요소, 가장 깊은 DOM 요소를 확인할 수 있습니다.
더 간단한 방법은 주요 브라우저의 개발자 도구에서 JavaScript 콘솔을 사용하는 것입니다. DOM의 총 HTML 요소 수를 가져오려면 페이지가 로드된 후 콘솔에서 다음 코드를 사용하면 됩니다.
document.querySelectorAll('*').length;
DOM 크기 업데이트를 실시간으로 확인하려면 성능 모니터 도구를 사용해도 됩니다. 이 도구를 사용하면 레이아웃 및 스타일 지정 작업 (및 기타 성능 측면)을 현재 DOM 크기와 연관시킬 수 있습니다.
DOM 크기가 Lighthouse DOM 크기의 경고 기준에 근접하거나 완전히 실패하는 경우 다음 단계는 DOM 크기를 줄여 페이지가 사용자 상호작용에 응답하는 기능을 개선하여 웹사이트의 INP를 개선하는 방법을 파악하는 것입니다.
상호작용의 영향을 받는 DOM 요소의 수를 측정하려면 어떻게 해야 하나요?
페이지의 DOM 크기와 관련이 있을 수 있는 실험실의 느린 상호작용을 프로파일링하는 경우 프로파일러에서 '스타일 재계산'이라고 표시된 활동을 선택하고 하단 패널의 컨텍스트 데이터를 관찰하여 영향을 받은 DOM 요소의 수를 파악할 수 있습니다.
위 스크린샷에서 작업을 선택하면 영향을 받는 요소의 수가 표시되는 것을 확인할 수 있습니다. 위 스크린샷은 DOM 요소가 많은 페이지에서 DOM 크기가 렌더링 작업에 미치는 영향을 보여주는 극단적인 사례이지만, 이 진단 정보는 상호작용에 대한 응답으로 다음 프레임이 페인트되는 데 걸리는 시간에 DOM 크기가 제한 요소인지 확인하는 데 유용합니다.
DOM 크기를 줄이려면 어떻게 해야 하나요?
불필요한 마크업이 있는지 웹사이트의 HTML을 감사하는 것 외에 DOM 크기를 줄이는 주요 방법은 DOM 깊이를 줄이는 것입니다. DOM이 불필요하게 깊다는 신호 중 하나는 브라우저의 개발자 도구의 요소 탭에 다음과 같은 마크업이 표시되는 경우입니다.
<div>
<div>
<div>
<div>
<!-- Contents -->
</div>
</div>
</div>
</div>
이와 같은 패턴이 표시되면 DOM 구조를 평면화하여 단순화할 수 있습니다. 이렇게 하면 DOM 요소의 수가 줄어들고 페이지 스타일을 간소화할 수 있습니다.
DOM 깊이는 사용하는 프레임워크의 증상일 수도 있습니다. 특히 JSX를 사용하는 것과 같은 구성요소 기반 프레임워크에서는 상위 컨테이너에 여러 구성요소를 중첩해야 합니다.
하지만 많은 프레임워크에서는 프래그먼트라고 하는 것을 사용하여 구성요소 중첩을 방지할 수 있습니다. 프래그먼트를 기능으로 제공하는 구성요소 기반 프레임워크에는 다음이 포함되나 이에 국한되지 않습니다.
선택한 프레임워크에서 프래그먼트를 사용하면 DOM 깊이를 줄일 수 있습니다. DOM 구조 평면화가 스타일 지정에 미치는 영향이 우려된다면 flexbox 또는 그리드와 같은 최신 (더 빠른) 레이아웃 모드를 사용하는 것이 좋습니다.
고려할 만한 기타 전략
DOM 트리를 평면화하고 불필요한 HTML 요소를 삭제하여 DOM을 최대한 작게 유지하려고 해도 사용자 상호작용에 따라 변경될 때 DOM이 상당히 커지고 많은 렌더링 작업이 시작될 수 있습니다. 이러한 상황에 처한 경우 렌더링 작업을 제한하기 위해 고려할 수 있는 다른 전략이 있습니다.
부가적 접근 방식 고려
페이지가 처음 렌더링될 때 페이지의 상당 부분이 사용자에게 처음에는 표시되지 않을 수 있습니다. 시작 시 DOM의 해당 부분을 생략하여 HTML을 지연 로드할 수 있지만 사용자가 페이지의 초기 숨겨진 측면이 필요한 부분과 상호작용할 때 이를 추가할 수 있습니다.
이 접근 방식은 초기 로드 중뿐만 아니라 그 이후에도 유용합니다. 초기 페이지 로드의 경우 처음부터 렌더링 작업이 줄어들므로 초기 HTML 페이로드가 더 가벼워지고 더 빠르게 렌더링됩니다. 이렇게 하면 중요한 기간 동안의 상호작용이 메인 스레드의 주의를 덜 끌어 경쟁이 줄어들고 실행될 기회가 더 많아집니다.
로드 시 처음에 숨겨진 페이지 부분이 많으면 다시 렌더링 작업을 트리거하는 다른 상호작용의 속도도 빨라질 수 있습니다. 하지만 다른 상호작용이 DOM에 더 추가되면 페이지 수명 주기 전반에 걸쳐 DOM이 커지므로 렌더링 작업이 증가합니다.
시간이 지남에 따라 DOM에 추가하는 것은 까다로울 수 있으며 자체적인 트레이드오프가 있습니다. 이 방법을 사용하는 경우 사용자 상호작용에 대한 응답으로 페이지에 추가하려는 HTML을 채울 데이터를 가져오기 위해 네트워크 요청을 할 가능성이 높습니다. 진행 중인 네트워크 요청은 INP에 포함되지 않지만 인식된 지연 시간을 늘릴 수 있습니다. 가능하다면 데이터가 가져오는 중임을 나타내는 로딩 스피너나 기타 표시기를 표시하여 사용자에게 작업이 진행 중임을 알립니다.
CSS 선택자 복잡성 제한
브라우저가 CSS의 선택자를 파싱할 때는 DOM 트리를 순회하여 해당 선택자가 현재 레이아웃에 어떻게 적용되는지(적용되는 경우) 파악해야 합니다. 이러한 선택자가 복잡할수록 브라우저가 페이지의 초기 렌더링을 실행하는 데 더 많은 작업을 해야 하며, 상호작용의 결과로 페이지가 변경되는 경우 스타일 재계산 및 레이아웃 작업도 늘어납니다.
content-visibility
속성 사용
CSS는 화면에 표시되지 않는 DOM 요소를 지연 렌더링하는 효과적인 방법인 content-visibility
속성을 제공합니다. 요소가 표시 영역에 접근하면 요청 시 렌더링됩니다. content-visibility
의 이점은 초기 페이지 렌더링에서 상당한 렌더링 작업을 줄일 뿐만 아니라 사용자 상호작용의 결과로 페이지 DOM이 변경될 때 화면에 표시되지 않는 요소의 렌더링 작업도 건너뛴다는 것입니다.
결론
DOM 크기를 꼭 필요한 부분만으로 줄이는 것은 웹사이트의 INP를 최적화하는 좋은 방법입니다. 이렇게 하면 DOM이 업데이트될 때 브라우저가 레이아웃 및 렌더링 작업을 실행하는 데 걸리는 시간을 줄일 수 있습니다. DOM 크기를 의미 있게 줄일 수 없더라도 CSS 포함 및 content-visibility
CSS 속성과 같은 기술을 사용하여 렌더링 작업을 DOM 하위 트리로 격리할 수 있습니다.
어떤 방법을 사용하든 렌더링 작업이 최소화되는 환경을 만들고 상호작용에 대한 응답으로 페이지에서 실행하는 렌더링 작업의 양을 줄이면 사용자가 웹사이트와 상호작용할 때 웹사이트가 더 빠르게 반응하는 것처럼 느껴집니다. 즉, 웹사이트의 INP가 낮아지고 사용자 환경이 개선됩니다.