더 나은 Core Web Vitals를 위한 Next.js의 렌더링 차단 CSS 제거

더 빠른 Core Web Vitals를 위해 Next.js에서 렌더링 차단 CSS 제거

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-03-09

Next.js에서 렌더링 차단 CSS 제거

CSS는 렌더링을 차단합니다. 브라우저는 <head> 내의 모든 스타일시트를 다운로드하고 파싱할 때까지 단 하나의 픽셀도 그리지 않습니다. Next.js 앱에서는 기본적으로 두 개의 외부 CSS 파일(글로벌 스타일시트 하나, 페이지 특정 스타일시트 하나)을 의미합니다. 빠른 3G 연결에서 이 두 파일을 다운로드하는 데는 브라우저가 렌더링을 시작하기도 전에 600ms가 소요됩니다. 이는 여러분의 First Contentful PaintLargest Contentful Paint에 고스란히 600ms가 추가된다는 뜻입니다.

마지막 검토: 2026년 3월, Arjen Karel

평균적으로 제 모든 클라이언트와 모든 기기에서 렌더링 차단 CSS로 인해 약 200ms의 지연이 측정됩니다. 2025 Web Almanac에 따르면 모바일 페이지의 85%가 여전히 렌더링 차단 리소스 감사를 통과하지 못합니다. 중간값(median) 페이지는 총 79 KB 크기의 8개 CSS 파일을 로드합니다. 느린 모바일 연결에서 렌더링 지연은 전체 LCP 시간의 69%를 차지할 수 있습니다. 이제 이것을 해결할 시간입니다.

App Router vs Pages Router

해결 방법은 어떤 Next.js 라우터를 사용하느냐에 따라 다릅니다. Pages Router(_document.tsx 사용)와 App Router(app/layout.tsx 사용)는 CSS 전송을 다르게 처리합니다. 스트리밍은 critters가 HTML을 처리하는 방식과 호환되지 않으므로, critters를 통한 중요 CSS 인라이닝은 App Router에서 작동하지 않습니다. App Router를 사용 중이라면 옵션 4로 건너뛰세요.

Next.js 13.4 이후부터 App Router가 기본값입니다. 하지만 많은 프로덕션 앱이 여전히 Pages Router로 실행되고 있으며, 아래의 처음 세 가지 옵션은 여전히 유효합니다.

옵션 1: 중요 CSS 생성 (Pages Router)

가장 빠른 옵션은 중요 CSS를 생성하는 것입니다. 중요 CSS는 페이지의 표시되는 부분을 렌더링하는 데 필요한 CSS 규칙의 모음입니다. 이러한 규칙은 <head>에 인라인으로 배치됩니다. 그런 다음 브라우저가 계속 렌더링하는 동안 원본 CSS 파일이 병렬로 다운로드됩니다. 원본 CSS 파일이 다운로드되면 페이지에 주입됩니다.

중요 CSS는 Next.js에서 실험적 기능으로 제공됩니다. next.config.jsexperimental: { optimizeCss: true }를 추가하세요. Next.js는 내부적으로 critters 라이브러리를 번들로 제공하므로 별도로 설치할 필요가 없습니다. 원본 critters 패키지는 더 이상 사용되지 않지만 (beasties 포크로 대체됨), Next.js는 아직 전환을 진행하지 않았습니다.

const nextConfig = {
  reactStrictMode: true,
  experimental: { optimizeCss: true }
}

이 옵션은 Pages Router에서만 작동합니다. critters는 완전히 렌더링된 HTML이 필요하기 때문에 스트리밍과 호환되지 않아 App Router를 지원하지 않습니다.

옵션 2: head에 모든 CSS 인라인 처리 (Pages Router)

Next.js 앱에서 실험적 기능을 활성화하고 싶지 않다면 수동으로 CSS를 인라인하는 것을 고려해 볼 수 있습니다. 사용자 정의 head 섹션을 만들고 _document.tsx에서 참조하세요.

단점은 중요 규칙뿐만 아니라 모든 것을 인라인하기 때문에 첫 번째 방법보다 인라인 CSS의 크기가 더 커진다는 것입니다. 그러나 스타일시트를 깔끔하고 가볍게 유지한다면, 이는 Next.js 앱의 Core Web Vitals를 크게 개선할 것입니다.

import { Html, Head, Main, NextScript } from "next/document";
import { readFileSync } from "fs";
import { join } from "path";

class InlineStylesHead extends Head {
  getCssLinks: Head["getCssLinks"] = ({ allFiles }) => {
    const { assetPrefix } = this.context;
    if (!allFiles || allFiles.length === 0) return null;
    return allFiles
      .filter((file: any) => /\.css$/.test(file))
      .map((file: any) => (
        <style
          key={file}
          nonce={this.props.nonce}
          data-href={`${assetPrefix}/_next/${file}`}
          dangerouslySetInnerHTML={{
            __html: readFileSync(join(process.cwd(), ".next", file), "utf-8"),
          }}
        />
      ));
  };
}

export default function Document() {
  return (
    <Html>
      <InlineStylesHead />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

옵션 3: Styled Components (Pages Router)

Styled Components는 JavaScript 파일 내에 CSS를 작성할 수 있게 해주는 CSS-in-JS 도구입니다. 클래스 이름의 스코프를 지정하고, 사용하지 않는 CSS를 자동으로 제거하며, 컴포넌트와 함께 스타일을 관리할 수 있습니다. Core Web Vitals 측면에서 볼 때, 이는 해당 페이지에 필요한 스타일만 주입된다는 것을 의미합니다.

next.config.jscompiler: { styledComponents: true }를 추가하고, styled-components를 설치한 다음(yarn add styled-componentsyarn add -D @types/styled-components), styled components의 서버 사이드 렌더링을 지원하도록 _document.js를 업데이트하세요.

const nextConfig = {
    reactStrictMode: true,
    compiler: {
    styledComponents: true,
  }
}
import Document, { DocumentContext } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      };
    } finally {
      sheet.seal();
    }
  }
}

App Router와 styled-components를 함께 사용하는 경우 다른 설정이 필요합니다. useServerInsertedHTML을 사용하여 스타일 레지스트리 클라이언트 컴포넌트를 만들고 루트 레이아웃을 이것으로 감싸세요. Styled components는 App Router의 클라이언트 컴포넌트에서만 작동합니다. 전체 패턴은 Next.js CSS-in-JS 가이드를 참조하세요.

옵션 4: inlineCss를 사용한 CSS 인라인 처리 (App Router, Next.js 15+)

App Router의 경우 Next.js 15에서 experimental.inlineCss가 도입되었습니다. 이는 모든 <link> 스타일시트 태그를 인라인 <style> 태그로 대체하여, 렌더링을 차단하는 폭포수(waterfall) 현상을 완전히 제거합니다.

const nextConfig = {
  experimental: {
    inlineCss: true,
  }
}

이 기능은 아직 실험적이며 Next.js 팀은 프로덕션 환경에서의 사용을 권장하지 않습니다. 이는 중요 CSS만이 아니라 모든 CSS를 인라인하기 때문에 HTML 크기가 증가합니다. Tailwind CSS를 사용하는 사이트의 경우, Tailwind가 작고 원자적인(atomic) CSS 번들을 생성하므로 이러한 트레이드오프가 잘 맞습니다. 스타일시트가 큰 사이트의 경우, HTML의 비대화는 Time to First Byte를 저해할 수 있습니다.

CSS Modules 및 Tailwind CSS에 대한 참고 사항

CSS Modules와 Tailwind CSS는 오늘날 Next.js에서 가장 인기 있는 두 가지 스타일링 접근 방식입니다. 둘 다 빌드 시 표준 CSS 파일로 컴파일되며 <link> 태그로 제공됩니다. 즉, 기본적으로 렌더링을 차단합니다.

Tailwind는 한 가지 장점이 있습니다. 사용하지 않는 클래스를 제거(purge)하므로 결과물이 일반적으로 매우 작습니다(gzipped 기준 보통 10 KB 미만). 10 KB 파일의 렌더링 차단 영향은 미미합니다. CSS Modules는 사용하지 않는 스타일에 주의하지 않으면 크기가 더 커질 수 있습니다.

어느 쪽이든, 위의 모든 옵션이 적용됩니다. 렌더링 차단 CSS를 완전히 제거하려면 원하는 스타일링 접근 방식을 optimizeCss(Pages Router) 또는 inlineCss(App Router)와 결합하세요.

어떤 옵션을 사용해야 합니까?

라우터와 CSS 전략에 따라 다릅니다.

  • App Router + Tailwind: inlineCss를 사용하세요. 작은 CSS 번들 덕분에 인라이닝이 실용적입니다.
  • App Router + 대용량 CSS: inlineCss가 안정화되기를 기다리거나 사용자 정의 beasties 설정을 사용하세요.
  • Pages Router: optimizeCss(옵션 1)를 사용하세요. 가장 간단한 해결책이며 중요한 것만 인라인합니다.
  • Pages Router + CSS-in-JS: 위에서 보여드린 SSR 패턴과 함께 styled-components 또는 Emotion을 사용하세요.

CoreDash로 모니터링되는 Next.js 사이트들에서 인라인된 중요 CSS를 사용하는 사이트는 기본 CSS 전송 방식에 비해 중간값(median) 기준으로 400ms의 FCP 개선을 보여줍니다. 이는 우수한 FCP 점수와 평범한 FCP 점수의 차이입니다.

어떤 옵션을 선택하든 Real User Monitoring을 통해 결과를 확인하세요. 랩(Lab) 점수는 일어날 수 있는 일을 알려줍니다. 필드(Field) 데이터는 실제로 일어난 일을 알려줍니다.

CSS 및 JavaScript가 렌더링 파이프라인과 상호 작용하는 방법에 대한 자세한 내용은 렌더링 차단 리소스 수정에 대한 완벽한 가이드를 참조하세요. Next.js 앱을 최적화하는 경우 서드 파티 스크립트 수정 및 Next.js에서 Core Web Vitals 측정하기 가이드도 확인하세요. 로딩 전략에 대한 더 폭넓은 시각을 보려면 브라우저가 무엇을 먼저 가져올지 결정하는 방법을 다루는 리소스 우선순위 지정 가이드를 읽어보세요.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

관리 안 하는 순간 퍼포먼스는 무너집니다.

모니터링, 퍼포먼스 버짓, 프로세스까지 세팅합니다. 일회성 수정과 진짜 해결의 차이가 바로 거기서 갈립니다.

한번 얘기해봐요
더 나은 Core Web Vitals를 위한 Next.js의 렌더링 차단 CSS 제거Core Web Vitals 더 나은 Core Web Vitals를 위한 Next.js의 렌더링 차단 CSS 제거