본문으로 바로가기
기술

웹 성능 최적화 핵심 전략 — 로딩 속도를 극적으로 개선하는 방법

A
AlwaysCorp 기술팀· 기술·개발 콘텐츠 전문
||10분 읽기
#웹성능#성능최적화#Core Web Vitals#LCP#CLS#번들최적화#이미지최적화#캐싱#CDN#웹개발#프론트엔드#로딩속도

성능이 비즈니스에 미치는 영향

숫자로 보는 성능의 가치

웹 성능은 기술적 지표를 넘어 비즈니스 성과에 직접적 영향을 미칩니다:

  • Google: 검색 결과 로딩이 0.5초 늘어나면 트래픽이 20% 감소
  • Amazon: 페이지 로딩이 100ms 늘어날 때마다 매출이 1% 감소
  • Pinterest: 랜딩 페이지 로딩 시간을 40% 단축 → 가입 전환율 15% 증가
  • BBC: 로딩 시간이 1초 추가될 때마다 사용자 10% 이탈

2018년 Google의 모바일 사이트 분석에 따르면, 로딩 시간이 1초에서 3초로 늘어나면 이탈률(bounce rate)이 32% 증가하고, 5초까지 늘어나면 90% 증가합니다.

---

렌더링 파이프라인 이해하기

브라우저의 렌더링 과정

브라우저가 HTML을 화면에 표시하기까지의 과정을 이해하면, 최적화 지점을 정확히 파악할 수 있습니다:

  1. HTML 파싱 → DOM 트리 생성
  2. CSS 파싱 → CSSOM 트리 생성
  3. DOM + CSSOM → 렌더 트리 생성
  4. 레이아웃(Layout/Reflow): 각 요소의 위치와 크기 계산
  5. 페인트(Paint): 픽셀 단위로 화면에 그리기
  6. 합성(Composite): GPU가 레이어를 합성하여 최종 화면 출력
브라우저 렌더링 파이프라인 — DOM에서 Composite까지의 과정
브라우저 렌더링 파이프라인 — DOM에서 Composite까지의 과정

렌더링 차단 리소스

CSS는 렌더링 차단 리소스입니다. 브라우저는 CSSOM이 완성될 때까지 렌더 트리를 생성하지 않으므로, CSS 로딩이 느리면 전체 렌더링이 지연됩니다.

```html <!-- ❌ 렌더링 차단: 모든 CSS를 다운로드할 때까지 렌더링 중단 --> <link rel="stylesheet" href="https://cdn.example.com/heavy-framework.css">

<!-- ✅ 조건부 로딩: 인쇄용 CSS는 화면 렌더링을 차단하지 않음 --> <link rel="stylesheet" href="print.css" media="print">

<!-- ✅ 비동기 CSS 로딩 --> <link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'"> ```

JavaScript는 파서 차단 리소스입니다. `<script>` 태그를 만나면 HTML 파싱이 중단됩니다.

```html <!-- ❌ 파서 차단: HTML 파싱이 멈추고 스크립트 실행 후 재개 --> <script src="heavy-script.js"></script>

<!-- ✅ defer: HTML 파싱 완료 후 실행, 순서 보장 --> <script defer src="app.js"></script>

<!-- ✅ async: 다운로드 완료 즉시 실행, 순서 미보장 --> <script async src="analytics.js"></script> ```

---

JavaScript 번들 최적화

코드 분할(Code Splitting)

전체 JavaScript를 하나의 번들로 전송하면, 현재 페이지에 불필요한 코드까지 다운로드됩니다.

```typescript // ❌ 정적 임포트: 페이지 로드 시 즉시 로드 import HeavyChart from './components/HeavyChart';

// ✅ 동적 임포트: 필요한 시점에 로드 const HeavyChart = dynamic(() => import('./components/HeavyChart'), { loading: () => <Skeleton />, ssr: false, // 서버에서 렌더링 불필요한 경우 }); ```

트리 셰이킹(Tree Shaking)

사용하지 않는 코드를 번들에서 제거하는 기법입니다. ES 모듈의 정적 구조를 분석하여 실제로 import된 함수만 포함합니다.

```typescript // ❌ 전체 라이브러리 임포트 → 트리 셰이킹 불가 import _ from 'lodash'; // ~72KB

// ✅ 필요한 함수만 임포트 → 트리 셰이킹 가능 import debounce from 'lodash/debounce'; // ~1KB ```

번들 분석

```bash # Next.js 번들 분석 npm install @next/bundle-analyzer # next.config.js에 설정 후 ANALYZE=true npm run build ```

번들 분석기를 통해 의외로 큰 의존성(moment.js의 로케일 파일, lodash 전체 등)을 발견하고 최적화할 수 있습니다.

---

이미지 최적화

이미지가 웹 성능에 미치는 비중

HTTP Archive에 따르면 웹페이지 전체 용량의 평균 약 50%가 이미지입니다. 이미지 최적화는 성능 개선의 가장 효율적인 출발점입니다.

차세대 이미지 형식

| 형식 | JPEG 대비 압축률 | 투명도 | 애니메이션 | 브라우저 지원 | |------|------------------|--------|------------|---------------| | WebP | 25~35% 절감 | ✅ | ✅ | 97%+ | | AVIF | 40~50% 절감 | ✅ | ✅ | 92%+ |

```html <!-- 차세대 형식 + 폴백 --> <picture> <source srcset="image.avif" type="image/avif"> <source srcset="image.webp" type="image/webp"> <img src="image.jpg" alt="설명" width="800" height="600"> </picture> ```

반응형 이미지

```html <img srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w" sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 800px" src="image-800.webp" alt="설명" > ```

지연 로딩(Lazy Loading)

뷰포트에 보이지 않는 이미지는 스크롤 시 로드합니다:

```html <!-- 네이티브 lazy loading (대부분의 모던 브라우저 지원) --> <img src="image.webp" alt="설명" loading="lazy" width="800" height="600">

<!-- ATF(Above the Fold) 이미지는 lazy loading 제외 --> <img src="hero.webp" alt="히어로" loading="eager" fetchpriority="high"> ```

---

캐싱 전략

브라우저 캐싱 (Cache-Control)

``` # 정적 자산 (JS, CSS, 이미지) — 해시가 포함된 파일명 Cache-Control: public, max-age=31536000, immutable

# HTML — 항상 서버에 확인 Cache-Control: no-cache

# API 응답 — 짧은 캐시 + 재검증 Cache-Control: public, max-age=60, stale-while-revalidate=300 ```

immutable: 콘텐츠가 절대 변하지 않음을 선언 (해시 파일명과 함께 사용) stale-while-revalidate: 캐시 만료 후에도 stale 데이터를 먼저 반환하고 백그라운드에서 갱신

CDN (Content Delivery Network)

CDN은 전 세계 에지 서버에 콘텐츠를 캐싱하여 사용자와 물리적으로 가까운 서버에서 응답합니다.

CDN 적용 효과:

  • TTFB 단축: 서울 서버 → 미국 사용자 = ~200ms RTT, CDN = ~20ms
  • 대역폭 절약: Origin 서버 부하 분산
  • DDoS 방어: 에지에서 악성 트래픽 흡수

Next.js를 Vercel에 배포하면 자동으로 글로벌 CDN에 배포됩니다.

---

폰트 최적화

웹 폰트의 성능 영향

웹 폰트는 FOIT(Flash of Invisible Text) 또는 FOUT(Flash of Unstyled Text) 문제를 유발합니다.

```css @font-face { font-family: 'Pretendard'; src: url('/fonts/Pretendard-Regular.woff2') format('woff2'); font-weight: 400; font-display: swap; / FOUT 허용 — CLS 유발 가능하나 텍스트 즉시 표시 / unicode-range: U+AC00-D7A3; / 한글만 로드 — 용량 대폭 절감 / } ```

Next.js의 폰트 최적화

```typescript import { Noto_Sans_KR } from 'next/font/google';

const notoSansKR = Noto_Sans_KR({ subsets: ['latin'], weight: ['400', '700'], display: 'swap', preload: true, }); ```

`next/font`는 빌드 시 폰트 파일을 다운로드하여 셀프 호스팅하므로, Google Fonts CDN에 대한 외부 네트워크 요청이 제거됩니다.

---

성능 측정 도구와 지표

실험실 도구 vs 현장 데이터

실험실(Lab) 도구: 일관된 환경에서 측정

  • Lighthouse (Chrome DevTools)
  • WebPageTest
  • PageSpeed Insights (Lab 섹션)

현장(Field) 데이터: 실제 사용자 경험 측정

  • Chrome User Experience Report (CrUX)
  • PageSpeed Insights (Field 섹션)
  • Web Vitals JavaScript 라이브러리

Google 검색 순위에 반영되는 것은 현장 데이터(CrUX)입니다. Lighthouse 점수가 100이라도 실제 사용자의 Core Web Vitals가 좋지 않으면 SEO에 불리합니다.

Core Web Vitals 임계값 차트 — Good/Needs Improvement/Poor 기준
Core Web Vitals 임계값 차트 — Good/Needs Improvement/Poor 기준

성능 예산(Performance Budget) 설정

```json { "budgets": [ { "resourceType": "script", "budget": 300 }, { "resourceType": "image", "budget": 500 }, { "metric": "lcp", "budget": 2500 } ] } ```

성능 예산을 설정하고 CI/CD 파이프라인에서 검사하면, 성능 저하를 배포 전에 감지할 수 있습니다.

---

실전 최적화 체크리스트

즉시 적용 가능 (Quick Wins)

  • [ ] 이미지를 WebP/AVIF로 변환
  • [ ] ATF 이미지를 제외한 나머지에 lazy loading 적용
  • [ ] CSS/JS에 적절한 Cache-Control 헤더 설정
  • [ ] 사용하지 않는 CSS/JS 제거

중기 최적화

  • [ ] 코드 분할로 초기 번들 크기 축소
  • [ ] next/image + next/font 활용
  • [ ] 서드파티 스크립트(광고, 분석) 지연 로딩
  • [ ] CDN 도입

장기 아키텍처

  • [ ] SSR/SSG 렌더링 전략 최적화
  • [ ] 성능 예산 설정 + CI 연동
  • [ ] Real User Monitoring(RUM) 도입
  • [ ] 성능 대시보드 구축 + 주간 리뷰
성능 최적화는 일회성 프로젝트가 아니라 지속적 프로세스입니다. 측정 → 분석 → 개선 → 모니터링의 사이클을 반복하며, 사용자 경험과 비즈니스 성과를 동시에 향상시킬 수 있습니다.
A

AlwaysCorp 기술팀

기술·개발 콘텐츠 전문

얼웨이즈 블로그에서 유용한 정보와 인사이트를 공유합니다.