본문 바로가기
FrontEnd

Next.js 외부 스크립트 로드 방식 분석 및 정리

by E_van 2024. 12. 2.

Next.js 프로젝트들을 진행 중 외부 스크립트를 로드하는 다양한 방식과 그에 따른 장단점을 분석한 내용입니다. 기술적 한계와 실용성을 고려해 정리해보았습니다.


1. await import 사용 방식

  • 특징:
    • 런타임에 ES 모듈을 동적으로 로드하는 방식.
    • Webpack은 정적으로 분석 가능한 경로만 처리 가능하며, 동적 경로는 처리 불가.
    • Webpack의 처리 방식을 우회하려면 webpackIgnore 옵션 사용.
  • 장점:
    • 모듈 방식으로 코드 분리가 가능.
  • 단점:
    • CSR 페이지 전환 시 스크립트 언마운트 문제가 발생 가능.
      • 해당 문제는 로드 후의 cleanup 작업을 제대로 하지 않을 경우 발생할 수 있음
    • Webpack 설정에 따라 사용이 제한될 수 있음.
  • 보완 코드 예시:
    • React의 useEffect와 cleanup 함수를 활용하여 언마운트 시 스크립트 정리를 수행.
useEffect(() => {
  const loadScript = async () => {
    const module = await import(/* webpackIgnore: true */ 'https://example.com/script.js');
    module.default();
  };

  loadScript();

  return () => {
    console.log('Cleanup external script');
  };
}, []);

 


2. Next Head 및 dangerouslySetInnerHTML 활용 방식

  • 특징:
    • Next.js에서 <Head>와 <script> 태그를 사용하여 외부 스크립트를 로드.
    • dangerouslySetInnerHTML은 보안 이슈 및 렌더링 제한 문제로 권장되지 않음.
  • 장점:
    • 외부 스크립트를 HTML 수준에서 삽입 가능.
  • 단점:
    • Next.js의 Script와 혼용 시 로드 순서 및 우선순위 관리가 복잡.
    • 보안 취약점(CSP 위반) 우려.
  • 코드 예시:
import Head from 'next/head';

export default function Page() {
  return (
    <>
      <Head>
        <script src="https://example.com/script.js" async />
      </Head>
    </>
  );
}

3. Fetch 및 Next.js API를 활용한 로드 방식

  • 특징:
    • Fetch를 통해 스크립트를 가져오고 DOM에 동적으로 삽입.
    • 스크립트 내에서 실행 함수 호출 시 eval 또는 Function 사용 필요.
  • 장점:
    • 스크립트 내용 조작 가능.
    • Next.js API 라우트를 활용하면 동적 경로 관리가 쉬움.
  • 단점:
    • eval 사용 시 보안 문제 발생.
    • 스크립트 로드 순서 보장이 어렵고 CSP 정책에 위배될 가능성.
  • 코드 예시:
useEffect(() => {
  fetch('https://example.com/script.js')
    .then(res => res.text())
    .then(scriptContent => {
      const script = document.createElement('script');
      script.textContent = scriptContent;
      document.body.appendChild(script);
    });

  return () => {
    console.log('Cleanup fetch script');
  };
}, []);

4. createElement와 useEffect를 사용한 스크립트 로드

  • 특징:
    • DOM API를 활용해 <script> 태그를 동적으로 생성하여 로드.
    • 스크립트를 컴포넌트에 종속시키며 async, defer 속성으로 로드 순서 제어.
  • 장점:
    • 안정적이고 브라우저 기본 기능을 활용.
    • 보안 및 호환성이 높음.
  • 단점:
    • 컴포넌트와 종속되어 동작하므로 전역적으로 사용할 때 관리 필요.
  • 코드 예시:
useEffect(() => {
  const script = document.createElement('script');
  script.src = 'https://example.com/script.js';
  script.type = 'module'
  script.async = true;
  document.body.appendChild(script);

  return () => {
    document.body.removeChild(script);
  };
}, []);

5. Next.js Script 컴포넌트 사용

  • 특징:
    • Next.js에서 권장하는 방식으로, Script 컴포넌트를 활용해 외부 스크립트 로드.
    • 로드 전략(beforeInteractive, afterInteractive, lazyOnload) 제공.
  • 장점:
    • 최적화된 방식으로 스크립트 로드 순서 관리가 용이.
    • 광고 스크립트나 추적 코드와 같은 단순 실행 스크립트에 적합.
  • 단점:
    • 커스텀 함수 호출 및 모듈 방식 처리에는 한계.
  • 코드 예시:
import Script from 'next/script';

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

결론 및 추천 방식

  1. 단순 실행 스크립트:
    • Next.js의 Script 컴포넌트를 활용하는 방식이 가장 안정적.
  2. 동적 로드 및 관리가 필요한 경우:
    • createElement와 useEffect를 조합하여 로드 및 cleanup 관리.
  3. 모듈 방식:
    • await import를 사용하되, Webpack의 webpackIgnore 옵션으로 처리.
    • 언마운트 문제 발생 시 cleanup 작업 필요.
  4. 보안과 효율성 고려:
    • eval이나 dangerouslySetInnerHTML 사용은 최대한 피해야 함.
    • CSP 정책 준수 필요.

이러한 방식들을 상황에 맞게 조합하여 사용하는 것이 가장 효율적입니다.