1. 왜 트래픽이 몰리면 갑자기 느려질까?
트래픽이 몰린다고 해서 단순히 “사용자가 많아서” 느려지는 건 아니야.
대부분은 이런 것들이 한꺼번에 터지기 때문이지.
- 오리진 서버(origin)로 가는 요청이 너무 많다
- 정적 파일(JS/CSS/이미지)이 전부 같은 서버에서 나간다
- 같은 데이터를 매번 새로 fetch 한다
- 안 봐도 되는 컴포넌트/이미지를 다 한 번에 로드한다
- “다음에 쓸 리소스”를 전혀 미리 가져오지 않는다
즉, 핵심은 두 가지다.
- 오리진(백엔드/메인 서버)에 가는 트래픽을 줄이는 것
- 사용자 입장에서 “필요한 순간에만 필요한 것만” 로딩하는 것
이 두 가지를 중심으로 CDN, Lazy Loading, Prefetching 전략을 묶어볼 거야.
2. CDN 전략 — 정적 리소스는 최대한 “가까운 데”서 쏜다
2-1. CDN의 역할
CDN(Content Delivery Network)은 전 세계에 흩어진 서버(POP)들이
정적 리소스(JS, CSS, 이미지, 폰트) 를 대신 제공해 주는 네트워크야.
효과는 딱 두 가지:
- 사용자는 지리적으로 가까운 서버에서 파일을 받는다 → 네트워크 지연 감소
- 오리진 서버는 정적 파일 제공 부담이 줄어든다 → 백엔드 여유 확보
대규모 트래픽에서는 “정적 리소스를 CDN으로 무조건 빼는 것”이 전제 조건이야.
2-2. 어떤 걸 CDN에 올려야 할까?
- JS 번들 파일 (.js)
- CSS (.css)
- 이미지 (.jpg, .png, .webp, .avif)
- 폰트 (.woff2, .ttf)
- 동영상 썸네일, 아이콘, 정적 JSON 등
그리고 가능하면 정적 도메인 분리도 해 주는 게 좋아.
- www.example.com → HTML/데이터
- static.example.com 또는 CDN 도메인 → JS/CSS/이미지
2-3. 캐시 정책 설계
CDN을 쓴다고 끝이 아니라, “어떻게 캐시할지” 가 진짜 중요하다.
- 파일 이름에 해시 붙이기: app.3f92a.js, style.8cd1.css
- 헤더에 강한 캐시 설정
- Cache-Control: public, max-age=31536000, immutable
이렇게 하면:
- 파일 내용이 안 바뀌면 CDN과 브라우저 캐시가 계속 사용
- 새로 빌드하면 파일명이 바뀌니 자동으로 최신 버전으로 교체
즉, “캐시는 세게, 파일명으로 버전 관리”가 기본 패턴이다.
3. Lazy Loading — 안 보는 건 나중에 불러라
대규모 트래픽 + 대규모 UI에서 중요한 철학은 이거야.
“지금 안 쓰는 건 지금 로드하지 않는다.”
3-1. 이미지 Lazy Loading
가장 쉬운 부분부터.
<img src="thumb.jpg" loading="lazy" alt="썸네일" />
- loading="lazy" 속성 하나로,
- 뷰포트 근처에 왔을 때만 이미지가 로드됨
- “긴 리스트 + 썸네일 많은 페이지”면 효과가 매우 크다
IntersectionObserver로 커스텀 구현도 가능해.
3-2. 컴포넌트/페이지 Lazy Loading (코드 스플리팅)
SPA/MPA 상관 없이, 한 번에 모든 코드를 불러오면 첫 로딩이 괴물이 된다.
- 라우트 단위 코드 스플리팅
- 컴포넌트 단위 Lazy Loading
예를 들어 (React 기준 느낌으로 설명하자면):
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function Page() {
return (
<Suspense fallback={<div>로딩 중...</div>}>
<HeavyComponent />
</Suspense>
);
}
- 유저가 해당 컴포넌트를 보기 전까지는 JS를 로드하지 않음
- JS 번들 크기 감소 → 초기 로딩 및 파싱 시간 감소
3-3. “스크롤 기반” Lazy UI
긴 목록(피드, 게시글 리스트, 상품 목록)에서
- 처음부터 100개 렌더링 ❌
- 위에서 10~20개만 렌더링, 아래는 Infinite Scroll + Lazy Load
이 구조가 되면
- DOM 노드 수가 줄어들어 렌더링 비용 감소
- 데이터 API 호출도 페이지네이션 단위로 분산
대규모 트래픽에선, 이런 “조각 나누기”가 오리진 트래픽과 렌더링 부담을 동시에 줄인다.
4. Prefetch / Preload — “미리 받아두기”의 힘
Lazy가 “나중에”라면, Prefetch/Preload는 “미리” 다.
대규모 트래픽에서도 체감 속도를 올리려면 이걸 잘 써야 한다.
4-1. Preload
지금 페이지에서 곧 사용할 리소스를 빠르게 받아오고 싶을 때.
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/static/hero.jpg" as="image">
<link rel="preload" href="/static/main.js" as="script">
- LCP에 영향을 주는 Hero 이미지, 주요 폰트, 상단 영역 JS/CSS를 preload
- “화면이 뜨긴 떴는데 폰트/이미지가 늦게 나오는” 문제를 줄여준다
4-2. Prefetch
지금은 안 쓰지만, 곧 쓰게 될 리소스.
예: 메인에서 로그인 페이지, 상세 페이지로 많이 이동한다면 👇
<link rel="prefetch" href="/page/detail" as="document">
또는 라우팅 라이브러리들이 제공하는
- “링크에 마우스 올리면 해당 페이지 리소스 prefetch”
- “뷰포트 근처에 링크가 오면, 미리 다음 페이지 번들 로드”
이런 기능을 켜두면, 사용자 입장에선 페이지 전환이 거의 즉시 일어나는 느낌을 받게 된다.
4-3. Preconnect / DNS Prefetch
외부 도메인(예: 이미지 CDN, API 서버, 폰트 서버)에 대해
- DNS 조회, TLS 핸드셰이크를 미리 해두는 것
- 트래픽이 많을수록 “연결 지연”이 체감 속도에 크게 영향을 준다
5. 캐싱 레이어 설계 — 오리진을 최대한 “심심하게”
대규모 트래픽에서 중요한 아이디어는 하나야.
“오리진 서버는 심심할수록 좋다.”
그걸 위해 캐싱 레이어를 여러 단계로 쌓는다.
5-1. 브라우저 캐시
- Cache-Control, ETag, Last-Modified
- 정적 파일은 강하게, HTML/API는 전략적으로
5-2. CDN 캐시
- 전 세계 POP에서 동일 파일 제공
- 인기 있는 페이지일수록 “오리진 요청 거의 없음” 상태까지 만들 수도 있다
5-3. Service Worker / PWA 캐시
- 자주 쓰는 API 응답, 정적 JSON, 번역 리소스 등을 SW 캐시로 재사용
- 오프라인/저속 네트워크 환경에서도 UX 유지
5-4. 프론트엔드 in-memory 캐시 (React Query / SWR 등)
- 동일한 API 요청을 한 페이지에서 여러 번 보내지 않도록
- stale-while-revalidate 패턴으로 바로 캐시 → 백그라운드에서 최신 데이터 받아오기
이렇게 하면:
- 프론트단에서 같은 요청이 1번만 서버로 가고, 나머지는 캐시로 해결
- 동시 접속 수가 많을수록 오리진 서버 보호에 엄청난 효과
6. 이미지 & 리소스 최적화 — 트래픽 절반은 이미지다
대규모 트래픽 시 트래픽량 = 돈이다. (CDN 비용, 네트워크 비용)
그래서 이미지/정적 리소스를 줄이는 건 성능 + 비용 모두에 중요해.
6-1. 이미지 포맷
- 사진: WebP, AVIF 우선
- 아이콘/로고: SVG
6-2. Responsive Image (srcset, sizes)
<img
src="image-800.jpg"
srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1024px) 800px, 1200px"
alt="예시 이미지"
/>
- 모바일에서 굳이 데스크톱 해상도 이미지를 받을 필요가 없지
- 작은 화면에는 작은 이미지 → 네트워크 절약 → LCP 개선
6-3. 아이콘 스프라이트 / SVG Sprite
- 수백 개 아이콘을 개별 요청 ❌
- SVG Sprite 한 번 로드 → <use>로 재사용
7. 렌더링 전략 — SSR / SSG / Edge / ISR
대규모 트래픽 환경에서는 “렌더링 방식 선택”도 중요하다.
- SSR (Server-Side Rendering)
- 매 요청마다 HTML 생성 → 동적이 많으면 좋지만, 트래픽 많으면 서버 부담 큼
- SSG (Static Site Generation)
- 빌드 시 HTML 생성 → CDN 캐시 + 정적 제공 → 트래픽 많을수록 유리
- ISR (Incremental Static Regeneration)
- 자주 안 바뀌는 페이지는 정적으로, 일정 주기마다 백그라운드 갱신
- Edge Rendering
- 사용자 근처 엣지 서버에서 SSR 수행 → TTFB 개선
패턴은 보통 이렇다 👇
- 상품 상세, 게시글 페이지: SSG + ISR
- 마이페이지, 관리자: SSR or CSR
- 홈/랭킹/카테고리: SSG + CDN 캐시 극대화
8. 예시 시나리오 — 동접 10만 메인 페이지 설계
가정:
- 메인 페이지 동시 접속 10만
- 상품 카드 20개, 이미지, 썸네일, 배너 포함
어떻게 설계할까?
- HTML은 SSG 또는 ISR로 생성 → CDN 캐시
- JS/CSS/이미지는 전부 CDN 도메인으로 제공
- Hero 이미지, 주요 폰트 preload
- 상품 카드 이미지는 loading="lazy" + srcset 사용
- 스크롤 아래 컨텐츠(추천, 더보기 영역)는 Lazy Load 컴포넌트로 분리
- API 데이터는 React Query(or SWR)로 캐싱, stale-while-revalidate 적용
- “상세 페이지” 링크는 prefetch 해서 클릭 시 거의 즉시 전환
- Core Web Vitals(LCP/FID/CLS)를 Web Vitals + Sentry로 모니터링
이렇게 해 두면:
- 트래픽이 늘어도 오리진이 터지지 않고
- 사용자 입장에서는 “첫 화면은 빠르고, 나머지는 자연스럽게 따라오는” 느낌을 받게 된다.
9. 체크리스트 — 대규모 트래픽 대비 빠른 점검표
CDN
- JS/CSS/이미지/폰트 전부 CDN에서 제공되는가
- 정적 파일 이름에 해시가 붙어 있는가
- Cache-Control: max-age + immutable 사용 중인가
Lazy Loading
- 이미지에 loading="lazy" 또는 IntersectionObserver 적용했는가
- 라우트/컴포넌트 코드 스플리팅이 되어 있는가
- 긴 리스트를 한 번에 다 렌더링하고 있지는 않은가
Prefetch / Preload
- Hero 이미지, 폰트, 핵심 JS/CSS에 preload를 써봤는가
- 다음 페이지/주요 라우트에 prefetch를 적용했는가
- 외부 도메인에 preconnect/dns-prefetch를 활용했는가
캐싱 레이어
- 브라우저 캐시 + CDN 캐시 + 프론트단 in-memory 캐시 구조가 있는가
- 자주 불리는 API는 SWR/React Query처럼 캐시를 쓰고 있는가
렌더링 전략
- 정말 SSR이 필요한 페이지만 SSR 하고 있는가
- 자주 보는 정적 페이지는 SSG/ISR로 돌리고 있는가
10. 마무리
대규모 트래픽 환경에서의 프론트엔드 최적화는
“한 가지 기술로 해결되는 문제”가 아니라,
- 어디에서 데이터를 가져오고(CDN, 캐시),
- 언제 로딩하며(Lazy, Prefetch),
- 무엇을 먼저 보여줄지(LCP 관점),
- 백엔드와 어떻게 부담을 나눌지(렌더링 전략)
전체 흐름을 설계하는 작업에 가깝다.
트래픽이 많아질수록,
“한 번만 요청하고, 여러 번 재사용하는 구조”가 정답에 가까워진다.
이 감각만 가져가면, 어떤 스택(React, Next.js, Vue, Svelte…)을 쓰든
대규모 트래픽에도 흔들리지 않는 프론트엔드를 설계할 수 있다.
'frontend > javascript' 카테고리의 다른 글
| 🟨 2-35. React 기반 UX 성능 튜닝 실전 — Suspense, Streaming, Transition 완전 이해 (0) | 2025.11.07 |
|---|---|
| 🟨 2-34. 사용자가 체감하는 “빠른 웹” 만들기 — UX 성능 최적화와 인지 부하 설계 (0) | 2025.11.07 |
| 🟨 2-32. 실제 서비스 적용 사례 — Next.js + Vercel + Sentry + Slack으로 자동화 구축하기 (0) | 2025.11.07 |
| 🟨 2-31. 프론트엔드 자동화의 완성 — CI/CD + 성능 모니터링 + 에러 리포팅 통합 시스템 구축 (0) | 2025.11.07 |
| 🟨 2-30. 웹 성능 모니터링과 자동 분석 — Lighthouse CI, Web Vitals, Sentry 통합 가이드 (0) | 2025.11.07 |