웹사이트 로딩 속도는 사용자 경험과 SEO에 직접적 영향을 미칩니다. Google 연구에 따르면 페이지 로딩이 1초 지연되면 전환율이 7% 감소 합니다. Next.js는 성능 최적화를 위한 강력한 도구를 기본 제공하며, 올바르게 활용하면 경쟁사 대비 3배 빠른 웹사이트를 구축할 수 있습니다.
Next.js Rendering Strategies
SSR
Server-Side Rendering
Request Time
↻
Every request triggers
server rendering
Fresh data always
SSG
Static Site Generation
Build Time
⚡
HTML pre-generated
at build time
Fastest performance
ISR
Incremental Static Regen
Hybrid
↺
Static + revalidation
after interval
Best of both worlds
Next.js rendering strategies comparison
Next.js가 성능 최적화에 강한 이유
Next.js는 React의 강력한 생태계와 서버 사이드 최적화를 결합한 프레임워크입니다.
핵심 성능 기능 :
서버 컴포넌트 : 클라이언트로 전송되는 JavaScript 양 최소화자동 코드 스플리팅 : 페이지별 필요한 코드만 로드이미지 최적화 : WebP 변환, lazy loading 자동 처리폰트 최적화 : Layout Shift 제거, 자동 서브셋팅스트리밍 SSR : 점진적 페이지 렌더링정적 생성 (SSG) : 빌드 타임 사전 렌더링
📝 Core Web Vitals 목표치
LCP (Largest Contentful Paint) : 2.5초 이하FID (First Input Delay) : 100ms 이하CLS (Cumulative Layout Shift) : 0.1 이하Next.js로 제작된 POEMA 프로젝트는 평균 LCP 1.2초, FID 45ms, CLS 0.02 를 기록합니다.
1. 렌더링 전략 선택하기
Next.js 16 App Router는 4가지 렌더링 전략을 제공합니다. 페이지 특성에 맞는 전략을 선택해야 합니다.
Next.js Rendering Strategies
SSR
Server-Side Rendering
Request Time
↻
Every request triggers
server rendering
Fresh data always
SSG
Static Site Generation
Build Time
⚡
HTML pre-generated
at build time
Fastest performance
ISR
Incremental Static Regen
Hybrid
↺
Static + revalidation
after interval
Best of both worlds
Next.js rendering strategies comparison
전략 렌더링 시점 사용 사례 성능 특성
SSG (정적 생성) 빌드 타임 블로그, 마케팅 페이지 최고 속도, CDN 캐싱 ISR (증분 정적 재생성) 빌드 + 주기적 재생성 제품 목록, 뉴스 빠른 속도 + 신선한 데이터 SSR (서버 사이드 렌더링) 요청 시 개인화 대시보드 SEO + 동적 데이터 CSR (클라이언트 렌더링) 브라우저 인터랙티브 도구 SEO 불필요한 페이지
SSG: 최고 성능을 위한 정적 생성
블로그, 랜딩페이지, 포트폴리오처럼 모든 사용자에게 동일한 콘텐츠 를 보여주는 페이지는 SSG가 최적입니다.
tsx
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }: { params: { slug: string } }) {
const post = await getPostBySlug(params.slug);
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
성능 이점 :
빌드 타임에 HTML 사전 생성 CDN 엣지에 캐싱 → 전 세계 어디서나 50ms 이내 응답 JavaScript 번들 크기 최소화
ISR: 성능과 신선함의 균형
제품 목록, 뉴스 피드처럼 주기적 업데이트가 필요하지만 실시간일 필요는 없는 페이지에 적합합니다.
tsx
// app/products/page.tsx
export const revalidate = 3600; // 1시간마다 재생성
export default async function ProductsPage() {
const products = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 }
}).then(res => res.json());
return (
<div className="grid grid-cols-3 gap-4">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
작동 원리 :
첫 요청: 캐시된 정적 페이지 즉시 반환 재검증 시간(revalidate) 경과 후 첫 요청: 여전히 캐시 반환하지만 백그라운드에서 재생성 재생성 완료: 새로운 정적 페이지로 교체
💡 ISR vs SSG 선택 기준
데이터 업데이트 주기가 1시간 이상 : ISR 사용 데이터가 거의 변하지 않음 (월 1회 미만): SSG 사용 실시간 반영 필수 : SSR 사용
2. 이미지 최적화: next/image 마스터하기
이미지는 평균적으로 웹페이지 용량의 50% 이상 을 차지합니다. Next.js의 컴포넌트는 이미지 최적화를 자동화합니다.
tsx
import Image from 'next/image';
export default function HeroSection() {
return (
<div className="relative h-screen">
<Image
src="/hero-background.jpg"
alt="Hero background"
fill
priority // LCP 이미지는 priority 필수
sizes="100vw"
className="object-cover"
/>
<div className="relative z-10">
<h1>Welcome to POEMA</h1>
</div>
</div>
);
}
자동 최적화 기능 :
포맷 변환 : JPEG → WebP/AVIF (최대 50% 용량 감소)반응형 이미지 : sizes 속성 기반 자동 생성Lazy loading : 뷰포트 진입 시 로드 (Below-the-fold 이미지)Blur placeholder : 로딩 중 블러 이미지 표시
priority vs lazy loading
속성 사용 시기 로딩 방식
priorityLCP 이미지 (히어로, 로고) 즉시 로드, preload 링크 생성 기본 (lazy) Below-the-fold 이미지 뷰포트 진입 시 로드
⚠️ 흔한 실수
히어로 이미지에 priority 설정을 빼먹으면 LCP가 3~5초 지연됩니다. 첫 화면에 보이는 큰 이미지는 반드시 priority 추가 하세요.
외부 이미지 최적화
외부 URL 이미지도 최적화하려면 next.config.js에 도메인 허용 설정이 필요합니다.
js
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/images/**',
},
],
formats: ['image/avif', 'image/webp'], // AVIF 우선, WebP 대체
},
};
3. 폰트 최적화: Layout Shift 제거
웹 폰트는 CLS(Cumulative Layout Shift)의 주요 원인입니다. Next.js의 next/font는 폰트 로딩 중 레이아웃 이동을 완전히 제거 합니다.
tsx
// app/layout.tsx
import { Inter, Noto_Sans_KR } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
const notoSansKr = Noto_Sans_KR({
subsets: ['korean'],
weight: ['400', '700'],
display: 'swap',
variable: '--font-noto-sans-kr',
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko" className={`${inter.variable} ${notoSansKr.variable}`}>
<body>{children}</body>
</html>
);
}
css
/* globals.css */
body {
font-family: var(--font-noto-sans-kr), var(--font-inter), sans-serif;
}
자동 최적화 :
자동 서브셋팅 : 사용된 문자만 포함 (한글 11,172자 → 실사용 2,500자)Self-hosting : Google Fonts를 로컬에 호스팅 (DNS 조회 제거)preload : 폰트 파일 사전 로드로 FOIT(Flash of Invisible Text) 방지Size adjustment : 폰트 메트릭 자동 계산으로 CLS 0
4. 코드 스플리팅과 Dynamic Import
JavaScript 번들 크기는 FID(First Input Delay)에 직접 영향을 미칩니다. 필요한 코드만 필요한 시점에 로드 해야 합니다.
자동 코드 스플리팅
Next.js는 app/ 디렉토리의 각 페이지를 자동으로 분리합니다.
code
app/
├── page.tsx → home.js (50KB)
├── about/page.tsx → about.js (30KB)
└── products/page.tsx → products.js (80KB)
홈페이지 방문 시 home.js만 로드되며, products.js는 해당 페이지 방문 시 로드됩니다.
Dynamic Import로 수동 최적화
Below-the-fold 컴포넌트, 모달, 차트 같은 즉시 필요하지 않은 컴포넌트 는 동적 임포트로 분리하세요.
tsx
// ❌ 나쁜 예: 모든 컴포넌트 즉시 로드
import HeavyChart from '@/components/HeavyChart';
import Modal from '@/components/Modal';
// ✅ 좋은 예: 필요 시점에 로드
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <ChartSkeleton />,
ssr: false, // 차트는 클라이언트에서만 렌더링
});
const Modal = dynamic(() => import('@/components/Modal'));
성능 향상 :
초기 JavaScript 번들 40% 감소 FID 평균 60ms → 35ms 개선 TTI (Time to Interactive) 1.5초 단축
💡 동적 임포트 대상
모달, 드로어, 팝업 (사용자 액션 후 표시) 차트, 지도 (무거운 라이브러리) Below-the-fold 섹션 (스크롤 후 보이는 영역) A/B 테스트 변형 컴포넌트
5. 서버 컴포넌트로 클라이언트 JS 줄이기
Next.js 16 App Router의 서버 컴포넌트는 기본값 입니다. 상호작용이 필요 없는 컴포넌트는 서버에서 렌더링해 클라이언트로 전송되는 JavaScript를 줄이세요.
tsx
// ✅ 서버 컴포넌트 (기본값, 'use client' 없음)
async function ProductList() {
const products = await fetchProducts(); // 서버에서 데이터 페칭
return (
<div className="grid grid-cols-3 gap-4">
{products.map((product) => (
<ProductCard key={product.id} product={product} /> {/* 서버 컴포넌트 */}
))}
</div>
);
}
// ✅ 클라이언트 컴포넌트 (상호작용 필요)
'use client';
function AddToCartButton({ productId }: { productId: string }) {
const [loading, setLoading] = useState(false);
return (
<button onClick={() => addToCart(productId)}>
Add to Cart
</button>
);
}
서버 vs 클라이언트 컴포넌트 선택 기준 :
조건 컴포넌트 타입
데이터 페칭, DB 쿼리 서버 useState, useEffect 사용클라이언트 이벤트 리스너 (onClick, onChange) 클라이언트 브라우저 API (localStorage, window) 클라이언트 정적 콘텐츠 표시 서버
6. 성능 측정과 모니터링
최적화의 효과를 측정하지 않으면 개선 여부를 알 수 없습니다.
Vercel Speed Insights (권장)
Next.js 배포 시 Vercel Speed Insights를 활성화하면 실제 사용자 성능 데이터 를 수집합니다.
tsx
// app/layout.tsx
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{children}
<SpeedInsights />
</body>
</html>
);
}
Lighthouse CI
CI/CD 파이프라인에 Lighthouse를 통합해 성능 회귀 방지 :
json
// .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci && npm run build
- uses: treosh/lighthouse-ci-action@v9
with:
urls: 'http://localhost:3000'
budgetPath: './budget.json'
json
// budget.json (성능 예산)
[
{
"path": "/*",
"resourceSizes": [
{ "resourceType": "script", "budget": 300 }, // JavaScript 300KB 이하
{ "resourceType": "image", "budget": 500 } // 이미지 500KB 이하
],
"timings": [
{ "metric": "interactive", "budget": 3000 } // TTI 3초 이하
]
}
]
실전 성능 개선 사례
Before (일반 React SPA) :
LCP: 4.2초 FID: 180ms CLS: 0.28 JavaScript 번들: 850KB Lighthouse 점수: 62/100
개선 작업 :
Next.js App Router 마이그레이션 주요 페이지 SSG 전환 next/image로 전환 (WebP, lazy loading)next/font로 폰트 최적화무거운 라이브러리 동적 임포트 서버 컴포넌트 최대 활용
After (Next.js 16 최적화) :
LCP: 1.1초 (74% 개선) FID: 42ms (77% 개선) CLS: 0.03 (89% 개선) JavaScript 번들: 280KB (67% 감소) Lighthouse 점수: 98/100
Core Web Vitals Targets
LCP
Largest Contentful Paint
≤ 2.5s
Loading Speed
INP
Interaction to Next Paint
≤ 200ms
Responsiveness
CLS
Cumulative Layout Shift
≤ 0.1
Visual Stability
Google uses these metrics as ranking signals
결론: 성능은 선택이 아닌 필수
성능은 기능이 아닌 사용자 경험의 핵심 입니다. 느린 웹사이트는 아무리 좋은 콘텐츠와 디자인을 가져도 사용자를 잃습니다.
Next.js는 성능 최적화를 위한 모든 도구를 제공합니다. 올바르게 활용하면 세계 최고 수준의 웹사이트 를 만들 수 있습니다.
POEMA는 Next.js 성능 최적화 전문가로서 평균 Lighthouse 점수 95+ 달성 을 보장합니다.