- Published on
Next.js 14 이미지 LCP 개선 - next/image 실전
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
웹 성능 지표에서 LCP는 체감 속도와 직결됩니다. 특히 커머스·콘텐츠 사이트처럼 히어로 이미지가 큰 화면을 차지하는 경우, LCP 후보가 거의 항상 이미지로 잡힙니다. Next.js 14는 next/image가 기본적으로 많은 최적화를 제공하지만, 설정을 조금만 잘못해도 다음 같은 상황이 쉽게 발생합니다.
- 히어로 이미지는 늦게 내려오고, 대신 작은 아이콘이나 아래 영역 이미지가 먼저 로드됨
- 레이아웃 시프트를 피하려고
fill을 썼는데, 실제로는 잘못된sizes때문에 과도하게 큰 이미지를 다운로드함 - CDN 캐시가 안 먹어 매 요청마다 이미지 변환이 발생하거나, 원본이 너무 커서 변환 자체가 느림
이 글은 Next.js 14 App Router 기준으로, LCP를 악화시키는 이미지 문제를 어떻게 찾고(LCP 후보 확인), 어떤 옵션을 어떻게 적용해야 실제로 개선되는지(priority, fetchPriority, sizes, 포맷, 캐시) 실전 관점에서 정리합니다.
관련해서 전체 로딩 체감이 느릴 때는 이미지뿐 아니라 RSC 캐시·prefetch도 같이 보셔야 합니다. 필요하면 Next.js App Router 로딩 느림? RSC 캐시·prefetch 최적화도 함께 참고하세요.
1) 먼저 LCP 후보가 진짜 이미지인지 확인하기
최적화는 측정부터입니다. Chrome DevTools에서 LCP가 어떤 요소로 잡히는지 먼저 확인하세요.
- DevTools
Performance탭에서 기록 - 타임라인에서
Largest Contentful Paint이벤트 클릭 Related node를 보면 LCP 요소가 무엇인지 나옵니다
이미지 LCP에서 자주 보이는 패턴은 다음과 같습니다.
- LCP 이미지가
lazy로 로딩되어 실제로는 스크롤 전인데도 늦게 시작 - 이미지 다운로드보다
TTFB나 메인 스레드 작업이 더 큰 병목
메인 스레드 병목이 큰 경우(긴 태스크, TBT 증가)는 이미지 최적화만으로는 한계가 있습니다. 이때는 Chrome INP 급락 원인 찾기 - Long Task·TBT 분석처럼 Long Task를 같이 제거해야 LCP도 안정적으로 줄어듭니다.
2) next/image가 LCP에 영향을 주는 핵심 포인트
next/image는 다음을 자동으로 해줍니다.
- 적절한
srcset생성(여러 해상도) - WebP, AVIF 등 포맷 협상(환경에 따라)
- 레이아웃 안정성(너비·높이 기반)
- lazy loading 기본 적용(뷰포트 밖 이미지)
하지만 LCP 이미지는 lazy가 아니라 최우선으로 내려와야 하고, 실제 화면 크기와 일치하는 파일 크기를 내려줘야 합니다. 즉, LCP 개선의 핵심은 아래 3가지입니다.
- LCP 이미지를
priority로 승격 sizes를 정확히 줘서 과다운로드 방지- 원본 및 변환된 이미지가 CDN 캐시를 타도록 구성
3) 히어로 이미지는 무조건 priority + 올바른 sizes
3-1) 가장 흔한 실수: fill만 쓰고 sizes를 안 줌
fill은 반응형 레이아웃에서 자주 쓰지만, sizes가 없으면 브라우저가 어떤 크기로 렌더링될지 예측하기 어렵고, 결과적으로 큰 이미지를 받아 LCP가 악화될 수 있습니다.
아래는 히어로 이미지의 권장 패턴입니다.
// app/(marketing)/page.tsx
import Image from "next/image";
export default function Page() {
return (
<section style={{ position: "relative", height: 420 }}>
<Image
src="/images/hero.jpg"
alt="제품 히어로"
fill
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
style={{ objectFit: "cover" }}
/>
</section>
);
}
포인트는 다음입니다.
priority: LCP 후보인 히어로 이미지는 lazy가 아니라 즉시 로드sizes: 실제 레이아웃에서 이미지가 차지하는 폭을 브레이크포인트 기준으로 명시fill사용 시 부모에position: relative와 높이(또는 aspect ratio)가 필요
3-2) width와 height를 아는 경우가 가장 안전
가능하면 fill보다 width와 height를 주는 방식이 예측 가능하고 디버깅도 쉽습니다.
import Image from "next/image";
export function Hero() {
return (
<Image
src="/images/hero.jpg"
alt="히어로"
width={1200}
height={600}
priority
sizes="(max-width: 768px) 100vw, 1200px"
style={{ width: "100%", height: "auto" }}
/>
);
}
이 패턴은 CLS 방지에도 매우 유리합니다.
4) priority는 한 장만: 남발하면 오히려 느려진다
priority는 사실상 preload 힌트를 추가하는 것과 유사한 효과를 냅니다. 문제는 여러 이미지에 priority를 주면 네트워크 경쟁이 발생해 정작 LCP 이미지가 늦어질 수 있다는 점입니다.
권장 기준:
- 페이지당
priority는 1장(많아도 2장) 이내 - LCP 후보(대개 첫 화면의 가장 큰 이미지)에만 적용
- 나머지는 기본 lazy(또는 스크롤 근접 시 로드)로 두기
5) Next.js 14에서 fetchPriority로 브라우저 힌트 강화하기
브라우저 레벨에서 이미지 우선순위를 더 명시하고 싶다면 fetchPriority를 고려할 수 있습니다. Next.js 버전과 설정에 따라 지원 방식이 다를 수 있지만, 원리는 같습니다.
- LCP 이미지:
high - 일반 이미지:
auto - 아래 영역 이미지:
low
예시:
import Image from "next/image";
export function HeroImage() {
return (
<Image
src="/images/hero.jpg"
alt="히어로"
width={1200}
height={600}
priority
fetchPriority="high"
sizes="(max-width: 768px) 100vw, 1200px"
/>
);
}
주의: priority와 fetchPriority를 같이 쓰는 경우가 많지만, 실제 효과는 네트워크/브라우저에 따라 달라질 수 있으니 Lighthouse와 실제 디바이스에서 재측정하세요.
6) LCP를 망치는 숨은 원인: 잘못된 sizes로 인한 과다운로드
반응형 페이지에서 LCP가 느린데 네트워크 워터폴을 보면 이미지가 너무 큰 사이즈로 내려오는 경우가 많습니다.
예를 들어 모바일에서 히어로가 실제로는 360px 폭인데, sizes를 100vw만 줘서 고해상도 기기에서 800px 이상짜리를 받는 식입니다. sizes는 “CSS 픽셀 기준으로 실제 렌더링 폭이 얼마나 되는지”를 알려주는 값입니다.
실전 팁:
- 레이아웃 컨테이너가 최대 1200px이면
1200px같은 상한을 마지막에 둡니다. - 데스크톱에서 2컬럼이고 이미지 영역이 50퍼센트라면
50vw가 들어가야 합니다.
<Image
src="/images/card.jpg"
alt="카드"
width={800}
height={600}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 600px"
/>
7) 외부 이미지(원격)일 때: remotePatterns와 캐시 전략
외부 이미지(예: CDN, S3, 이미지 서버)를 next/image로 최적화하려면 next.config.js에 허용이 필요합니다.
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "cdn.example.com",
pathname: "/images/**",
},
],
},
};
module.exports = nextConfig;
그리고 LCP 관점에서 더 중요한 건 캐시입니다.
- 원격 원본이 느리면 최적화된 이미지 생성도 늦어짐
- 최적화 결과가 캐시되지 않으면 트래픽이 늘수록 변환 비용이 반복됨
실전 권장:
- 원본 이미지는 CDN에서 강한 캐시(
Cache-Control)로 서빙 - 가능하면 빌드 타임에 최적화된 정적 이미지를
/public에 두거나, 이미지 CDN(CloudFront, Cloudflare Images 등)로 변환을 오프로드 - 동적 최적화가 필요하면 Next 이미지 최적화 캐시가 안정적으로 유지되도록 런타임/스토리지 구성을 확인
8) 포맷과 품질: AVIF는 만능이 아니다
next/image는 브라우저에 따라 AVIF/WebP 등을 선택할 수 있습니다. 다만 AVIF는 인코딩 비용이 높을 수 있고, 서버에서 온더플라이 변환이 일어나면 첫 요청이 느려져 LCP가 흔들릴 수 있습니다.
전략은 두 갈래입니다.
- 트래픽이 큰 히어로: 사전 변환된 정적 에셋을 준비(AVIF/WebP)하거나 이미지 CDN 사용
- 트래픽이 적거나 내부 도구: 온더플라이 변환을 허용하되 캐시를 강하게
품질 조절 예:
<Image
src="/images/hero.jpg"
alt="히어로"
width={1200}
height={600}
priority
quality={80}
sizes="(max-width: 768px) 100vw, 1200px"
/>
quality를 무조건 낮추기보다는, 실제 다운로드 바이트와 시각적 품질을 같이 확인하세요.
9) placeholder는 LCP를 줄이지 않는다, 하지만 체감은 좋아진다
placeholder="blur"는 저해상도 블러 이미지를 먼저 보여줘서 “빈 화면” 시간을 줄입니다. 다만 LCP 자체는 보통 최종 이미지가 로드된 시점으로 잡히므로, LCP를 직접 줄이기보다는 체감 품질을 개선하는 용도에 가깝습니다.
import Image from "next/image";
import hero from "@/public/images/hero.jpg";
export function Hero() {
return (
<Image
src={hero}
alt="히어로"
priority
placeholder="blur"
sizes="(max-width: 768px) 100vw, 1200px"
/>
);
}
정적 import를 쓰면 blur 데이터가 자동으로 포함되어 운영이 편합니다.
10) 워터폴에서 확인해야 할 체크리스트
이미지 LCP 튜닝을 한 뒤에는 반드시 네트워크 워터폴로 “시작 시점”과 “다운로드 크기”를 확인하세요.
- LCP 이미지 요청이 초기 HTML 직후 바로 시작하는가
- LCP 이미지가 다른 리소스(CSS, JS) 때문에 늦게 시작하지는 않는가
- 실제로 내려받는 이미지 바이트가 기대보다 크지 않은가
- 모바일/데스크톱 각각에서
sizes가 올바르게 동작하는가
특히 App Router에서 초기 로딩이 느리면 이미지가 빨라도 전체가 느릴 수 있습니다. 이런 경우는 앞서 언급한 RSC 캐시·prefetch 최적화 글(Next.js App Router 로딩 느림? RSC 캐시·prefetch 최적화)과 함께 보시는 게 효과적입니다.
11) 실전 레시피: LCP 히어로 이미지 최적화 템플릿
아래 템플릿을 그대로 적용하고, sizes만 레이아웃에 맞게 조정하면 대부분의 LCP 이미지 문제가 정리됩니다.
import Image from "next/image";
type Props = {
src: string;
alt: string;
};
export function LcpHeroImage({ src, alt }: Props) {
return (
<div style={{ position: "relative", width: "100%", height: 420 }}>
<Image
src={src}
alt={alt}
fill
priority
fetchPriority="high"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 90vw, 1200px"
quality={80}
style={{ objectFit: "cover" }}
/>
</div>
);
}
운영에서 자주 쓰는 추가 팁:
- 히어로는 가능하면 한 장만, 위에 겹치는 배경/오버레이는 CSS로 처리
- 텍스트는 이미지에 박지 말고 실제 텍스트로 렌더링(접근성·번역·성능 모두 유리)
- 이미지 원본은 업로드 단계에서 리사이즈해서 “필요 이상으로 큰 원본”을 없애기
마무리
Next.js 14에서 이미지 LCP 개선은 결국 우선순위와 정확한 사이징의 싸움입니다. next/image를 쓴다고 자동으로 LCP가 좋아지지는 않습니다. LCP 후보가 되는 히어로 이미지를 특정하고, 그 한 장에만 priority를 주고, sizes를 레이아웃에 맞게 정확히 선언하며, 캐시와 포맷 전략까지 맞추면 LCP는 눈에 띄게 안정화됩니다.
마지막으로, 이미지가 빨라졌는데도 LCP가 계속 흔들린다면 메인 스레드 Long Task나 초기 라우트 데이터 패칭 병목이 남아 있을 가능성이 큽니다. 이 경우는 이미지 최적화와 함께 성능 병목을 분리해서 접근하는 것이 가장 빠른 해결책입니다.