DOM 크기가 크면 생각보다 상호작용에 큰 영향을 미칩니다. 이 가이드에서는 그 이유와 해결 방법을 설명합니다.
방법은 없습니다. 웹페이지를 만들면 해당 페이지에는 문서 객체 모델 (DOM)이 생깁니다. DOM은 페이지의 HTML 구조를 나타내며, 페이지의 구조와 콘텐츠에 대한 액세스 권한을 JavaScript 및 CSS에 제공합니다.
하지만 문제는 DOM의 크기가 페이지를 빠르고 효율적으로 렌더링하는 브라우저의 기능에 영향을 미친다는 것입니다. 일반적으로 DOM이 클수록 처음에 페이지를 렌더링하고 페이지 수명 주기에서 렌더링을 업데이트하는 데 더 많은 비용이 소요됩니다.
DOM이 매우 큰 페이지에서 DOM을 수정하거나 업데이트하는 상호작용이 페이지의 빠른 응답 기능에 영향을 주는 값비싼 레이아웃 작업을 트리거하는 경우 문제가 됩니다. 고비용 레이아웃 작업은 페이지의 Interaction to Next Paint (INP)에 영향을 줄 수 있습니다. 페이지가 사용자 상호작용에 빠르게 반응하도록 하려면 DOM 크기가 필요한 만큼만 커야 합니다.
페이지의 DOM이 너무 큰 경우는 언제인가요?
Lighthouse에 따르면 페이지의 DOM 크기가 노드 1,400개를 초과하면 페이지의 DOM 크기가 과도하다고 합니다. Lighthouse는 페이지의 DOM이 800개의 노드를 초과하면 경고를 생성하기 시작합니다. 다음 HTML을 예로 들어 보겠습니다.
<ul>
<li>List item one.</li>
<li>List item two.</li>
<li>List item three.</li>
</ul>
위 코드에는 4개의 DOM 요소, 즉 <ul>
요소와 이 요소의 <li>
하위 요소 3개가 있습니다. 웹페이지에는 이보다 더 많은 노드가 있을 것이 거의 확실합니다. 따라서 DOM 크기를 유지하기 위해 할 수 있는 작업과 페이지의 DOM을 가능한 한 작게 했을 때 렌더링 작업을 최적화하기 위한 기타 전략을 이해하는 것이 중요합니다.
큰 DOM은 페이지 성능에 어떤 영향을 주나요?
큰 DOM은 몇 가지 방식으로 페이지 성능에 영향을 미칩니다.
- 페이지의 초기 렌더링 중 CSS가 페이지에 적용되면 DOM과 유사한 구조인 CSSOM (CSS 객체 모델)이 생성됩니다. CSS 선택자의 구체성이 높아짐에 따라 CSSOM은 더욱 복잡해지고, 웹페이지를 화면에 그리는 데 필요한 레이아웃, 스타일 지정, 합성, 페인트 작업을 실행하는 데 더 많은 시간이 필요합니다. 이러한 추가 작업은 페이지 로드 도중에 초기에 발생하는 상호작용의 상호작용 지연 시간을 증가시킵니다.
- 상호작용이 요소 삽입 또는 삭제를 통해 또는 DOM 콘텐츠와 스타일을 수정하여 DOM을 수정하는 경우, 해당 업데이트를 렌더링하는 데 필요한 작업으로 인해 레이아웃, 스타일 지정, 합성, 페인트 작업이 매우 큰 비용이 들 수 있습니다. 페이지의 초기 렌더링과 마찬가지로, HTML 요소가 상호작용의 결과로 DOM에 삽입될 때 CSS 선택자 특정성이 증가하면 렌더링 작업이 더 좋아질 수 있습니다.
- JavaScript가 DOM을 쿼리할 때 DOM 요소에 대한 참조가 메모리에 저장됩니다. 예를 들어
document.querySelectorAll
를 호출하여 페이지의 모든<div>
요소를 선택하는 경우, 결과가 많은 수의 DOM 요소를 반환하는 경우 메모리 비용이 상당히 높을 수 있습니다.
![Chrome DevTools의 성능 패널에서 과도한 렌더링 작업으로 인해 발생한 긴 작업의 스크린샷 긴 작업의 호출 스택은 페이지 스타일을 다시 계산하고 미리 페인트를 하는 데 소비된 상당한 시간을 보여줍니다.](https://web.dev/static/articles/dom-size-and-interactivity/image/a-screenshot-a-long-task-a768ec4a643a9.png?authuser=2&hl=ko)
이 모든 것이 상호작용에 영향을 줄 수 있지만 위 목록의 두 번째 항목이 특히 중요합니다. 상호작용으로 인해 DOM이 변경되면 페이지에서 잘못된 INP를 초래하는 많은 작업이 시작될 수 있습니다.
DOM 크기는 어떻게 측정하나요?
DOM 크기는 두 가지 방법으로 측정할 수 있습니다. 첫 번째 방법은 Lighthouse를 사용합니다. 감사를 실행할 때 현재 페이지의 DOM 관련 통계는 '과도한 DOM 크기 방지하기'에 있습니다. '진단' 섹션에 있는 있습니다. 이 섹션에서는 DOM 요소의 총 개수, 가장 많은 하위 요소가 포함된 DOM 요소 및 가장 깊은 DOM 요소를 확인할 수 있습니다.
간단한 방법은 모든 주요 브라우저의 개발자 도구에서 JavaScript 콘솔을 사용하는 것입니다. DOM에 있는 총 HTML 요소의 수를 가져오려면 페이지가 로드된 후 콘솔에서 다음 코드를 사용하면 됩니다.
document.querySelectorAll('*').length;
DOM 크기 업데이트를 실시간으로 확인하려면 실적 모니터링 도구를 사용하면 됩니다. 이 도구를 사용하면 레이아웃과 스타일 지정 작업 (및 기타 성능 측면)을 현재 DOM 크기와 함께 연결할 수 있습니다.
<ph type="x-smartling-placeholder">![Chrome DevTools의 성능 모니터 스크린샷 왼쪽에는 페이지의 수명 기간 동안 지속적으로 모니터링할 수 있는 페이지 성능의 다양한 측면이 있습니다. 이 스크린샷에서는 DOM 노드 수, 초당 레이아웃, 섹션당 스타일 재계산이 활발하게 모니터링되고 있습니다.](https://web.dev/static/articles/dom-size-and-interactivity/image/a-screenshot-the-perform-d46dd7f32a5de.png?authuser=2&hl=ko)
DOM의 크기가 Lighthouse DOM 크기의 경고 임계값에 가까워지거나 모두 실패하는 경우, 다음 단계는 웹사이트의 INP를 개선할 수 있도록 DOM의 크기를 줄여 사용자 상호작용에 대한 페이지의 기능을 개선하는 방법을 파악하는 것입니다.
상호작용의 영향을 받는 DOM 요소의 수를 어떻게 측정할 수 있나요?
실험실에서 느린 상호작용을 프로파일링하는 경우 페이지의 DOM 크기와 관련이 있을 수 있다고 생각되는 경우, 프로파일러에서 'ReCalculate Style'이라는 라벨이 지정된 액티비티를 선택하여 얼마나 많은 DOM 요소가 영향을 받았는지 파악할 수 있습니다. 하단 패널의 문맥 데이터를 관찰합니다.
<ph type="x-smartling-placeholder">![Chrome DevTools의 성능 패널에서 선택한 스타일 재계산 활동의 스크린샷. 상호작용 트랙에는 클릭 상호작용이 표시되며 대부분의 작업은 스타일 재계산과 사전 페인트 작업에 사용됩니다. 하단의 패널에는 선택한 활동에 대한 자세한 내용이 표시되며, 이 패널에는 2,547개의 DOM 요소가 영향을 받았음을 보고합니다.](https://web.dev/static/articles/dom-size-and-interactivity/image/a-screenshot-selected-st-9a6c0cc217aa3.png?authuser=2&hl=ko)
위 스크린샷에서 작업의 스타일 재계산을 선택하면 영향을 받는 요소의 수가 표시됩니다. 위의 스크린샷은 많은 DOM 요소가 있는 페이지에서 DOM 크기가 렌더링 작업의 영향을 미치는 극단적인 사례를 보여주지만, 이 진단 정보는 어떤 경우든 DOM의 크기가 상호작용에 대한 응답으로 다음 프레임이 페인트하는 데 걸리는 시간을 제한 요소인지 결정하는 데 유용합니다.
DOM 크기를 줄이려면 어떻게 해야 하나요?
웹사이트의 HTML에 불필요한 마크업이 있는지 감사하는 것 외에도 DOM 크기를 줄이는 주요 방법은 DOM 깊이를 줄이는 것입니다. DOM이 불필요하게 깊은 경우의 신호 중 하나는 브라우저 개발자 도구의 Elements 탭에 다음과 같은 마크업이 표시되는 경우입니다.
<div>
<div>
<div>
<div>
<!-- Contents -->
</div>
</div>
</div>
</div>
이와 같은 패턴이 보이면 DOM 구조를 평면화하여 패턴을 단순화할 수 있습니다. 이렇게 하면 DOM 요소의 수가 줄어들고 페이지 스타일을 단순화할 수 있습니다.
DOM 깊이는 사용하는 프레임워크의 징후일 수도 있습니다. 특히 구성요소 기반 프레임워크(예: JSX에 의존하는 프레임워크)의 경우 상위 컨테이너에 여러 구성요소를 중첩해야 합니다.
그러나 많은 프레임워크에서 프래그먼트라고 하는 것을 사용하여 구성요소를 중첩하지 않도록 할 수 있습니다. 프래그먼트를 기능으로 제공하는 구성요소 기반 프레임워크에는 다음이 포함되나 이에 국한되지 않습니다.
선택한 프레임워크에서 프래그먼트를 사용하여 DOM 깊이를 줄일 수 있습니다. DOM 구조 평면화가 스타일 지정에 미치는 영향이 우려되는 경우 flexbox 또는 그리드와 같은 더 현대적이고 빠른 레이아웃 모드를 사용하는 것이 좋습니다.
기타 고려할 만한 전략
DOM을 최대한 작게 유지하기 위해 DOM 트리를 평면화하고 불필요한 HTML 요소를 제거하더라도 여전히 상당히 클 수 있으며 사용자 상호작용에 반응하여 많은 렌더링 작업을 시작할 수 있습니다. 이러한 상황에 처한 경우 렌더링 작업을 제한하기 위해 고려할 수 있는 다른 전략이 있습니다.
가산적 접근 방식 고려
페이지가 처음 렌더링될 때 처음에는 페이지의 많은 부분이 사용자에게 표시되지 않을 수 있습니다. 시작 시 DOM의 해당 부분을 생략하여 HTML을 지연 로드할 기회가 될 수 있지만, 사용자가 페이지의 초기에 숨겨진 측면이 필요한 페이지 부분과 상호작용할 때 이를 추가할 수 있습니다.
이 접근 방식은 초기 로드 도중은 물론 로드 후에도 유용합니다. 초기 페이지 로드의 경우 미리 렌더링 작업을 적게 해야 합니다. 즉, 초기 HTML 페이로드는 더 가벼워지고 렌더링 속도도 빨라집니다. 이렇게 하면 이 중요한 기간 동안 기본 스레드의 주의를 끌기 위한 경쟁을 줄이면서 상호작용을 실행할 수 있는 기회가 더 많아집니다.
로드 시 처음에는 페이지의 많은 부분이 숨겨지면 재렌더링 작업을 트리거하는 다른 상호작용의 속도도 빨라질 수 있습니다. 그러나 다른 상호작용이 DOM에 더 추가되면 페이지 수명 주기 동안 DOM이 확장됨에 따라 렌더링 작업도 증가합니다.
시간이 지남에 따라 DOM에 추가하는 것은 까다로울 수 있으며 고유한 장단점이 있습니다. 이 방법을 사용할 경우 사용자 상호작용에 대한 응답으로 페이지에 추가하려는 HTML을 채우기 위해 데이터를 가져오기 위해 네트워크 요청을 할 가능성이 높습니다. 이동 중인 네트워크 요청은 INP에 포함되지 않지만 인지되는 지연 시간은 늘어날 수 있습니다. 가능하면 데이터를 가져오는 중임을 나타내는 로드 스피너 또는 기타 표시기를 표시하여 사용자가 상황을 파악할 수 있도록 하세요.
CSS 선택자 복잡성 제한
브라우저가 CSS에서 선택자를 파싱할 때 DOM 트리를 순회하여 이러한 선택자가 현재 레이아웃에 적용되는지, 적용되는지 파악해야 합니다. 이러한 선택자가 더 복잡할수록 페이지의 초기 렌더링은 물론 상호작용의 결과로 페이지가 변경될 경우 향상된 스타일 재계산 및 레이아웃 작업을 모두 수행하기 위해 브라우저가 더 많은 작업을 실행해야 합니다.
content-visibility
속성 사용
CSS는 화면 밖의 DOM 요소를 효과적으로 지연 렌더링하는 content-visibility
속성을 제공합니다. 요소가 표시 영역에 가까워지면 요청 시 렌더링됩니다. content-visibility
의 이점은 초기 페이지 렌더링에서 상당한 양의 렌더링 작업을 줄일 뿐 아니라 사용자 상호작용의 결과로 페이지 DOM이 변경될 때 화면 밖 요소의 렌더링 작업을 건너뜁니다.
결론
웹사이트의 INP를 최적화하는 좋은 방법은 DOM 크기를 꼭 필요한 정도로만 줄이는 것입니다. 이렇게 하면 DOM이 업데이트될 때 브라우저가 레이아웃 및 렌더링 작업을 수행하는 데 걸리는 시간을 줄일 수 있습니다. DOM 크기를 유의미하게 줄일 수 없더라도 렌더링 작업을 DOM 하위 트리로 격리하는 데 사용할 수 있는 몇 가지 기법이 있습니다(예: CSS 포함 및 content-visibility
CSS 속성).
어떤 방법을 사용하든 렌더링 작업을 최소화하는 환경을 만들고 상호작용에 대한 페이지의 렌더링 작업량을 줄여 사용자가 상호작용할 때 웹사이트가 더 반응하는 느낌을 받을 수 있습니다. 즉, 웹사이트의 INP가 낮아져 사용자 환경이 개선됩니다.