CDN이 뭔지 모르는 프론트엔드 개발자를 위한 CDN 입문
CDN이 뭔지 모르겠다
프론트엔드 하다 보면 CDN이라는 말이 자주 나온다.
- "이미지를 CDN에 올려서 서빙하세요"
- "라이브러리를 CDN으로 불러오면 빠릅니다"
- "CloudFront로 CDN 설정했어요"
근데 정확히 어떻게 동작하는지는 몰라도 대충 쓸 수는 있다. 이 글은 그 "대충 아는" 상태를 벗어나기 위해 정리한 것이다.
CDN이란
CDN(Content Delivery Network) — 전 세계 여러 곳에 서버를 두고, 사용자에게 가장 가까운 서버에서 파일을 제공하는 네트워크다.
비유하자면 이렇다.
서울에 본사(Origin 서버)가 있는 편의점이 있다고 하자. 부산 사람이 이 편의점에서 물건을 사려면 서울까지 다녀와야 한다. 근데 부산에 물류창고(Edge 서버)를 두면? 부산 사람은 가까운 창고에서 빠르게 받을 수 있다.
CDN에서 쓰는 용어 정리:
| 용어 | 의미 |
|---|---|
| Origin 서버 | 원본 파일이 있는 서버. 내가 직접 운영하는 서버 |
| Edge 서버 | CDN이 전 세계에 두는 중계 서버. 사용자에게 가깝다 |
| 캐시(Cache) | Edge 서버에 미리 복사해둔 파일. 요청이 오면 Origin 대신 여기서 응답한다 |
| 레이턴시(Latency) | 요청을 보내고 응답이 올 때까지의 지연 시간. 거리가 멀수록 늘어난다 |
왜 필요한가
웹 성능에서 레이턴시가 중요한 이유는 파일 크기만큼이나 거리가 속도에 영향을 주기 때문이다.
서울 서버 → 서울 사용자: 약 5ms
서울 서버 → 미국 사용자: 약 150ms
서울 서버 → 유럽 사용자: 약 250ms이미지 한 장이 100KB라도, 250ms 기다린 다음에야 다운로드가 시작된다. CDN을 쓰면 유럽 사용자도 현지 Edge 서버에서 받아서 레이턴시가 줄어든다.
국내 서비스만 한다면 CDN이 덜 중요하다. 근데 이미지, 동영상, JS 번들 같은 정적 파일(Static Assets) — 서버에서 연산 없이 그대로 내보내는 파일들 — 은 CDN으로 서빙하는 게 거의 표준이다.
CDN 동작 방식
첫 요청은 Origin까지 갔다 오지만, 그 이후 같은 파일 요청은 Edge 캐시에서 바로 응답한다. 그래서 많이 쓰이는 파일일수록 CDN 효과가 크다.
이미지 CDN 최적화
이미지는 웹 페이지에서 용량이 가장 큰 리소스다. CDN을 이미지에 적용하면 거리 단축 외에도 추가 이점이 생긴다.
이미지 CDN이 해주는 것들
1. 포맷 변환
원본은 PNG나 JPG로 올려도, CDN이 브라우저 지원 여부를 보고 자동으로 WebP(JPG 대비 약 30% 작은 차세대 이미지 포맷)나 AVIF로 변환해서 보내준다.
원본: photo.jpg (500KB)
Chrome 사용자 → photo.webp (350KB) 자동 변환
Safari 구버전 → photo.jpg (500KB) 그대로2. 리사이징(Resizing)
URL 파라미터로 원하는 크기를 요청하면 CDN이 그 크기로 잘라서 내보낸다.
원본: /images/banner.jpg (4000x2000px, 2MB)
모바일 요청: /images/banner.jpg?w=400 → 400px 리사이즈, 120KB3. 품질(Quality) 조절
/images/banner.jpg?q=80 → 품질 80%, 용량 대폭 감소, 육안 차이 거의 없음Next.js에서 이미지 CDN 쓰기
Next.js는 next/image 컴포넌트가 이미지 최적화를 내장하고 있다. 기본적으로 Next.js 서버 자체가 이미지 CDN 역할을 한다.
import Image from "next/image"
export function Banner() {
return (
<Image
src="/images/banner.jpg"
alt="배너 이미지"
width={1200}
height={600}
priority // 뷰포트 최상단 이미지에는 priority를 붙여서 빠르게 로드
/>
)
}next/image가 자동으로 해주는 것:
- WebP/AVIF 변환
- 뷰포트 크기에 맞는 리사이징
- lazy loading(화면에 보일 때만 로드)
blurplaceholder(로딩 중 흐린 이미지 표시)
외부 CDN URL 이미지 (Cloudinary, S3+CloudFront 등)
S3에 올린 이미지나 Cloudinary 같은 외부 이미지 CDN을 쓸 때는 next.config.ts에 허용 도메인을 등록해야 한다.
// next.config.ts
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "res.cloudinary.com", // Cloudinary CDN
},
{
protocol: "https",
hostname: "*.amazonaws.com", // S3 / CloudFront
},
],
},
}등록 후에는 외부 URL도 next/image로 최적화할 수 있다.
<Image
src="https://res.cloudinary.com/my-app/image/upload/v1/photo.jpg"
alt="프로필 사진"
width={200}
height={200}
/>Cloudinary는 URL 파라미터로 변환 옵션을 지정할 수도 있다.
원본: https://res.cloudinary.com/my-app/image/upload/photo.jpg
변환: https://res.cloudinary.com/my-app/image/upload/w_400,f_webp,q_80/photo.jpg
↑ 400px ↑ webp ↑ 품질 80sizes로 반응형 최적화
모바일/데스크탑에서 이미지 크기가 다를 때 sizes를 지정하면 브라우저가 적절한 크기를 요청한다.
<Image
src="/images/card.jpg"
alt="카드 이미지"
fill // 부모 컨테이너를 꽉 채움
sizes="(max-width: 768px) 100vw, 50vw"
// 모바일(768px 이하): 화면 너비 100%
// 그 외: 화면 너비 50%
/>sizes 없이 fill만 쓰면 항상 100vw 크기로 요청해서 불필요하게 큰 이미지를 받는다.
JS 라이브러리 CDN
이미지 외에도 JS 라이브러리를 CDN으로 로드하는 경우가 있다. 앞선 Mermaid 도입기에서도 이 방식을 썼다.
<!-- npm 패키지 대신 CDN에서 직접 로드 -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>이 방식이 유리한 경우:
- 패키지가 너무 무거워서 메인 번들에 포함시키기 부담스러울 때
- 특정 페이지에서만 드물게 사용할 때 (Mermaid 다이어그램처럼)
- 브라우저 캐시 공유 효과를 기대할 때 — 다른 사이트에서 같은 CDN URL을 이미 받은 사람은 캐시에서 바로 쓴다
반대로 이 방식이 불리한 경우:
- 매 페이지에서 쓰는 핵심 라이브러리라면 npm 패키지로 번들에 포함시키는 게 낫다 (네트워크 요청 절약)
- CDN 서버 장애 시 기능이 통째로 멈출 수 있다
정리
| 상황 | 선택 |
|---|---|
| 사진, 배너 등 무거운 이미지 | 이미지 CDN (CloudFront, Cloudinary 등) |
| Next.js 프로젝트 이미지 | next/image (자동으로 CDN 역할) |
| 모든 페이지에서 쓰는 JS 라이브러리 | npm 패키지로 번들에 포함 |
| 특정 페이지에서만 쓰는 무거운 라이브러리 | CDN 동적 로드 |
| 동영상, PDF 등 대용량 파일 | CDN 필수 |
CDN은 "서버를 여러 곳에 두는 것" 이상의 의미가 없다. 핵심은 사용자에게 가깝게, 캐시를 활용해서, 빠르게 파일을 보내는 것이다.