Next.js를 AWS로 배포할 때 비용이 증가할 수 있는 포인트
Next.js AWS 배포 시 비용이 폭증하는 4가지 원인과 최적화 전략
Next.js는 강력한 프레임워크지만, 아키텍처에 대한 고민 없이 AWS(EC2, ECS, 람다 등)에 기본 설정으로 배포할 경우 '요금 폭탄'을 맞을 위험이 있습니다. 특히 네트워크 아웃바운드 비용(DTO)과 서버 컴퓨팅 요금을 유발하는 주요 원인과 해결책을 정리했습니다.
1. 기본 <Image> 컴포넌트의 서버 사이드 최적화 (가장 흔한 실수)
Next.js의 내장 이미지 최적화 기능(next/image)은 훌륭하지만, 서버 환경에서 직접 구동할 경우 비용 증가의 주범이 됩니다.
🚨 비용 증가 원인:
- 컴퓨팅 부하: 요청이 올 때마다 서버(Node.js/Docker)가
sharp라이브러리를 통해 이미지를 리사이징하고 포맷을 변환하므로 CPU와 메모리 사용량이 치솟습니다. (Scale-up 비용 발생) - 아웃바운드(DTO) 요금: EC2나 컨테이너에서 변환된 이미지가 퍼블릭 인터넷을 타고 직접 클라이언트로 전송되면서 비싼 트래픽 비용이 발생합니다.
✅ 최적화 전략 (Custom Loader + Serverless CDN 아키텍처):
- 원본 스토리지와 CDN 분리: 무거운 원본 이미지는 S3에 안전하게 보관하고, 그 앞에 CloudFront(CDN)를 배치합니다. S3에서 CloudFront로 데이터가 이동할 때는 AWS 내부망을 타기 때문에 데이터 전송 비용(DTO)이 전면 무료이며, 사용자에게 전달될 때만 상대적으로 저렴한 CloudFront 아웃바운드 요금이 적용됩니다.
- 서버리스(Serverless) 온더플라이(On-the-fly) 이미지 최적화: Next.js 서버(EC2 등)가 감당하던 무거운 리사이징 작업을 Lambda@Edge나 일반 Lambda(또는 S3 Object Lambda)로 완전히 오프로딩합니다. 사용자가 특정 크기의 이미지를 요청하면, Lambda가 개입하여 최초 1회만 이미지를 원하는 해상도 및 차세대 웹 포맷(WebP, AVIF)으로 변환합니다. 변환된 이미지는 CloudFront 엣지 로케이션에 강력하게 캐싱되므로, 이후의 수많은 동일 요청에는 컴퓨팅 연산이 전혀 발생하지 않아 요금이 0에 수렴하게 됩니다.
- Next.js Custom Loader 적용: 프론트엔드 개발자는 기존 코드의
<Image />컴포넌트 사용 방식을 그대로 유지하면서,next.config.js에 커스텀 로더(Custom Loader) 함수만 추가로 정의하면 됩니다. 이 로더는 렌더링 시점에 이미지의src,width,quality값을 가로채어https://my-cdn.com/image.jpg?w=800&f=webp와 같이 CDN을 직접 가리키는 URL로 치환해 줍니다. 결과적으로 대규모 이미지 트래픽이 Next.js 앱 서버를 전혀 거치지 않게 되어 렌더링 서버의 성능 저하를 막고 비용을 완벽하게 차단할 수 있습니다.
💻 커스텀 로더(Custom Loader) 구현 코드
이 구조를 프론트엔드에 연결하기 위해 Next.js의 기본 이미지 변환 기능을 끄고, 모든 이미지 요청을 CloudFront 인프라로 리다이렉트하는 커스텀 로더를 적용합니다.
① 로더 함수 작성 (utils/customLoader.js)
// utils/customLoader.js
export default function myCustomImageLoader({ src, width, quality }) {
// 환경변수 또는 실제 운영 환경의 CloudFront(CDN) 도메인을 입력합니다.
const cdnDomain = process.env.NEXT_PUBLIC_CDN_URL || '[https://my-cdn.cloudfront.net](https://my-cdn.cloudfront.net)';
// quality 값이 명시되지 않으면 기본값 75를 사용합니다.
const q = quality || 75;
// AWS 인프라(Lambda@Edge 등)가 파싱할 수 있는 쿼리스트링 규격으로 URL을 생성합니다.
// Next.js 컴포넌트가 넘겨주는 디바이스별 width에 맞춰 차세대 포맷(webp)을 강제합니다.
return `${cdnDomain}${src}?w=${width}&q=${q}&f=webp`;
}
② Next.js 환경 설정에 로더 등록 (next.config.js)
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
// Next.js 자체 서버 최적화를 끄고 커스텀 로더 사용을 선언합니다.
loader: 'custom',
// 위에서 생성한 파일의 경로를 지정합니다.
loaderFile: './utils/customLoader.js',
},
};
module.exports = nextConfig;
③ 컴포넌트에서 사용 (app/page.tsx)
import Image from 'next/image';
export default function HomePage() {
return (
<main>
<h1>초고속 CDN 이미지 최적화 적용</h1>
{/* 개발자는 평소와 완전히 동일하게 코드를 작성합니다.
렌더링 시점에 src가 CDN 주소로 자동 치환되어 Next.js 서버를 타지 않습니다. */}
<Image
src="/images/main-banner.jpg" // S3 버킷 내의 상대 경로
alt="메인 서비스 배너"
width={1200}
height={600}
priority={true} // LCP 최적화를 위한 사전 로드 설정
/>
</main>
);
}
커스텀 로더 환경에서도 <Image> 컴포넌트를 써야 하는 4가지 이유
서버 측 이미지 최적화를 CDN에 넘기더라도, Next.js <Image> 컴포넌트가 제공하는 '브라우저 렌더링 최적화' 기능 때문에 기본 <img> 태그보다 훨씬 뛰어난 사용자 경험(UX)과 프론트엔드 성능을 보장합니다.
1. 반응형 srcset 자동 생성 (데이터 절약의 핵심)
- 동작 방식: 사용자의 디바이스 화면 크기(Viewport)를 감지하여 브라우저 크기에 맞춘
<img srcset="...">속성을 자동으로 생성합니다. - 효과: PC에서는 큰 해상도(예:
w=1200)를, 모바일 화면에서는 작은 해상도(예:w=384)를 CDN에 자동으로 요청하므로 미디어 쿼리 분기 처리 없이 데이터를 획기적으로 절약할 수 있습니다.
2. CLS(Cumulative Layout Shift) 완벽 방지
- 동작 방식:
width와height값을 바탕으로 이미지가 다운로드되기 전부터 이미지가 들어갈 공간의 비율(Aspect Ratio)을 브라우저에 미리 확보해 둡니다. - 효과: 이미지가 늦게 로딩되면서 텍스트나 레이아웃이 밑으로 '툭' 밀려나는 화면 덜컹거림(Layout Shift) 현상을 원천 차단합니다.
3. LCP(최대 콘텐츠 풀 페인트) 자동 최적화
- 동작 방식:
priority={true}속성을 부여하면 HTML의<head>태그에<link rel="preload" href="CDN_주소">를 자동으로 삽입합니다. - 효과: 브라우저가 자바스크립트를 해석하기도 전에 메인 배너 같은 핵심 이미지를 최우선으로 다운로드하게 만들어 구글 SEO 점수에 결정적인 LCP 지표를 개선합니다.
4. 레이지 로딩(Lazy Loading) 및 블러(Blur) 효과 기본 탑재
- 레이지 로딩: 기본적으로 화면 스크롤이 이미지 근처에 도달했을 때만 네트워크 요청을 보내 초기 렌더링 속도를 높입니다.
💡 결론
커스텀 로더(Custom Loader)를 적용한 <Image> 컴포넌트 사용은 역할을 완벽하게 분담하는 똑똑한 아키텍처입니다. 비용이 발생하는 무거운 백엔드 역할(이미지 리사이징, 포맷 변환)은 CDN(AWS)에 위임하고, 프론트엔드 최적화(CLS 방지, 자동 srcset 등)는 Next.js가 담당하게 만들어 성능과 비용 두 마리 토끼를 모두 잡을 수 있습니다.
2. 불필요한 동적 SSR(Server-Side Rendering) 남용
모든 페이지를 서버 컴포넌트 기반의 동적 렌더링으로 처리하면 서버 유지비와 트래픽 비용이 동시에 증가합니다.
🚨 비용 증가 원인:
- 페이지 이동 시마다 서버가 HTML과 무거운 RSC Payload(컴포넌트 트리 데이터)를 새로 생성해서 내려보내야 하므로, 트래픽 용량(DTO)이 커집니다.
- 사용자 트래픽에 비례해 Next.js 구동을 위한 컴퓨팅 리소스(EC2, ECS)가 상시 필요합니다.
✅ 최적화 전략 (Static Export 및 클라이언트 위임):
- SEO가 필요 없거나 정적인 페이지는
output: 'export'설정을 통해 빌드 타임에 정적 HTML/JS로 뽑아냅니다. - 이 정적 파일들을 S3 + CloudFront로 서비스하면 컴퓨팅 비용은 0원이 되고, 트래픽 비용은 저렴한 CDN 단가로 처리됩니다.
3. 서버 패칭에 대한 맹신과 클라이언트 패칭(axios)의 오해
'무조건 서버에서 데이터를 가져오는 것이 효율적이다'라는 오해로 인해 아키텍처가 비효율적으로 변할 수 있습니다.
🚨 비용 증가 원인:
- 동적 데이터 처리를 모두 Next.js 서버에 맡기면, 트래픽 집중 시 Next.js 서버 자체가 병목이 되어 서버를 증설해야 합니다.
- 서버에서 조립된 무거운 UI 레이아웃이 매번 네트워크를 탑니다.
✅ 최적화 전략 (클라이언트 컴포넌트의 전략적 활용):
- 실시간으로 변하거나 개인화된 데이터(SEO 불필요)는 클라이언트 컴포넌트에서
axios나fetch(React Query 등 활용)로 직접 백엔드 API를 호출하도록 위임합니다. - 브라우저(클라이언트)가 렌더링과 데이터 패칭을 분담하므로 Next.js 서버 부하가 줄어들고, 주고받는 데이터도 순수 JSON으로 최소화되어 DTO 비용이 절감됩니다.
4. 1:1 매핑만 하는 무의미한 BFF (Route Handlers)
Next.js의 API Routes(app/api/*)를 프론트엔드를 위한 중간 서버(BFF)로 사용할 때, 아무런 역할 없이 통과만 시키는 경우입니다.
🚨 비용 증가 원인:
- 백엔드의 무거운 JSON 응답이 가공 없이 Next.js 서버를 거쳐 그대로 클라이언트로 나가므로 DTO 비용 절감 효과가 전혀 없습니다.
- 불필요한 네트워크 홉(Hop)이 추가되어 사용자 응답 속도만 느려지고 서버 자원만 낭비됩니다.
✅ 최적화 전략:
- BFF 레이어는 반드시 페이로드 다이어트(필요한 필드만 남기고 삭제) 또는 **요청 병합(여러 API 결과를 하나로 합침)**을 수행하여 아웃바운드 트래픽 크기를 줄이는 데 기여해야 합니다.
- API Key 은닉이나 CORS 회피 등 보안 목적이 아니라면, 1:1 중계 API는 과감히 삭제하고 클라이언트에서 백엔드를 직접 호출합니다.
💡 총괄 요약: 비용 최적화의 핵심 철학
"비싼 컴퓨팅 서버(EC2)가 일하게 하지 말고 브라우저와 CDN이 일하게 하라. 그리고 서버 밖으로 나가는 데이터(JSON, 이미지)는 뼈대만 남기고 다이어트하라."