Next.js 성능 최적화
규칙
- 이미지: 항상
next/image 사용, 명시적인 sizes 속성 필수, priority는 LCP 히어로 이미지에만 적용. 폰트: next/font 사용 (렌더링 차단 Google <link> 사용 금지).
- 먼저 측정:
@next/bundle-analyzer, next build 출력, Lighthouse를 통해 가장 큰 덩어리를 먼저 수정.
dynamic(() => import(...), { ssr: false })로 무거운 클라이언트 전용 위젯(차트, 에디터)을 코드 분할. 가능한 경우 초기 JS 유지 < 200KB (gzip).
- 적극적으로 캐싱: 준동적 페이지에 대해 정적 +
revalidate (ISR); TTFB 차단 대신 loading.tsx/Suspense를 사용해 느린 부분을 스트리밍.
- 폭포수 문제 방지: RSC 내에서 병렬화, 반복
fetch 호출 메모이제이션 (요청별 자동 중복 제거), 비용이 큰 계산에는 unstable_cache 사용.
패턴
import Image from 'next/image';
<Image src="/hero.jpg" alt="대시보드" width={1200} height={630} priority sizes="100vw" />
<Image src="/avatar.png" alt="사용자" width={64} height={64} sizes="64px" />
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return <html lang="ko" className={inter.className}><body>{children}</body></html>;
}
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('@/components/chart'), { ssr: false, loading: () => <p>차트 로딩 중…</p> });
export const revalidate = 60;
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' });
module.exports = withBundleAnalyzer({});
체크리스트