next/image: 이미지 최적화
일반 <img> 태그 대신 next/image를 사용하면 자동으로:
- WebP/AVIF 변환
- 화면 크기에 맞는 리사이징
- lazy loading
- layout shift 방지 (CLS 개선)
기본 사용법
import Image from "next/image";
// 정적 이미지: import하면 크기 자동 감지
import profilePic from "@/public/profile.jpg";
function Profile() {
return (
<Image
src={profilePic}
alt="프로필 사진"
// width, height 불필요 (자동 감지)
/>
);
}
// 원격 이미지: 크기 명시 필수
function ProductImage({ url }: { url: string }) {
return (
<Image
src={url}
alt="상품 이미지"
width={400}
height={300}
/>
);
}
fill: 컨테이너 채우기
function HeroImage() {
return (
// 부모에 position: relative 필요
<div className="relative h-96 w-full">
<Image
src="/hero.jpg"
alt="히어로"
fill
className="object-cover" // CSS로 크기 제어
priority // LCP 이미지에 사용
/>
</div>
);
}
원격 이미지 허용 설정
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.example.com",
pathname: "/products/**",
},
{
protocol: "https",
hostname: "*.amazonaws.com",
},
],
},
};
module.exports = nextConfig;
next/font: 폰트 최적화
폰트를 빌드 시 내려받아 self-hosting합니다. 외부 도메인 요청 없음 → 성능 향상.
// app/layout.tsx
import { Noto_Sans_KR, Inter } from "next/font/google";
const notoSansKR = Noto_Sans_KR({
subsets: ["latin"],
weight: ["400", "700"],
display: "swap", // FOUT 방지
});
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter", // CSS 변수로 사용
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko" className={inter.variable}>
<body className={notoSansKR.className}>
{children}
</body>
</html>
);
}
로컬 폰트
import localFont from "next/font/local";
const myFont = localFont({
src: [
{
path: "../public/fonts/MyFont-Regular.woff2",
weight: "400",
style: "normal",
},
{
path: "../public/fonts/MyFont-Bold.woff2",
weight: "700",
style: "normal",
},
],
variable: "--font-my",
});
Metadata API로 SEO 최적화
// 정적 메타데이터
export const metadata = {
title: {
template: "%s | 내 사이트", // 페이지 제목 | 사이트명
default: "내 사이트",
},
description: "사이트 설명",
openGraph: {
type: "website",
locale: "ko_KR",
url: "https://example.com",
siteName: "내 사이트",
},
twitter: {
card: "summary_large_image",
},
};
// 동적 메타데이터 (generateMetadata 함수 사용)
Script 최적화
import Script from "next/script";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{children}
{/* 페이지 로드 후 비동기 로드 */}
<Script
src="https://analytics.example.com/script.js"
strategy="lazyOnload"
/>
</body>
</html>
);
}
| strategy | 설명 |
|---|---|
beforeInteractive | 가능한 빨리 |
afterInteractive | hydration 후 |
lazyOnload | 모든 리소스 로드 후 |
정리
| 최적화 | 방법 | 효과 |
|---|---|---|
| 이미지 | next/image | WebP 변환, lazy load |
| 폰트 | next/font/google | self-hosting, no FOUT |
| 스크립트 | next/script | 로드 전략 제어 |
| SEO | metadata API | OG 태그, 타이틀 관리 |
다음 편에서는 인증 구현 — NextAuth.js로 소셜 로그인과 세션 관리를 구현하는 방법을 배웁니다.