gzip·brotli 압축 적용: 전송량 70% 줄이기
텍스트 리소스 압축이 주는 효과와 nginx·Apache 설정법, 적용 확인과 흔한 실수.
HTTP 압축은 서버 설정 몇 줄로 텍스트 전송량을 70~80% 줄이는, 웹 성능 튜닝에서 가장 확실한 한 수입니다. 238KB짜리 HTML이 gzip을 거치면 30KB 안팎으로 줄어들고, 그만큼 첫 화면이 빨리 뜨고 모바일 데이터도 절약됩니다. CDN을 쓰면 대부분 자동으로 켜지지만, 직접 운영하는 nginx·Apache 서버에서는 설정이 빠져 있거나 절반만 적용된 경우가 의외로 많습니다.
이 가이드는 압축이 적용되는 대상과 브라우저–서버 간 협상 원리부터, 바로 붙여 넣을 수 있는 nginx·Apache 설정 블록, brotli 사전압축, 그리고 이미지 이중 압축·Vary 누락 같은 흔한 실수까지 정리합니다. 내 사이트가 지금 압축을 쓰고 있는지는 압축 적용 확인 도구로 URL만 넣으면 바로 알 수 있습니다.
무엇을 압축해야 하나 — 텍스트만, 이미지는 절대 금지
gzip·brotli 같은 범용 압축은 반복 패턴이 많은 텍스트에서 효과가 큽니다. HTML 태그, CSS 속성명, JS 식별자, JSON 키는 같은 문자열이 수없이 반복되므로 70~80%씩 줄어듭니다. 반면 JPG·PNG·WebP· MP4·WOFF2는 이미 포맷 자체가 압축이라 다시 압축해도 1~2% 줄거나 오히려 커지고, CPU만 낭비합니다.
| 리소스 | 압축? | 이유·기대 절감 |
|---|---|---|
| HTML, CSS, JS | 예 | 반복 패턴 多 — 70~80% 절감 |
| JSON, XML, SVG | 예 | API 응답·아이콘 — 60~85% 절감 |
.txt, RSS, 소스맵 | 예 | 텍스트 계열은 전부 대상 |
| JPG, PNG, WebP, AVIF | 아니오 | 이미 압축된 포맷 — 절감 거의 0, CPU 낭비 |
| MP4, WOFF2, ZIP, PDF(대부분) | 아니오 | 내부 압축 포함 — 이중 압축 무의미 |
내 페이지에서 텍스트 리소스가 전체 용량의 몇 %를 차지하는지는 페이지 무게 측정 도구로 리소스 유형별 분포를 보면 압축의 기대 효과를 미리 가늠할 수 있습니다.
협상 원리 — Accept-Encoding과 Content-Encoding
압축은 브라우저와 서버가 자동으로 협상합니다. 순서는 단순합니다.
- 브라우저가 요청에
Accept-Encoding: gzip, deflate, br, zstd헤더를 실어 “나는 이런 형식을 풀 수 있다”고 알립니다. 요즘 브라우저는 전부 gzip과 brotli(br)를 지원합니다. - 서버는 그중 자기가 지원하는 형식으로 본문을 압축하고, 응답에
Content-Encoding: gzip(또는br)을 붙입니다. - 브라우저가 헤더를 보고 자동으로 압축을 풀어 렌더링합니다. 개발자는 클라이언트 쪽에서 할 일이 없습니다.
여기서 중요한 헤더가 하나 더 있습니다. 같은 URL이 클라이언트에 따라 압축본·비압축본으로 다르게 나가므로, 응답에 Vary: Accept-Encoding을 붙여 중간 캐시(CDN·프록시)가 두 버전을 구분해 저장하게 해야 합니다. 이게 빠지면 gzip을 못 푸는 클라이언트에 압축본이 캐시로 전달되는 사고가 납니다.
nginx 설정 — 실전 예: 238KB HTML → 30KB
nginx는 gzip 모듈이 기본 내장입니다. http 블록에 아래를 넣으면 끝입니다.
# /etc/nginx/nginx.conf — http { } 안에
gzip on;
gzip_comp_level 5; # 1~9. 5가 속도/압축률 균형점
gzip_min_length 256; # 256바이트 미만은 압축 안 함
gzip_vary on; # Vary: Accept-Encoding 자동 추가
gzip_proxied any; # 프록시 뒤에서도 압축
gzip_types
text/css text/plain text/xml
application/javascript application/json
application/xml application/rss+xml
image/svg+xml font/ttf; # text/html 은 기본 포함이라 쓰면 경고실제로 이 설정을 적용한 서버에서 238,412바이트짜리 HTML 문서를 요청하면 Content-Encoding: gzip과 함께 약 30KB(87% 절감)로 내려옵니다. 곧바로 체감되는 차이는 다운로드 시간입니다. 4G(가정 5Mbps) 기준 전송 시간이 약 380ms에서 48ms로 줄어듭니다. 적용 전후 응답 속도 변화는 응답 속도 측정 도구로 비교해 보세요.
brotli는 같은 텍스트에서 gzip보다 15~20% 더 줄여 줍니다. nginx 기본 배포판에는 없어서 ngx_brotli 모듈을 추가로 빌드/설치해야 하며, 설정은 거의 대칭입니다.
# ngx_brotli 설치 후 brotli on; brotli_comp_level 5; # 동적 압축은 4~6 권장 (11은 정적 전용) brotli_types text/css application/javascript application/json image/svg+xml;
모듈 빌드가 부담스럽다면 Cloudflare 같은 CDN을 앞단에 두는 것만으로 brotli가 자동 적용됩니다. 이 경우 원본 서버는 gzip만 켜 둬도 충분합니다.
Apache와 정적 사전압축(.br/.gz)
Apache는 mod_deflate로 처리합니다.
# .htaccess 또는 VirtualHost <IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/css text/plain AddOutputFilterByType DEFLATE application/javascript application/json AddOutputFilterByType DEFLATE application/xml image/svg+xml </IfModule>
한 단계 더 나가면 빌드 시점 사전압축이 있습니다. 정적 자산(JS 번들, CSS)은 배포할 때 미리 app.js.br·app.js.gz를 만들어 두고, 서버는 요청이 오면 압축 없이 파일만 내려 줍니다. 두 가지 장점이 있습니다.
- CPU 0: 요청마다 압축하지 않으므로 트래픽이 몰려도 서버 부하가 없습니다.
- 최고 압축률: 실시간이 아니므로 brotli 레벨 11, gzip 레벨 9처럼 가장 높은 레벨을 쓸 수 있습니다. brotli 11은 동적 압축 대비 10% 이상 더 줄어듭니다.
nginx는 gzip_static on;(내장)과 brotli_static on;(ngx_brotli)으로 사전압축 파일을 자동으로 찾아 서빙합니다. Vite·webpack·Next.js 모두 빌드 단계에서 .br·.gz를 생성하는 플러그인이 있습니다.
적용 확인과 흔한 실수 5가지
설정 후 확인은 압축 적용 확인 도구에 URL을 넣거나, curl로 직접 헤더를 보내 보면 됩니다.
curl -sI -H "Accept-Encoding: gzip, br" https://example.com/ | grep -i \ -e content-encoding -e vary # content-encoding: br ← 압축 적용 중 # vary: Accept-Encoding ← 캐시 안전
주의할 함정들입니다.
- 이미지·영상까지
gzip_types에 넣는 것: 절감은 없고 요청마다 CPU만 태웁니다. 텍스트 MIME 타입만 나열하세요. - 압축 레벨 과다: gzip 9는 5보다 CPU를 몇 배 쓰면서 압축률은 1~2%p밖에 안 좋아집니다. 동적 압축은 gzip 5, brotli 4~6이 정석이고, 최고 레벨은 정적 사전압축에서만 쓰세요.
Vary: Accept-Encoding누락: 중간 캐시가 압축본과 비압축본을 섞어 내보내는 원인. nginx는gzip_vary on;한 줄로 해결됩니다.- 너무 작은 응답 압축: 수백 바이트짜리는 gzip 헤더 오버헤드 때문에 오히려 커질 수 있습니다.
gzip_min_length 256처럼 하한을 두세요. - BREACH: 응답 본문에 CSRF 토큰 같은 비밀값과 공격자가 조작 가능한 입력이 함께 들어가는 동적 페이지는, 압축 크기 변화를 관찰해 비밀을 추출하는 BREACH 공격의 조건이 됩니다. 이런 응답은 압축을 끄거나, 토큰을 요청마다 마스킹하는 프레임워크 기능을 쓰세요. 정적 자산·공개 페이지는 해당 없습니다.
마지막으로 효과 측정입니다. 적용 전후를 페이지 무게 측정으로 전송량 변화를, 응답 속도 측정으로 체감 속도 변화를 기록해 두면 성능 개선을 숫자로 증명할 수 있습니다.
자주 묻는 질문
gzip과 brotli 중 무엇을 써야 하나요?
이미지(JPG, PNG, WebP)도 gzip으로 압축해야 하나요?
압축이 적용됐는지 어떻게 확인하나요?
gzip_comp_level은 몇으로 설정해야 하나요?
압축이 보안에 문제가 된다는 말이 있던데 사실인가요?
이 가이드와 함께 쓰면 좋은 도구
관련 가이드
- Cache-Control 완전 정리: 브라우저 캐시 제대로 쓰기max-age·no-cache·no-store·immutable의 진짜 의미와 리소스 유형별 권장 캐시 정책.
- 사이트가 느릴 때: TTFB 원인 진단과 개선 순서첫 바이트까지의 시간(TTFB)이 늘어지는 원인 — DNS·TLS·서버·DB — 을 구간별로 진단하고 줄이는 법.
- Core Web Vitals(LCP·CLS·INP) 개선 체크리스트LCP·CLS·INP가 무엇이고 무엇을 측정하는지, 점수를 올리는 실전 체크리스트.