Next.js 프로젝트들을 진행 중 외부 스크립트를 로드하는 다양한 방식과 그에 따른 장단점을 분석한 내용입니다. 기술적 한계와 실용성을 고려해 정리해보았습니다.
1. await import 사용 방식
- 특징:
- 런타임에 ES 모듈을 동적으로 로드하는 방식.
- Webpack은 정적으로 분석 가능한 경로만 처리 가능하며, 동적 경로는 처리 불가.
- Webpack의 처리 방식을 우회하려면 webpackIgnore 옵션 사용.
- 장점:
- 모듈 방식으로 코드 분리가 가능.
- 단점:
- CSR 페이지 전환 시 스크립트 언마운트 문제가 발생 가능.
- 해당 문제는 로드 후의 cleanup 작업을 제대로 하지 않을 경우 발생할 수 있음
- Webpack 설정에 따라 사용이 제한될 수 있음.
- CSR 페이지 전환 시 스크립트 언마운트 문제가 발생 가능.
- 보완 코드 예시:
- 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');
}}
/>
</>
);
}
결론 및 추천 방식
- 단순 실행 스크립트:
- Next.js의 Script 컴포넌트를 활용하는 방식이 가장 안정적.
- 동적 로드 및 관리가 필요한 경우:
- createElement와 useEffect를 조합하여 로드 및 cleanup 관리.
- 모듈 방식:
- await import를 사용하되, Webpack의 webpackIgnore 옵션으로 처리.
- 언마운트 문제 발생 시 cleanup 작업 필요.
- 보안과 효율성 고려:
- eval이나 dangerouslySetInnerHTML 사용은 최대한 피해야 함.
- CSP 정책 준수 필요.
이러한 방식들을 상황에 맞게 조합하여 사용하는 것이 가장 효율적입니다.