API용 ZSTD, Brotli, GZIP 비교: 속도, 압축률, CPU 비용과 프로덕션에서 JSON 및 바이너리 페이로드에 대한 실무적 기본값.

API 응답 압축은 서버가 응답 바디(대개 JSON)를 네트워크로 보내기 전에 더 작은 바이트 스트림으로 인코딩하는 것을 말합니다. 클라이언트(브라우저, 모바일 앱, SDK 또는 다른 서비스)는 이를 해제한 뒤 처리합니다. HTTP에서는 Accept-Encoding(클라이언트가 지원하는 것)과 Content-Encoding(서버가 선택한 것) 같은 헤더로 협상합니다.
압축은 주로 세 가지를 제공합니다:
대가는 명확합니다: 압축은 대역폭을 절약하지만 CPU(압축/해제)와 때로는 메모리(버퍼)에 비용을 부과합니다. 그게 가치 있는지는 병목이 무엇인지에 달려 있습니다.
압축은 다음과 같은 응답에서 빛을 발합니다:
대형 JSON 목록(카탈로그, 검색 결과, 분석 등)을 반환한다면 압축은 가장 쉬운 이득 중 하나입니다.
압축은 다음과 같은 경우 CPU 낭비가 될 수 있습니다:
ZSTD vs Brotli vs GZIP 중 선택할 때 실무적 결정은 보통 다음으로 귀결됩니다:
이 글의 나머지는 특정 API와 트래픽 패턴에 대해 이 세 가지를 균형 있게 맞추는 방법입니다.
세 코덱 모두 페이로드 크기를 줄이지만, 속도·압축률·호환성 등 서로 다른 제약을 최적화합니다.
ZSTD의 속도: 엔드포인트가 테일 레이턴시에 민감하거나 서버가 CPU 바운드인 경우에 좋습니다. 중간~대형 JSON 응답에서는 네트워크 시간에 비해 압축 오버헤드가 보통 무시될 만큼 빠릅니다.
Brotli의 압축률: 대역폭이 주요 제약(모바일 클라이언트, 비싼 egress, CDN 중심)이고 응답이 주로 텍스트일 때 유리합니다. 작은 응답에서도 CPU를 더 들여서 작게 만드는 것이 가치가 있을 수 있습니다.
GZIP의 호환성: 최대한 많은 클라이언트를 지원해야 하고 협상 실패 위험을 최소화해야 할 때 좋습니다(오래된 SDK, 임베디드 클라이언트, 레거시 프록시). 최상의 성능은 아니더라도 안전한 기준입니다.
압축 “레벨”은 CPU 시간과 출력 파일 크기 사이의 절충을 설정하는 프리셋입니다:
세 포맷 모두에서 디코딩은 보통 압축보다 훨씬 저렴하지만, 매우 높은 레벨은 클라이언트 CPU/배터리에도 부담이 될 수 있습니다(특히 모바일).
압축은 종종 “작아진 응답 = 더 빠른 API”로 팔립니다. 느리거나 비용이 높은 네트워크에서는 자주 맞지만 자동은 아닙니다. 압축이 서버 CPU 시간을 충분히 추가하면, 전송 바이트가 적어도 요청이 더 느려질 수 있습니다.
두 가지 비용을 구분하면 도움이 됩니다:
높은 압축률은 전송 시간을 줄이지만, 압축으로 인해 응답당 15–30ms가 늘어난다면 특히 빠른 연결에서는 오히려 느려질 수 있습니다.
부하가 걸리면 압축은 평균보다 p95/p99 레이턴시를 더 악화시킬 수 있습니다. CPU 사용량이 피크를 치면 요청이 큐에 쌓이고, 큐잉은 작은 비용을 큰 지연으로 증폭합니다—평균 레이턴시는 괜찮아 보여도 느린 사용자들이 고통받습니다.
추측하지 마세요. A/B 테스트나 단계적 롤아웃으로 다음을 비교하세요:
실제 트래픽 패턴과 페이로드로 테스트하세요. “최고” 압축 레벨은 바이트가 가장 많이 줄어드는 것이 아니라 전체 시간을 줄이는 수준입니다.
압축은 무료가 아닙니다—작업을 네트워크에서 CPU와 메모리로 옮깁니다. API에서는 요청 처리 시간 증가, 메모리 풋프린트 상승, 때로는 클라이언트 측 지연으로 드러납니다.
대부분의 CPU는 응답 압축에 사용됩니다. 압축은 패턴을 찾고 상태/사전을 구성하며 인코딩된 출력을 작성합니다.
디코딩은 보통 더 저렴하지만 여전히 관련됩니다:
API가 이미 CPU 바운드라면(무거운 인증, 비싼 쿼리 등), 높은 압축 레벨을 켜면 페일리어 테일 레이턴시가 증가할 수 있습니다.
압축은 몇 가지 방식으로 메모리를 늘립니다:
컨테이너 환경에서는 높은 피크 메모리가 OOM 킬이나 더 낮은 인스턴스 밀도로 이어질 수 있습니다.
압축은 응답당 CPU 사이클을 늘려 인스턴스당 처리량을 줄입니다. 이는 오토스케일링을 더 자주 트리거해 비용을 올릴 수 있습니다. 흔한 패턴은: 대역폭 비용은 줄지만 CPU 사용은 늘어납니다—어떤 자원이 더 희소한지에 따라 옳은 선택이 달라집니다.
모바일이나 저전력 장치에서는 디코딩이 렌더링, JS 실행, 배터리 사용과 경쟁합니다. 몇 KB를 절약하지만 디코딩에 시간이 더 오래 걸리면 특히 “사용 가능한 데이터까지의 시간”에서 느리게 느껴질 수 있습니다.
Zstandard(ZSTD)는 느리지 않으면서 강한 압축률을 제공하도록 설계된 현대적인 포맷입니다. 많은 JSON 중심 API에서 좋은 "기본값"입니다: GZIP보다 눈에 띄게 작은 응답을 비슷하거나 더 낮은 지연으로 제공하고, 클라이언트 측 디코딩도 매우 빠릅니다.
ZSTD는 단순히 바이트가 작아지는 것이 아니라 엔드투엔드 시간을 신경쓸 때 특히 가치가 있습니다. 비교적 빠른 압축과 매우 빠른 디코딩을 제공해 요청 처리의 밀접한 경쟁이 있을 때 유리합니다.
소형~중형 JSON은 의미 있는 이득을 볼 수 있고, 대형 응답은 더 큰 이득을 얻습니다.
대부분의 API에는 낮은 레벨(보통 1–3)로 시작하세요. 이는 지연/크기 절충에서 가장 좋은 경우가 많습니다.
다음 경우에만 높은 레벨을 사용하세요:
실용적 접근법은 전역적으로 낮은 기본값을 두고, 일부 대형 응답 엔드포인트에 대해서만 레벨을 높이는 것입니다.
ZSTD는 스트리밍을 지원해 큰 응답에서 피크 메모리를 줄이고 더 빨리 전송을 시작할 수 있습니다.
사전 모드는 많은 유사한 객체(반복 키, 안정적 스키마)를 반환하는 API에서 큰 이득이 될 수 있습니다. 사전은 작은 빈번한 페이로드에 특히 효과적이며 버전 관리 가능한 사전을 안전하게 관리할 수 있을 때 유용합니다.
서버 측 지원은 많은 스택에서 간단하지만 클라이언트 호환성이 결정 요인이 될 수 있습니다. 일부 HTTP 클라이언트, 프록시, 게이트웨이는 기본적으로 Content-Encoding: zstd를 광고하거나 수용하지 않을 수 있습니다.
서드파티 소비자가 있다면 폴백(보통 GZIP)을 유지하고 Accept-Encoding에 명확히 포함된 경우에만 ZSTD를 활성화하세요.
Brotli는 텍스트를 매우 잘 압축하도록 설계되었습니다. JSON, HTML 같은 “단어 많은” 페이로드에서 GZIP보다 압축률이 좋을 때가 많습니다—특히 높은 레벨에서.
텍스트 중심 응답이 Brotli의 강점입니다. 대형 JSON 문서(카탈로그, 검색 결과, 구성 블롭 등)는 Brotli로 상당히 줄일 수 있어 느린 네트워크에서 이득이 크고 발신 비용을 줄이는 데 도움이 됩니다.
또한 한 번 압축해서 여러 번 제공하는 경우(캐시 가능한 응답, 버전된 리소스)에는 높은 레벨의 Brotli가 CPU 비용을 요청 수로 나누어 충분히 가치가 됩니다.
동적 생성 응답(요청마다 생성)에서는 Brotli의 최상의 비율이 높은 레벨을 요구하는데 이는 CPU 비용이 크고 레이턴시를 늘립니다. 압축 시간을 고려하면 ZSTD(또는 잘 튜닝된 GZIP)에 비해 실무상의 이점이 예상보다 작을 수 있습니다.
또한 잘 압축되지 않는 페이로드(이미 압축된 데이터, 많은 바이너리 형식)에는 매력적이지 않습니다—그저 CPU만 소모하게 됩니다.
브라우저는 HTTPS에서 Brotli를 대체로 잘 지원하므로 웹 트래픽에 인기입니다. 비브라우저 API 클라이언트(모바일 SDK, IoT 장치, 오래된 HTTP 스택)는 지원이 일관되지 않을 수 있으니 Accept-Encoding으로 올바르게 협상하고 폴백(보통 GZIP)을 유지하세요.
GZIP은 여전히 API 압축의 기본 답변입니다. 거의 모든 HTTP 클라이언트, 브라우저, 프록시, 게이트웨이가 Content-Encoding: gzip을 이해하고, 그 예측 가능성이 제어 불가능한 경로들 사이에서 중요합니다.
GZIP의 장점은 “최고”라서가 아니라 거의 잘못된 선택이 아니기 때문입니다. 많은 조직이 운영 경험을 가지고 있고, 웹 서버에 합리적 기본값이 있으며, 중간자가 최신 인코딩을 잘못 처리하는 경우가 GZIP에서는 적습니다.
API 페이로드(주로 JSON)에 대해서는 중간~낮은 레벨이 절충점입니다. 레벨 1–6은 대부분의 크기 감소를 제공하면서 CPU를 합리적으로 유지합니다.
아주 높은 레벨(8–9)은 약간 더 줄일 수 있지만 동적 응답과 지연이 중요한 트래픽에는 보통 가치가 적습니다.
현대 하드웨어에서 GZIP은 비슷한 압축률에서 일반적으로 ZSTD보다 느리고, 텍스트에서 Brotli의 최고 비율을 따라잡지 못합니다. 실무 API 워크로드에서는:
오래된 클라이언트, 임베디드 디바이스, 엄격한 기업 프록시, 레거시 게이트웨이 등을 지원해야 한다면 GZIP이 가장 안전한 선택입니다. 일부 중간자는 알 수 없는 인코딩을 제거하거나 패스스루하지 못하거나 협상을 깨뜨릴 수 있습니다—GZIP에서는 이런 문제가 덜 발생합니다.
환경이 혼재되어 불확실하면 GZIP으로 시작하고 완전 제어 경로에 대해서만 ZSTD/Brotli를 추가하는 것이 안전한 롤아웃 전략입니다.
압축 성공은 알고리즘만의 문제가 아닙니다. 가장 큰 결정자는 전송하는 데이터의 유형입니다. 어떤 페이로드는 ZSTD/Brotli/GZIP으로 크게 줄고, 어떤 것은 거의 줄지 않아 단지 CPU만 소모합니다.
텍스트 중심 응답은 반복 키, 공백, 예측 가능한 패턴 때문에 매우 잘 압축됩니다:
반복성과 구조가 많을수록 압축률이 좋습니다.
Protocol Buffers나 MessagePack 같은 바이너리 형식은 JSON보다 컴팩트하지만 무작위는 아닙니다. 반복 태그, 유사한 레코드 레이아웃, 예측 가능한 시퀀스를 포함할 수 있어 여전히 압축될 수 있는 경우가 많습니다, 특히 큰 응답이나 리스트가 많은 엔드포인트에서는 더더욱. 신뢰할 수 있는 답은 실측입니다: 실제 트래픽 샘플로 압축 온/오프를 비교하세요.
많은 형식은 내부적으로 이미 압축을 사용합니다. HTTP 응답 수준에서 추가 압축을 적용하면 거의 절감이 없고 오히려 응답 시간이 증가할 수 있습니다:
이 경우 콘텐츠 타입으로 압축을 비활성화하는 것이 일반적입니다.
간단한 접근은 응답이 최소 크기를 넘을 때만 압축하는 것입니다:
Content-Encoding을 활성화이렇게 하면 CPU가 실제로 대역폭을 줄이고 엔드투엔드 성능을 개선하는 페이로드에 집중됩니다.
클라이언트와 서버가 인코딩에 합의해야 압축이 원활히 동작합니다. 이 합의는 클라이언트가 보내는 Accept-Encoding과 서버가 보내는 Content-Encoding으로 이뤄집니다.
클라이언트는 디코딩 가능한 것을 광고합니다:
GET /v1/orders HTTP/1.1
Host: api.example
Accept-Encoding: zstd, br, gzip
서버는 하나를 선택하고 사용한 것을 선언합니다:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: zstd
클라이언트가 Accept-Encoding: gzip만 보냈는데 서버가 Content-Encoding: br로 응답하면 해당 클라이언트는 본문을 파싱하지 못할 수 있습니다. 클라이언트가 Accept-Encoding을 보내지 않으면 가장 안전한 기본은 압축하지 않는 것입니다.
API에서는 실무적으로 다음과 같은 우선순위를 사용하는 경우가 많습니다:
zstd(속도/비율 균형이 좋음)br(종종 더 작음, 가끔 느림)gzip(최대한의 호환성)즉: zstd > br > gzip.
이 규칙이 항상 보편적인 것은 아닙니다: 트래픽이 대부분 브라우저라면 br의 우선순위를 더 올려야 하고, 오래된 모바일 클라이언트가 많다면 gzip이 가장 안전한 선택일 수 있습니다.
응답을 여러 인코딩으로 제공할 수 있다면 다음을 추가하세요:
Vary: Accept-Encoding
이 헤더가 없으면 CDN이나 프록시가 gzip(또는 zstd) 버전을 캐시하고 해당 인코딩을 요청하지 않은 클라이언트에게 잘못 제공할 수 있습니다.
일부 클라이언트는 지원을 광고하지만 디코더에 버그가 있을 수 있습니다. 탄력성을 유지하려면:
zstd에 대한 디코딩 오류가 급증하면 임시로 gzip으로 폴백협상은 모든 바이트를 쥐어짜는 것보다 클라이언트를 깨뜨리지 않는 데 더 중점을 둬야 합니다.
API 압축은 고립된 문제가 아닙니다. 전송 프로토콜, TLS 오버헤드, CDN/게이트웨이가 실제 결과를 바꾸거나 잘못 구성되면 문제를 일으킬 수 있습니다.
HTTP/2에서는 여러 요청이 하나의 TCP 연결을 공유합니다. 연결 오버헤드는 줄지만, 패킷 손실은 TCP 수준에서 모든 스트림을 차단할 수 있습니다. 압축은 응답 본문을 줄여 손실 사건 뒤에 “갇히는” 데이터 양을 줄여 도움을 줍니다.
HTTP/3는 QUIC(UDP) 위에서 동작해 스트림 간 TCP 수준의 헤드오브라인 블로킹을 피합니다. 페이로드 크기는 여전히 중요하지만 손실에 따른 패널티는 연결당 덜 극단적입니다. 실무에서는 압축이 여전히 유용하며, 주로 대역폭 절감과 더 빠른 "time to last byte" 측면에서 혜택이 나타납니다.
TLS는 이미 CPU를 소모합니다(핸드셰이크, 암호화/복호화). 높은 레벨의 압축을 추가하면 피크 시 CPU 한도를 넘길 수 있습니다. 따라서 프로덕션에서는 "빠른 압축과 적당한 비율" 설정이 보통 "최대 비율"보다 더 낫습니다.
일부 CDN/게이트웨이는 특정 MIME 타입을 자동으로 압축하고, 일부는 오리진이 보낸 것을 그대로 통과시키며, 몇몇은 잘못 구성되면 Content-Encoding을 제거하거나 정규화할 수 있습니다.
라우트별 동작을 검증하고 Vary: Accept-Encoding이 유지되도록 하여 캐시가 잘못된 버전을 제공하지 않도록 하세요.
엣지에서 캐시한다면 인코딩별 별도 변형(gzip/br/zstd)을 저장하는 것을 고려하세요. 매 요청마다 다시 압축하는 대신 미리 압축된 버전을 캐시하는 것이 비용 효율적입니다. 오리진에서 캐시하는 경우에도 엣지가 협상하고 여러 인코딩을 캐시하도록 할 수 있습니다.
핵심은 일관성입니다: 올바른 Content-Encoding, 올바른 Vary, 그리고 어디서 압축을 담당할지의 명확한 소유구조.
Accept-Encoding: br을 광고하면 Brotli를 우선시하세요. 브라우저는 Brotli 디코딩을 효율적으로 수행하고 텍스트 응답에서 보통 더 나은 크기 절감을 제공합니다.초기에는 놀라움을 줄 가능성이 적은 레벨로 시작하세요:
더 강한 비율이 필요하면 프로덕션 유사 페이로드 샘플로 검증하고 p95/p99 레이턴시를 추적한 뒤 레벨을 올리세요.
작은 응답을 압축하면 CPU 비용이 절감보다 클 수 있습니다. 실무 시작점:
(1) 절감된 바이트, (2) 추가된 서버 시간, (3) 엔드투엔드 레이턴시 변화 를 비교하며 튜닝하세요.
압축은 기능 플래그 뒤에 숨겨서 롤아웃한 뒤, 엔드포인트별 구성(예: /v1/search는 활성화, 이미 작은 엔드포인트는 비활성화)을 추가하세요. 문제 해결과 엣지 클라이언트를 위해 Accept-Encoding: identity로 클라이언트 옵트아웃을 제공하세요. 캐시 정확성을 위해 항상 Vary: Accept-Encoding을 포함하세요.
빠르게 API를 생성하는 환경(예: React 프런트엔드와 Go + PostgreSQL 백엔드를 빠르게 배포)에서는 압축이 "작은 설정, 큰 영향"을 주는 구성 요소 중 하나입니다. 프로덕션에서 엔드포인트와 페이로드 모양이 안정화되면 응답 압축과 캐시 헤더를 튜닝하는 것이 흔한 과정입니다.
Koder.ai 같은 플랫폼에서는 팀이 전체 스택을 빠르게 프로토타입하고 배포한 뒤, 프로덕션 트래픽을 기반으로 압축 동작을 튜닝하는 경우가 많습니다. 핵심 요점은 같습니
응답이 텍스트 중심(JSON/GraphQL/XML/HTML)이고 중간 크기 이상이며 사용자가 느리거나 비용이 높은 네트워크에 있거나 발신(egress) 비용이 유의미할 때 응답 압축을 사용하세요. 작은 응답, 이미 압축된 미디어(JPEG/MP4/ZIP/PDF) 또는 추가 작업이 p95/p99 레이턴시를 악화시키는 CPU 바운드 서비스에는 압축을 건너뛰거나 높은 임계값을 사용하세요.
압축은 대역폭을 CPU(및 때로는 메모리)로 교환하기 때문에 API가 더 느려질 수 있습니다. 압축에 드는 시간은 서버가 바이트 전송을 시작하는 시점을 지연시키고, 부하가 걸리면 큐잉이 증폭되어 특히 **테일 레이턴시(p95/p99)**를 악화시킬 수 있습니다. 가장 좋은 설정은 단순히 바이트를 줄이는 것이 아니라 엔드투엔드 소요 시간을 줄이는 설정입니다.
많은 API에서 실용적인 우선순위는 다음과 같습니다:
zstd 우선(빠르고 좋은 비율)br(텍스트에서 더 작을 수 있으나 CPU 비용이 클 수 있음)gzip(가장 넓은 호환성)항상 최종 선택은 클라이언트가 에 광고한 내용을 기준으로 하고, 안전한 폴백(보통 또는 )을 유지하세요.
낮은 수준에서 시작하고 측정하세요.
작은 페이로드에 대해 CPU를 낭비하지 않도록 최소 응답 크기 임계값을 사용하세요.
엔드포인트별로 바이트 절감, 추가된 서버 시간, p50/p95/p99에 미치는 영향을 비교하며 튜닝하세요.
구조화되고 반복적인 콘텐츠에 압축 효과가 큽니다:
HTTP 협상에 따라야 합니다:
Accept-Encoding을 보냅니다(예: zstd, br, gzip).Content-Encoding: zstd).클라이언트가 을 보내지 않았다면 안전한 기본은 보통 입니다. 클라이언트가 광고하지 않은 을 반환하면 클라이언트가 본문을 파싱하지 못할 위험이 있습니다.
다음 헤더를 추가하세요:
Vary: Accept-Encoding이 헤더는 CDN/프록시가 (예:) gzip 응답을 캐시해 해당 인코딩을 요청하지 않거나 디코딩하지 못하는 클라이언트에게 잘못 제공하는 것을 방지합니다. 여러 인코딩을 지원하면 이 헤더는 정확한 캐싱 동작에 필수입니다.
대표적인 장애 원인은 다음과 같습니다:
성능 기능처럼 롤아웃하세요:
Accept-Encodinggzipidentity높은 레벨은 보통 크기 감소가 점진적이며, CPU를 크게 올려 p95/p99를 악화시킬 수 있습니다.
일반적 접근은 텍스트 계열 Content-Type에 대해서만 압축을 활성화하고, 이미 압축된 형식에 대해서는 비활성화하는 것입니다.
Accept-EncodingContent-EncodingContent-Encoding이 설정되어 있지만 본문이 압축되어 있지 않음 또는 그 반대)Accept-Encoding을 무시하거나 클라이언트가 광고하지 않은 인코딩 반환)Content-Length가 잘못됨)디버깅 시에는 원시 응답 헤더를 캡처하고 신뢰할 수 있는 도구/클라이언트로 디코딩을 검증하세요.
gzip으로 폴백)부하에서 테일 레이턴시가 상승하면 레벨을 낮추거나 임계값을 올리거나 더 빠른 코덱(보통 ZSTD)으로 전환하세요.