페이지 속도 최적화

렌더링 차단 리소스 해결: CSS와 JavaScript를 안전하게 로드하는 방법

첫 렌더링의 불필요한 의존성을 제거하면서 필수 스타일과 상호작용을 유지합니다.

완료 표시가 있는 두 자원 막대가 렌더링된 페이지 윤곽에 연결된 모습

렌더링 차단 지연은 유용한 콘텐츠가 나타나기 전에 필요한 CSS와 JavaScript를 찾아 불필요한 작업을 중요 경로 밖으로 옮겨 줄입니다. 필수 스타일은 유지하고 스크립트 목적에 맞는 로딩 모드를 선택하며 모든 자원을 미리 로드하지 않습니다. 목표는 스타일이 없거나 기능이 깨진 채 경고만 없애는 것이 아니라 올바른 첫 화면을 더 빨리 표시하는 것입니다.

렌더링, 파싱, 메인 스레드 작업을 구분합니다

브라우저는 HTML을 DOM으로, 스타일을 표시 정보로 처리합니다. 관련 구조와 스타일을 결합하고 레이아웃을 계산해 픽셀을 그립니다. 현재 페이지에 적용되는 스타일시트는 모양을 결정하는 규칙을 제공하므로 렌더링을 지연시킬 수 있습니다.

async나 defer가 없는 클래식 스크립트는 다운로드와 실행 중 HTML 파싱을 멈출 수 있습니다. 이는 명시적 렌더링 차단 지시와 다르지만 자원 발견과 첫 표시를 늦춥니다. 스크립트가 스타일을 조회할 수 있으므로 앞선 차단 스타일시트를 기다릴 수도 있습니다. 확장자보다 의존 사슬이 중요합니다.

비동기로 내려받은 스크립트도 작업을 명시적으로 옮기지 않으면 메인 스레드에서 실행합니다. 긴 실행은 다운로드 후에도 렌더링과 입력 처리를 늦춥니다. 로딩 속성은 일정을 바꾸며 파일의 일을 없애지는 않습니다.

영향받는 가시 이벤트부터 찾습니다. 이미지가 늦다면 다운로드와 표시를 막는 CSS 또는 JavaScript를 구분합니다. LCP 가이드가 단계를 나눕니다. 화면은 빨리 보이지만 반응이 나쁘면 JavaScript 실행 분석을 사용합니다.

현재 도구에서 의존 사슬을 읽습니다

“렌더링 차단 리소스 제거”는 검색과 오래된 보고서에 남아 있습니다. Lighthouse 13은 여러 기존 감사를 인사이트로 옮겼으므로 현재 보고서는 차단 요청 인사이트를 표시할 수 있습니다. 원리는 같지만 예전 이름이나 JSON 식별자가 계속 존재한다고 가정하지 않습니다.

Performance 패널에서 탐색을 기록하고 Network 워터폴을 봅니다. 문서, 스타일, 스크립트, 글꼴, LCP 자원을 식별합니다. 다른 파일 이후에 발견되는 요청과 시작자를 찾습니다. @import 사슬의 작은 CSS가 HTML에 직접 연결한 큰 CSS보다 늦게 시작할 수 있습니다.

기준 스크린샷이나 필름스트립으로 전후 표시를 남깁니다. 깨진 메뉴나 무스타일 콘텐츠의 빠른 페인트가 유용한 개선은 아닐 수 있습니다. 첫 화면에 없는 코드가 아래 콘텐츠를 지원할 수 있으므로 전체 페이지도 확인합니다.

여러 인사이트의 예상 절약량을 독립 측정처럼 더하지 않습니다. 같은 의존 사슬을 가리킬 수 있습니다. 일관된 변경 하나를 적용한 뒤 실제 요청과 표시를 관찰합니다.

중요 CSS를 작고 관리 가능하게 유지합니다

중요 CSS는 처음 보이는 레이아웃에 필요한 스타일입니다. 신중히 고른 부분을 인라인하면 별도 요청을 피할 수 있습니다. 전체 사이트 CSS를 넣는 것은 다릅니다. HTML 응답을 키우고 다른 탐색에서 독립 캐시 파일로 재사용할 수 없게 합니다.

첫 화면은 기기, 언어, 동의 상태, 콘텐츠에 따라 달라집니다. 데스크톱 화면 하나에서 추출한 규칙은 모바일 메뉴, 긴 번역 제목, 오류 메시지를 놓칠 수 있습니다. 대표 템플릿과 시각 검사에 기반한 빌드 과정으로 다룹니다.

예제는 필수 레이아웃을 문서 안에 두고 인쇄 전용 CSS에 미디어 조건을 지정합니다.

html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance notes</title>
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.6; }
    header, main { max-width: 70rem; margin-inline: auto; padding: 1rem; }
    img { max-width: 100%; height: auto; }
  </style>
  <link rel="stylesheet" href="/styles/print.css" media="print">
</head>
<body>
  <header>Performance notes</header>
  <main><h1>A readable first render</h1><p>Essential content is available immediately.</p></main>
</body>
</html>

현재와 맞지 않는 미디어 조건은 그 매체의 렌더링 차단을 피하지만 다운로드를 금지하지는 않습니다. 화면용 CSS를 media="print"로 지정하고 그대로 두면 적용 범위가 바뀝니다. 로드 후 미디어를 바꾸는 기법은 스크립트 실패, CSP, 무스타일 표시를 처리해야 합니다.

인라인 스타일은 의도한 nonce나 hash로 Content Security Policy를 준수해야 합니다. 예제를 복사하려고 기존 정책을 약화하지 않습니다. 빌드는 중요 규칙과 전체 CSS를 동기화해 오래된 선언으로 나중에 레이아웃이 뛰지 않게 해야 합니다.

실제 페이지 필요에 따라 CSS를 나눕니다

작은 공통 기반과 경로 또는 컴포넌트별 스타일은 단순한 기사가 복잡한 앱 전체 CSS를 받지 않게 합니다. 컴포넌트가 있을 때 필요한 스타일을 로드합니다. 너무 많은 작은 파일은 일정 관리와 유지보수 비용을 더하므로 의미 있는 기능을 경계로 삼습니다.

빌드를 제어한다면 불필요한 @import 사슬을 없앱니다. 한 CSS 안에서 다른 CSS를 발견하면 요청 의존성이 늘어납니다. 필수 스타일을 HTML에서 직접 연결하거나 빌드에서 import를 해결합니다. 모든 import 규칙이 느리다는 뜻이 아니라 요청 사슬의 문제입니다.

런타임 CSS-in-JS를 점검합니다. 일부 방식은 JavaScript 다운로드와 실행 뒤에 스타일을 삽입해 발견을 늦추고 재계산을 더합니다. 서버나 빌드 추출이 경로를 바꿀 수 있지만 라이브러리, 프레임워크, 버전에 따라 다릅니다.

컴포넌트 소스만 보지 말고 생성 응답을 검사합니다. 이미 CSS를 추출하는 설정도 있고 런타임 주입 설정도 있습니다. 라이브러리를 바꾸기 전에 초기 HTML의 사용 가능한 스타일을 확인합니다.

의존성에 따라 async, defer, 모듈을 선택합니다

독립 스크립트는 다른 순서를 허용하지만 DOM이나 라이브러리에 의존하는 코드는 그렇지 않을 수 있습니다. 모든 것에 async를 붙이면 경쟁 조건이 생길 수 있습니다.

외부 스크립트의 일반 모드는 다음과 같습니다.

형태 다운로드 실행과 순서
속성 없는 클래식 파서를 멈출 수 있음 파싱 재개 전에 실행
defer 클래식 파싱과 병행 파싱 후 문서 순서로, DOMContentLoaded 전 실행
async 클래식 파싱과 병행 준비되면 실행, 문서 순서 보장 없음
async 없는 type="module" 파싱 중 모듈 그래프 다운로드 기본 지연 실행, 그래프에 의존성 포함

의존하는 클래식 파일은 defer로 순서를 유지합니다.

html
<script src="/scripts/vendor.js" defer></script>
<script src="/scripts/navigation.js" defer></script>

모듈 진입점은 유형을 선언하고 import로 의존성을 표현합니다.

html
<script type="module" src="/scripts/app.js"></script>

정말 독립적인 스크립트에는 async가 적절할 수 있습니다.

html
<script src="/scripts/independent-widget.js" async></script>

예제는 서로 다른 자원을 위한 선택지이며 동일 기능 세 가지를 함께 넣으라는 지시가 아닙니다. 일반 인라인 클래식 스크립트에서 defer는 유용한 효과가 없습니다. 모듈은 기본적으로 지연되며 async를 붙이면 그래프 실행 시점이 바뀝니다. 레거시 혼합 페이지를 바꾸기 전에 script 문서를 확인합니다.

변경 후 메뉴, 검색, 검증, 결제, 동의 흐름을 해당하는 만큼 시험하고 Console 오류를 확인합니다. 뒤에 있는 인라인 클래식 코드가 defer 라이브러리를 너무 일찍 참조할 수 있습니다. 우연히 한 번 성공할 때까지 속성을 바꾸지 말고 의존성을 수정합니다.

구체적인 지연에 preload와 preconnect를 씁니다

Preload는 현재 페이지 자원을 일반 발견보다 먼저 알립니다. CSS 적용, 스크립트 실행, 이미지 표시를 직접 하지 않습니다. 정상 소비자가 필요하고 나중 요청과 일치해야 합니다.

이 예제는 CSS가 곧 쓸 글꼴을 앞당깁니다.

html
<link rel="preload" href="/fonts/site-regular.woff2"
      as="font" type="font/woff2" crossorigin>

as와 요청 모드를 올바르게 지정합니다. 틀린 힌트는 중복 또는 미사용 다운로드를 만들 수 있습니다. 모든 것을 미리 로드하면 이전에 늦췄던 자원까지 중요한 요청과 경쟁합니다.

Preconnect는 연결 수립을 다루며 파일 크기나 실행 시간을 줄이지 않습니다. 중요한 외부 출처가 늦게 발견될 때 유용할 수 있습니다. 일찍 필요한 출처에 제한하고 적절한 CORS 모드를 씁니다. 문서에 이미 사용한 같은 출처 연결은 파일이 더 있다는 이유만으로 새 힌트가 필요하지 않습니다.

modulepreload는 모듈 다운로드와 준비에 특화된 의미가 있습니다. 프레임워크가 만든 힌트가 있으면 출력 내용을 검사해 사용합니다. 그래프와 요청 모드를 이해하지 않고 일반 스크립트 preload로 기계적으로 바꾸지 않습니다.

Coverage를 삭제 명령이 아닌 기록으로 봅니다

Coverage는 기록한 활동 중 CSS와 JavaScript의 사용 영역을 보여줍니다. 새로고침으로 기록을 시작한 뒤 메뉴, 대화상자, 스크롤, 탭, 다른 컴포넌트가 필요한 반응형 화면을 사용합니다.

미사용은 그 기록에서 쓰지 않았다는 뜻입니다. 다른 경로, 늦은 상호작용, 오류, 언어에 필요할 수 있습니다. 데스크톱만 시험하면 모바일 규칙이 불필요해 보입니다. 한 번의 빨간 영역을 전부 지우지 않습니다.

큰 미사용 번들을 원래 모듈, 템플릿, 플러그인으로 연결합니다. 없어진 기능은 제거하고 경로별 자원은 필요한 곳에서만 로드하며 도달 불가능한 코드는 빌드에서 제거합니다. 다음 빌드가 덮어쓸 생성 파일을 직접 편집하지 않습니다.

제거 후 재검사하고 포함한 상호작용을 기록합니다. Coverage는 의존 그래프와 시각·기능 검사와 함께 쓸 때 유용합니다. 동적 앱이 모든 상태에서 특정 함수를 필요로 하지 않는다는 증명은 아닙니다.

CMS 변환을 함께 확인합니다

테마, 캐시, 최적화 플러그인이 각각 스타일을 합치고 스크립트를 늦추고 힌트를 바꿀 수 있습니다. 최종 HTML은 설정 화면과 크게 다를 수 있으므로 응답과 시작자를 검사합니다.

모든 지연 옵션을 동시에 켜지 않습니다. 이해 가능한 기준을 유지하고 한 계층씩 바꾸며 핵심 흐름을 확인합니다. 일부 스크립트는 인라인 초기화보다 먼저 필요하고 일부 스타일은 기본 데스크톱 검사에 없는 배너나 모바일 메뉴를 지원합니다.

템플릿 변경 후 관련 캐시를 무효화하고 새 응답과 저장 응답을 모두 확인합니다. 불일치는 브라우저 일정 문제가 아니라 서로 다른 페이지 버전일 수 있습니다. 설정을 되돌릴 방법도 유지합니다.

더 이른 올바른 화면을 검증합니다

같은 조건에서 탐색을 반복하고 첫 유용한 콘텐츠, LCP, 이동, 메인 스레드 작업을 비교합니다. 전체 페이지와 중요한 상호작용을 확인합니다. 차단 요청 감소는 화면과 기능이 정상일 때 유용한 근거입니다.

좁은 화면, 긴 번역 제목, 빈 캐시를 포함합니다. 글꼴 발견이 계속 늦다면 웹 글꼴 최적화로 이어갑니다. 변경한 의존성을 성능 체크리스트에 기록하고 페이지 속도 가이드로 다음 병목을 조사합니다. 반복 가능한 테스트에서도 이점이 유지되는 변경을 남깁니다.

관련 가이드