Lighthouse의 Network dependency tree: 읽고 해결하는 방법
브라우저가 다른 파일 내부에서만 발견하는 파일 때문에 페이지는 각 파일을 차례대로 기다려야 합니다. 트리와 preconnect 표를 읽는 방법과 체인 길이를 줄이는 방법을 설명합니다.
PageSpeed Insights의 Network dependency tree란 무엇입니까?
Lighthouse의 Network dependency tree(Lighthouse 13 이전의 "Avoid chaining critical requests")는 페이지에 가장 먼저 필요한 리소스와 각 리소스가 어떤 파일을 기다렸는지 보여줍니다. 다른 파일 내부에서 발견되는 파일마다 대기 시간이 추가됩니다. 폰트는 자체 도메인에서 직접 호스팅하세요. 스타일시트는 @import 대신 HTML에서 링크하고, preconnect는 피할 수 없는 도메인에서 중요 파일을 가져올 때만 적용하세요.
빠른 해결 방법만 필요하십니까? 웹 폰트, WordPress @import, Shopify, 스크립트가 추가하는 파일 항목으로 바로 이동하세요.
Network dependency tree는 어떻게 읽습니까?
Lighthouse 13과 PageSpeed Insights에서는 이 안내 문구를 다음과 같이 설명합니다. "체인 길이를 줄이고, 리소스 다운로드 크기를 줄이거나, 불필요한 리소스 다운로드를 미뤄 페이지 로드를 개선하고 중요 요청 체인을 방지하세요." 이 항목은 기존의 "Avoid chaining critical requests"(이전 "Minimize critical requests depth")와 "Preconnect to required origins"를 대체한 것이며, 트리 아래에 preconnect 표 2개가 함께 표시되는 이유이기도 합니다.
아래 리포트는 일반적인 WordPress 사이트 구조로 제작한 치과 홈페이지의 측정 결과입니다. 자식 테마의 스타일시트가 부모 테마 스타일시트를 임포트하고, 부모 테마는 Google Fonts를 임포트하며, 아이콘은 CDN의 Font Awesome에서 불러옵니다.

모바일 환경의 Lighthouse 13.6, DevTools 쓰로틀링(Slow 4G, 4x CPU).
- 들여쓰기된 행은 브라우저가 한 단계 상위 파일 내부에서만 발견한 파일이므로, 해당 상위 파일을 기다려야 했던 파일입니다. 같은 수준의 행들은 Google의 css2 스타일시트 아래에 있는 3개의 폰트 파일처럼 병렬로 다운로드됩니다.
- 행에 표시된 시간과 KiB는 페이지 로드 시작 시점부터 계산하여 해당 파일이 도착한 시점과 파일 크기입니다.
- Maximum critical path latency는 주황색으로 표시된 가장 느린 체인의 시간입니다. 치과 페이지에서는 2단계 깊이에 있는 155KiB짜리 아이콘 폰트가 이에 해당합니다. 4단계 아래에 있는 Google Fonts보다 더 늦게 도착합니다.
- LCP 및 Unscored: 이 항목은 Performance 점수에 직접 반영되지 않습니다. 하지만 여기에 포함된 스타일시트가 첫 번째 렌더링을 가로막으며, LCP는 첫 렌더링보다 먼저 발생할 수 없습니다.
PageSpeed Insights는 쓰로틀링을 적용하지 않고 로드를 기록하므로, 트리에 표시되는 시간이 이 스크린샷보다 훨씬 짧습니다.
파일 체인이 페이지를 지연시키는 이유는 무엇입니까?
치과 페이지에서 방문자는 처음에 흰 화면만 보게 됩니다. 이어서 fallback 폰트로 텍스트가 표시되다가 웹 폰트가 도착하면 글꼴이 바뀌며, 아이콘 자리에는 빈 원만 남습니다.
브라우저는 HTML을 미리 읽어 내려가며(preload scanner) 그곳에서 발견한 모든 파일을 한 번에 요청합니다. 하지만 다른 파일 내부에 지정된 파일은 해당 파일이 도착해야만 발견할 수 있습니다. 스타일시트 안의 폰트, @import 뒤에 있는 스타일시트, 스크립트가 추가하는 스크립트가 그렇습니다. 단계가 늘어날 때마다 다음 요청을 시작하기 전 서버 왕복 시간(round trip)이 추가됩니다.
다른 도메인에 있는 파일은 대기 시간이 더 깁니다. 브라우저가 먼저 연결(DNS, TCP, TLS)을 맺어야 하기 때문입니다. 모바일 네트워크에서는 이미 열려 있는 연결에서 요청할 때보다 몇 배나 더 오래 걸립니다. 치과 페이지에서 Google Fonts는 2개의 @import 뒤에 위치하여 HTML로부터 4단계 아래에 있으며, 마지막 2단계는 새로운 도메인(fonts.googleapis.com, fonts.gstatic.com)에 연결됩니다. 스타일시트가 첫 렌더링을 지연시키는 이유는 Render-blocking requests에서 자세히 설명합니다.
트리를 평탄화하면 페이지가 얼마나 빨라집니까?
문제를 해결한 후 치과 페이지는 첫 렌더링이 1초 빨라졌고, 자체 폰트는 거의 2초 더 일찍 표시되었습니다. 세 가지 버전을 측정했습니다. Before는 @import가 2개 있는 상태입니다. Links는 WordPress가 wp_enqueue_style로 출력하듯 동일한 파일들을 HTML에서 직접 링크한 상태입니다. After는 부모와 자식 스타일시트를 단일 파일로 합치고, 폰트는 사이트의 자체 도메인에 두고 3개 모두 preload했으며, 아이콘은 인라인 SVG로 바꾼 상태입니다.
| Before | Links | After | |
|---|---|---|---|
| 단계 | 4 | 2 | 1 |
| 가장 느린 체인 | 4.18 | 3.25 | 1.21 |
| 첫 번째 렌더링 | 2.36 | 1.43 | 1.35 |
| 모든 폰트 | 3.57 | 2.61 | 1.72 |
| LCP | 2.36 | 1.73 | 1.96 |
시간 단위는 초이며, 쓰로틀링이 적용된 모바일 환경(Slow 4G, CPU 4배 감속) 기준입니다. 단계 수와 가장 느린 체인(리포트의 Maximum critical path latency)은 동일한 쓰로틀링 환경의 Lighthouse 13.6 측정값입니다.

Before

After
동일하게 쓰로틀링된 스마트폰에서 로드 시작 2초 후의 화면입니다.
Links 버전만으로도 첫 렌더링 개선 효과의 대부분을 얻을 수 있습니다. 폰트와 아이콘이 여전히 각자의 도메인을 기다리기는 하지만, @import 2단계가 제거되었기 때문입니다. 히어로 사진인 LCP는 수정 후 0.4초 더 빨라집니다. Links에서 LCP가 가장 빠른 이유는 사진이 대역폭을 거의 독점하기 때문입니다. After에서는 preload된 3개의 폰트가 대역폭을 나눠 쓰므로, 해결책에서는 1~2개만 preload하도록 구성합니다.
실제 방문자가 이 체인으로 인해 대기하고 있는지 확인하려면 CoreDash에서 모바일 LCP를 세부 단계별로 나누어 보세요. 늦게 도착하는 스타일시트와 폰트는 긴 Render Delay로 나타납니다. Render Delay는 LCP 이미지가 도착한 후, 또는 텍스트의 경우 첫 번째 바이트가 도착한 후 대기하는 시간입니다.

모바일 환경의 CoreDash, p75, 텍스트가 LCP 요소인 경우: Time To First Byte 대비 Render Delay를 확인하세요.
Preconnected origins와 Preconnect candidates는 무엇을 의미합니까?
preconnect(<link rel="preconnect">)는 브라우저가 특정 도메인의 파일을 필요로 하기 전에 해당 도메인과의 연결을 미리 엽니다. Preconnected origins는 페이지에서 preconnect를 적용한 도메인 목록입니다. Preconnect candidates는 파일을 가져오기 위해 연결 수립이 여전히 필요했던 도메인과, preconnect로 절약할 수 있는 예상 시간(Est LCP savings)을 나타냅니다.

용량이 크고 lazy loading이 적용된 히어로 사진이 포함된 동일 치과 페이지, DevTools 쓰로틀링이 적용된 Lighthouse 13.6.
- "Unused preconnect. Only use preconnect for origins that the page is likely to request." 페이지에서 해당 도메인(여기서는 maps.googleapis.com 및 www.youtube.com)의 리소스를 전혀 로드하지 않았다는 의미입니다. 결제 페이지 등 다른 템플릿에서도 사용하지 않는지 확인한 후 삭제하세요.
- "Unused preconnect. Check that the crossorigin attribute is used properly." 폰트는
crossorigin을 지정하여 연결을 열어야 하지만, 스타일시트나 스크립트는 이 속성 없이 열어야 합니다. cdnjs.cloudflare.com은 치과 페이지에 Font Awesome 스타일시트와 폰트를 모두 전송하므로, 스타일시트가 preconnect를 사용하고 폰트는 두 번째 연결을 새로 열었습니다. 폰트는 직접 호스팅하세요. 불가능할 때만 해당 도메인에crossorigin을 추가한 두 번째 preconnect를 설정하세요. - "More than 4 preconnect connections were found." preconnect를 추가할 때마다 페이지에 우선적으로 필요한 파일들과 병렬로 연결이 수립됩니다. 가능한 리소스는 자체 도메인에서 호스팅하고, preconnect는 피할 수 없는 도메인의 중요 파일에만 남기세요.
- Preconnect candidates 항목의 도메인. 해당 파일을 자체 도메인으로 옮기세요. 자체 호스팅이 불가능하고 중요한 파일일 때만 preconnect를 사용하세요.
Google Fonts를 아직 직접 호스팅할 수 없다면, HTML에 스타일시트를 링크하고 fonts.gstatic.com에 crossorigin을 추가하여 preconnect를 설정하세요. 브라우저는 링크를 읽는 즉시 fonts.googleapis.com에 연결합니다. 단, 이 방법도 체인이 완전히 사라지지는 않습니다.
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 플러그인의 preconnect 설정이 crossorigin을 항상 올바르게 처리하는 것은 아닙니다. LiteSpeed Cache의 DNS Preconnect는 어떤 도메인에도 이 속성을 추가하지 않으며, Perfmatters는 체크박스를 선택한 곳에만 추가합니다. 페이지 소스를 직접 확인하세요. 이 속성은 폰트 도메인에만 필요합니다.
Network dependency tree는 어떻게 해결합니까?
Lighthouse가 주황색으로 표시한 가장 느린 체인의 가장 깊은 행부터 점검하세요. 체인에 포함된 각 파일마다 확인해야 합니다. 이 파일을 HTML에서 직접 호출할 수 있는가? 아니면 자체 도메인에서 제공할 수 있는가?
Google Fonts 및 기타 웹 폰트
코드를 수정하지 않고 해결하려면 Google Fonts를 자체 도메인에서 호스팅하는 설정을 활성화하세요. WP Rocket이나 FlyingPress의 Self-host Google Fonts, Perfmatters의 Local Google Fonts, Elementor(설정, 성능)의 Load Google Fonts Locally, 또는 OMGF 플러그인을 사용하면 됩니다. WP Rocket의 설정은 테마 스타일시트에 있는 Google Fonts @import를 감지하지 못합니다. 설정 후에도 트리에 fonts.googleapis.com이 여전히 남아 있다면 @import를 직접 제거하세요(다음 섹션 참고). LiteSpeed Cache나 Autoptimize처럼 폰트 로더 스크립트를 사용해 Google Fonts를 "비동기(asynchronously)"로 로드하는 옵션은 체인에 단계를 하나 더 추가할 뿐입니다. 대신 폰트를 직접 호스팅하세요.
직접 코드로 수정한다면, 자체 스타일시트에 @font-face 규칙을 넣고 woff2 파일은 같은 경로의 fonts 폴더에 두세요:
@font-face {
font-family: "Poppins";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("fonts/poppins-v24-latin-700.woff2") format("woff2");
} 폰트는 스타일시트 내부에서만 선언할 수 있으므로, 이 체인을 줄이는 방법은 preload뿐입니다. 폰트 파일 개수를 줄이고 한두 개만 preload하세요. 첫 화면에서 가장 큰 텍스트에 사용된 폰트를 먼저 preload한 뒤 LCP를 확인하세요:
<link rel="preload" href="/wp-content/themes/acme-smile/fonts/poppins-v24-latin-700.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/wp-content/themes/acme-smile/fonts/open-sans-v44-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/wp-content/themes/acme-smile/style.css?ver=2.5.0"> 자체 도메인이라도 preload에는 crossorigin이 반드시 필요합니다. 그렇지 않으면 폰트가 두 번 다운로드됩니다. 폰트 preload에 관한 자세한 내용은 Font display를 참고하세요.
Next.js에서는 next/font가 이미 폰트 자체 호스팅과 preload를 자동으로 처리합니다. CSS 파일에 직접 작성한 @import url(https://fonts.googleapis.com/...)을 next/font로 교체하세요.
@import를 사용하는 WordPress 테마
부모 테마에서 Google Fonts @import를 삭제하더라도 다음 테마 업데이트 시 다시 복원됩니다. 테마 자체의 폰트 설정을 찾아보거나, 개발자에게 폰트를 enqueue해 달라고 요청하세요.
자식 테마의 style.css에서 부모 스타일시트를 @import하면 체인이 한 단계 늘어납니다. 테마 제작자에게 @import가 없는 자식 테마를 요청하거나, 개발자에게 리포트의 트리 결과와 함께 @import를 대체하는 아래의 자식 테마 functions.php 코드를 전달하세요:
add_action( 'wp_enqueue_scripts', function () {
wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
} ); 적용 후 페이지 소스를 확인하세요. 부모 스타일시트는 한 번만 로드되어야 합니다.
아이콘 폰트
Font Awesome과 Elementor의 아이콘은 모든 아이콘이 포함된 폰트를 로드하는 스타일시트를 불러옵니다. Elementor를 사용한다면 Inline Font Icons 설정을 켜세요. 스타일시트와 폰트가 인라인 SVG로 대체됩니다. 그 외의 환경이라면 페이지에서 사용하는 아이콘을 인라인 SVG로 바꾸거나, Font Awesome을 자체 도메인에서 호스팅하세요.
Shopify
Google Fonts 코드를 theme.liquid에 직접 붙여넣지 말고 테마 편집기(Theme settings, Typography)에서 폰트를 선택하세요. 그러면 테마가 스토어의 자체 도메인에서 폰트를 제공하며, Dawn 및 이를 기반으로 제작된 테마들은 폰트를 자동으로 preload합니다. 스타일시트를 추가하는 앱 임베드(App embeds)도 트리에 나타납니다. 하나씩 비활성화하면서 Lighthouse를 다시 실행해 보세요. Shopify는 페이지 헤더에 자체 preconnect를 전송합니다. 자체 CDN과 렌더링 차단 파일이 있는 다른 외부 도메인 중 처음 3개 도메인이 대상이며, Lighthouse는 이를 4개 제한에 포함하여 계산합니다. theme.liquid에 남아 있는 오래된 cdn.shopify.com preconnect를 제거하고, 트리에 여전히 필요한 도메인에 대해서만 직접 preconnect를 추가하세요. 앱 임베드를 하나씩 끌 때마다 Shopify가 전송하는 preconnect도 하나씩 줄어들 수 있습니다. Shopify 자체 preconnect만 남았는데도 "More than 4 preconnect connections" 경고가 계속 표시된다면 그대로 두세요.
스크립트가 추가하는 파일 및 변경할 수 없는 서비스
스크립트가 추가하는 스타일시트는 해당 스크립트가 실행될 때까지 대기해야 합니다. 첫 화면에 필요한 리소스라면 HTML에 link 태그를 넣고, 그렇지 않다면 스크립트를 defer 처리하세요. 가장 먼저 실행되어야 하는 동의 관리 도구(consent tool)는 렌더링을 계속 차단할 수밖에 없습니다. 이 도구가 피할 수 없는 외부 도메인에서 파일을 로드한다면 해당 도메인에 preconnect를 설정하세요. 서버가 HTML을 생성하는 데 시간이 걸리는 경우, 103 Early Hints를 활용하면 HTML 본문보다 먼저 preconnect와 폰트 preload를 전송합니다.
문제를 해결한 후에도 트리가 여전히 빨간색인 이유는 무엇입니까?
Lighthouse는 HTML이 스타일시트를 단 하나만 로드해도 이 항목을 빨간색으로 표시합니다. 따라서 체인이 전혀 없는 페이지라도 빨간색으로 남아 있습니다. 이 빨간색 표시는 그대로 두어도 괜찮습니다. HTML과 HTML에서 직접 호출하는 파일들뿐이기 때문입니다. 수정해야 할 대상은 다른 파일 아래로 들여쓰기된 모든 행입니다:
- 자체 스타일시트 아래의 폰트. 파일 개수를 줄이고 한두 개만 preload하세요. 첫 화면에 표시되지 않는 폰트는 체인으로 남아 있어도 괜찮습니다.
- 외부 서비스가 추가하는 파일. Cloudflare의 이메일 난독화(
email-decode.min.js) 및 Web Analytics 비콘, 또는 LiteSpeed Cache의 Guest Mode가 생성하는guest.vary.php등이 해당합니다. 해당 기능을 끄세요.
PageSpeed Insights의 알려진 버그로 인해 간혹 다른 사이트의 요청이 트리에 포함되기도 합니다. 테스트를 다시 실행해 보세요.

모든 체인이 제거된 치과 페이지, DevTools 쓰로틀링이 적용된 Lighthouse 13.6.
트리가 단축되었는지 어떻게 확인합니까?
Maximum critical path latency는 실행할 때마다 조금씩 달라지므로, 먼저 Lighthouse를 다시 실행하여 단계 수와 가장 느린 체인을 비교하세요. Chrome DevTools의 Network 패널에 있는 Initiator 열을 확인하면 각 파일을 어떤 파일이 요청했는지 알 수 있습니다. 실제 방문자 데이터의 경우, 이후 방문부터 CoreDash에서 모바일 FCP와 LCP 추이를 관찰하세요. Search Console 데이터 반영에는 최대 28일이 걸립니다.
가장 긴 체인의 원인이 직접 수정할 수 없는 테마나 페이지 빌더, 또는 자체 폰트를 불러오는 앱입니까? Core Web Vitals power hour에서 트리를 파일 단위로 함께 분석하고, 어떤 파일을 자체 도메인으로 이전할지, 어떤 리소스를 HTML에 넣을지, 어떤 도메인에 preconnect가 필요한지 함께 검토하고 결정합니다.

더 빠른 사이트가 필요하신가요?
저는 Arjen입니다. 17년 동안 느린 사이트를 고쳐 왔습니다. 직접 해결하기 어려운 문제가 있다면 URL을 보내 주세요. 편하게 이야기 나눠요.
URL 보내기
수정했는데 왜 효과가 없을까요?
때로는 CDN이 아직 이전 HTML을 제공하거나, 수정이 데스크톱 템플릿에만 들어가고 모바일은 다른 템플릿을 쓰는 경우가 있습니다. URL과 문제를 보내 주시면 가능한 시간대를 몇 가지 회신해 드립니다. 먼저 field data를 살펴보고, 사이트를 1시간 함께 본 뒤, 하루 안에 정리 노트를 서면으로 보내 드립니다. €350 (부가세 별도).
URL 보내기수정을 구현할 개발자가 없으신가요? 사이트 복사본에서 제가 직접 작업합니다.