How to load and optimize scripts

김동현·2026년 3월 4일

next.js 공식문서 번역

목록 보기
32/79

레이아웃 스크립트 (Layout Scripts)

여러 라우트(페이지)에서 공통으로 서드파티 스크립트를 불러오고 싶다면, next/script를 임포트(import)해서 레이아웃(layout) 컴포넌트에 직접 스크립트를 넣어주시면 돼요.

//filename="app/dashboard/layout.tsx" switcher
import Script from 'next/script'

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <>
      <section>{children}</section>
      <Script src="https://example.com/script.js" />
    </>
  )
}
//filename="app/dashboard/layout.js" switcher
import Script from 'next/script'

export default function DashboardLayout({ children }) {
  return (
    <>
      <section>{children}</section>
      <Script src="https://example.com/script.js" />
    </>
  )
}

사용자가 특정 폴더 라우트(예: dashboard/page.js)나 그 안에 중첩된 라우트(예: dashboard/settings/page.js)에 접속할 때 이 서드파티 스크립트를 가져오게 됩니다. 사용자가 같은 레이아웃 안에서 이리저리 여러 라우트를 돌아다니더라도, Next.js가 알아서 이 스크립트를 딱 한 번만 로드되도록 꼼꼼하게 챙겨준답니다.

💡 [강사님의 실무 꿀팁!]
특정 레이아웃에만 스크립트를 묶어두는 건 아주 좋은 습관이에요. 예를 들어 '관리자 페이지(dashboard)' 레이아웃에만 필요한 통계 스크립트가 있다면, 일반 고객들이 보는 페이지에서는 불필요하게 로드할 필요가 없겠죠? 이렇게 레이아웃 단위를 활용하면 코드 분할과 성능 최적화를 동시에 챙길 수 있습니다!

애플리케이션 스크립트 (Application Scripts)

만약 애플리케이션의 모든 라우트에서 서드파티 스크립트를 불러와야 한다면, next/script를 임포트하고 최상위(root) 레이아웃에 스크립트를 직접 포함시키면 됩니다.

//filename="app/layout.tsx" switcher
import Script from 'next/script'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script src="https://example.com/script.js" />
    </html>
  )
}
//filename="app/layout.js" switcher
import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script src="https://example.com/script.js" />
    </html>
  )
}

이렇게 설정해 두면, 우리 앱의 어떤 라우트에 접속하든 간에 이 스크립트가 로드되고 실행될 거예요. 여기서도 마찬가지로, 사용자가 여러 페이지를 이동하더라도 Next.js가 스크립트가 오직 한 번만 로드되도록 보장해 줍니다.

추천 사항(Recommendation): 성능에 불필요한 악영향을 주지 않으려면, 서드파티 스크립트는 꼭 필요한 특정 페이지나 레이아웃에만 포함시키는 것을 강력히 권장해요.

💡 [강사님의 실무 꿀팁!]
Root Layout(app/layout.tsx)은 우리 웹사이트의 대문 같은 곳이에요. 여기에 무거운 스크립트(예: 용량이 큰 마케팅 픽셀 등)를 무턱대고 넣으면 사이트 전체의 초기 로딩 속도(LCP)가 느려질 수 있어요. 전역으로 꼭 필요한 스크립트(예: 구글 애널리틱스, 전역 에러 트래킹 등)만 신중하게 골라서 넣으세요!

로딩 전략 (Strategy)

next/script의 기본 동작만으로도 어떤 페이지나 레이아웃에서든 서드파티 스크립트를 불러올 수 있지만, strategy라는 속성을 사용하면 스크립트가 '언제' 로드될지 아주 세밀하게 튜닝할 수 있어요.

  • beforeInteractive: Next.js 코드가 실행되기 전, 그리고 페이지의 하이드레이션(hydration)이 일어나기 전에 스크립트를 로드합니다.
  • afterInteractive: (기본값) 스크립트를 일찍 로드하긴 하지만, 페이지의 하이드레이션이 어느 정도 진행된 후에 로드합니다.
  • lazyOnload: 브라우저가 유휴 시간(idle time)일 때, 즉 다른 급한 작업들을 먼저 처리하고 나서 나중에 스크립트를 로드합니다.
  • worker: (실험적 기능) 웹 워커(web worker)에서 스크립트를 백그라운드로 로드합니다.

각 전략과 구체적인 사용 사례에 대해 더 깊이 알고 싶다면 next/script API 레퍼런스 문서를 참고해 보세요.

💡 [강사님의 실무 부연설명!]

  • 하이드레이션(Hydration)이란? 화면에 정적인 HTML이 먼저 그려진 후, 자바스크립트가 붙어서 버튼 클릭 같은 인터랙션이 가능해지는 과정을 말해요.
  • 어떻게 골라야 할까? > - 봇 탐지 솔루션이나 쿠키 동의 팝업처럼 화면이 뜨자마자(또는 그보다 먼저) 실행되어야만 하는 건 beforeInteractive를 쓰세요.
    • 구글 태그 매니저(GTM)나 애널리틱스처럼 페이지 분석을 위한 건 기본값인 afterInteractive가 딱 좋아요.
    • 챗봇 위젯이나 페이스북 좋아요 버튼처럼 사용자가 당장 쓰지 않아도 되고 좀 나중에 떠도 괜찮은 건 무조건 lazyOnload를 쓰세요! 이렇게만 분리해도 사이트 체감 속도가 확 빨라집니다.

웹 워커로 스크립트 덜어내기 (Offloading Scripts To A Web Worker) - 실험적 기능

경고(Warning): worker 전략은 아직 완전히 안정화된 기능이 아니며, App Router 방식에서는 아직 작동하지 않습니다. 주의해서 사용해 주세요.

worker 전략을 사용하는 스크립트들은 Partytown(파티타운)이라는 도구를 통해 메인 스레드(Main thread)에서 떨어져 나와 별도의 웹 워커(web worker) 환경에서 실행돼요. 이렇게 하면 화면을 그리고 유저의 클릭을 처리하는 메인 스레드를 우리 애플리케이션의 핵심 코드를 실행하는 데에만 온전히 집중시킬 수 있어서, 사이트 성능을 크게 끌어올릴 수 있답니다.

이 전략은 아직 실험적인 단계라서, next.config.js 파일에 nextScriptWorkers 플래그를 활성화해 주어야만 사용할 수 있어요.

//filename="next.config.js"
module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
}

설정을 추가한 뒤 개발 서버를 실행해 보세요. 그러면 Next.js가 셋업을 마무리하기 위해 필요한 패키지를 설치하라고 친절하게 안내해 줄 거예요.

pnpm dev
npm run dev
yarn dev
bun dev

터미널을 보면 아마 이런 식의 안내 문구가 나올 겁니다: "Partytown을 설치하려면 npm install @qwik.dev/partytown 명령어를 실행해 주세요." (Please install Partytown by running npm install @qwik.dev/partytown)

설치가 모두 끝나고 나면, 컴포넌트에서 strategy="worker"라고 적어주기만 해도 우리 애플리케이션 안에서 자동으로 Partytown이 생성되고, 스크립트 실행 작업을 웹 워커로 싹 넘겨주게(offload) 됩니다.

//filename="pages/home.tsx" switcher
import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="worker" />
    </>
  )
}
//filename="pages/home.js" switcher
import Script from 'next/script'

export default function Home() {
  return (
    <>
      <Script src="https://example.com/script.js" strategy="worker" />
    </>
  )
}

웹 워커에서 서드파티 스크립트를 불러올 때는 성능상 큰 이점이 있지만, 그만큼 반드시 고려해야 할 트레이드오프(장점과 단점의 균형)도 여럿 존재해요. 이 부분에 대한 더 자세한 정보는 Partytown의 트레이드오프(tradeoffs) 문서를 꼭 한 번 정독해 보시길 추천합니다.

💡 [강사님의 실무 꿀팁!]
자바스크립트는 기본적으로 '싱글 스레드(Single Thread)'로 동작해서 한 번에 하나의 일만 처리할 수 있어요. 무거운 스크립트가 돌고 있으면 유저가 버튼을 눌러도 화면이 버벅이게 되죠(TBT 증가). 웹 워커를 쓰면 이 무거운 작업을 다른 스레드(일꾼)에게 넘겨버릴 수 있어서 아주 좋아요! 하지만 워커 안에서는 웹페이지의 DOM 요소(document.getElementById 등)에 직접 접근하는 데 제약이 생길 수 있으니 적용 후에 기능이 잘 동작하는지 꼼꼼히 테스트해야 해요.

인라인 스크립트 (Inline Scripts)

외부 .js 파일에서 불러오지 않고 컴포넌트 내부에서 직접 작성하는 '인라인 스크립트'도 Script 컴포넌트를 통해 안전하게 사용할 수 있어요. 중괄호({})를 열고 그 안에 자바스크립트 코드를 직접 적어 넣는 방식이 가능합니다.

<Script id="show-banner">
  {`document.getElementById('banner').classList.remove('hidden')`}
</Script>

또는 dangerouslySetInnerHTML 이라는 리액트 속성을 사용해서 코드를 삽입할 수도 있죠.

<Script
  id="show-banner"
  dangerouslySetInnerHTML={{
    __html: `document.getElementById('banner').classList.remove('hidden')`,
  }}
/>

경고(Warning): 인라인 스크립트를 작성할 때는 반드시 id 속성을 지정해 주셔야 합니다! 그래야 Next.js가 해당 스크립트를 정확히 추적하고 최적화해 줄 수 있거든요.

💡 [강사님의 실무 꿀팁!]
인라인 스크립트를 쓸 때 id 값을 빠뜨려서 Next.js가 스크립트를 여러 번 실행하거나 오류를 뿜어내는 경우를 실무에서 정말 자주 봅니다. 어떤 스크립트인지 알 수 있는 명확하고 고유한 id 값(예: id="google-analytics-init")을 꼭 넣어주는 습관을 들이세요!

스크립트 실행 후 추가 코드 실행하기 (Executing Additional Code)

특정 이벤트가 끝난 뒤에 우리만의 추가적인 코드를 실행하고 싶다면, Script 컴포넌트에 이벤트 핸들러를 달아줄 수 있어요.

  • onLoad: 스크립트를 끝까지 다 불러온(load) 직후에 코드를 실행합니다.
  • onReady: 스크립트를 다 불러온 후, 그리고 해당 컴포넌트가 마운트(mount)될 때마다 코드를 실행합니다.
  • onError: 스크립트를 불러오는 데 실패했을 때 에러 처리 코드를 실행합니다.

한 가지 주의할 점은, 이런 이벤트 핸들러들은 파일 맨 첫 줄에 "use client"라고 명시되어 있는 클라이언트 컴포넌트(Client Component) 안에서 next/script를 임포트해 사용할 때만 정상적으로 동작한다는 거예요!

//filename="app/page.tsx" switcher
'use client'

import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        onLoad={() => {
          console.log('Script has loaded')
        }}
      />
    </>
  )
}
//filename="app/page.js" switcher
'use client'

import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        onLoad={() => {
          console.log('Script has loaded')
        }}
      />
    </>
  )
}

각 이벤트 핸들러에 대해 더 자세히 알아보고 예시 코드도 구경하고 싶으시다면 next/script API 레퍼런스의 onload 섹션을 참고해 주세요.

💡 [강사님의 실무 꿀팁!]
카카오맵 API나 아임포트(결제), 혹은 외부 SDK를 연동할 때 이 onLoad가 정말 구세주 같은 역할을 합니다. 스크립트가 로드되지도 않았는데 window.kakao 같은 객체에 접근하려고 하면 에러가 나잖아요? onLoad 안에서 초기화 로직을 작성하면 스크립트가 완전히 준비된 상태를 보장받을 수 있어서 아주 안전하게 개발할 수 있답니다.

추가적인 속성들 지정하기 (Additional Attributes)

일반적인 <script> HTML 태그에 쓸 수 있는 DOM 속성 중에서 Next.js의 Script 컴포넌트가 자체적으로 사용하지 않는 속성들도 참 많죠. 보안을 위한 nonce 속성이나 커스텀 데이터를 담는 data-* 속성들(custom data attributes)이 대표적이에요.

Script 컴포넌트에 이런 추가적인 속성들을 적어주면, Next.js가 최종적으로 HTML 화면에 렌더링하는 최적화된 <script> 태그 안으로 그 속성들을 알아서 그대로 넘겨줍니다(forwarding).

//filename="app/page.tsx" switcher
import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        id="example-script"
        nonce="XUENAJFW"
        data-test="script"
      />
    </>
  )
}
//filename="app/page.js" switcher
import Script from 'next/script'

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/script.js"
        id="example-script"
        nonce="XUENAJFW"
        data-test="script"
      />
    </>
  )
}

💡 [강사님의 실무 꿀팁!]
기업의 큰 프로젝트에서는 보안 팀에서 CSP(콘텐츠 보안 정책)를 아주 엄격하게 설정하는 경우가 많아요. 인가되지 않은 외부 스크립트가 마음대로 실행되는 걸 막기 위해서죠. 이때 백엔드나 서버에서 발급해 준 고유한 암호화 키 같은 걸 nonce 속성으로 넘겨주어야만 스크립트가 정상 실행되곤 하는데, 위 예시처럼 편하게 값만 쏙 넣어주시면 완벽하게 해결된답니다.

API 레퍼런스 (API Reference)

next/script API에 대해 더 깊이 알아보세요.

  • Script 컴포넌트(Script Component)
    • 내장된 next/script 컴포넌트를 사용해서 여러분의 Next.js 애플리케이션 안의 서드파티 스크립트를 최적화해 보세요.

모든 문서의 전체적인 구조(semantic overview)를 한눈에 파악하시려면 사이트맵 문서(/docs/sitemap.md)를 확인해 주세요.

이용 가능한 모든 문서의 목록(index)을 보시려면 llms.txt 문서(/docs/llms.txt)를 확인해 주세요.

profile
프론트에_가까운_풀스택_개발자

0개의 댓글