컴퓨터 공학 & 통신

Http 통신 응답 압축 (feat. gzip, NestJS)

왈왈디 2024. 12. 22. 23:21
728x90

Http 통신에서 압축이란?

Http 통신에서 압축(compression)이란 성능을 향상 시키는 주요한 방법 중 하나다.
요청과 응답 body를 압축함으로써 최대 70%까지 대역폭 사용량을 감소시킬 수 있고,
속도를 향상 시킬 수 있다.
 
다만, 압축과 해제를 위해서는 CPU가 조금 더 사용되기 때문에,
[트래픽 크기와 속도 vs CPU 사용량]의 trade off 관계이다.
압축이 CPU를 많이 사용하는 것은 아니지만,
압축 효율이 그다지 좋지 않은 경우에는 압축하지 않는 것이 나을 수 있다.
 
또, 이미 압축된 이미지, 오디오, 비디오 파일을 한 번 더 압축하는 것은
효용보다 비용이 더 크므로
두 번 압축하지 않도록 주의해야 한다.
 
실질적으로, 브라우저와 서버가 압축 기능을 지원하기 때문에
개발자가 직접 압축 알고리즘으로 압축을 구현하는 일은 거의 없다.
다만 적절하게 압축이 이루어지도록 설정이 필요하다.
 
압축은

  • 파일 포맷 압축,
  • 종단 간 압축,
  • HTTP 커넥션 중 두 노드 사이

등 세 개의 계층에서 이뤄질 수 있다.
그 중 종단 간 압축에 대해 알아보자. 

종단 간 (End-to-end) 압축

종단 간 압축은 서버에서 응답 바디를 압축 처리하여 클라이언트에게 도달할 때까지
바디가 변하지 않는 것을 의미한다.
중간에 어떤 노드들을 거치든지 바디를 건들이지 않는다.
 
최신 브라우저와 서버들은 종단 간 압축을 대부분 지원하므로,
압축 알고리즘만 결정하면 된다.
 
최근에는 gzip, br이 가장 흔히 사용되는 압축 알고리즘이다.
gzip이 가장 일반적이며, br이 신흥강자로 떠오르고 있다.
 
사용할 알고리즘을 선택하기 위해서는 브라우저와 서버 간 협상이 필요하다.
브라우저와 서버는 http 헤더를 통해 의사소통한다.
 
브라우저는 브라우저가 지원하는 압축 알고리즘과, 
알고리즘의 우선순위를 Accept-Encoding 헤더에 담아 전송한다.

Accept-Encoding: gzip

Accept-Encoding: gzip, compress, br

Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1

 
q값은 우선순위, 선호도를 의미하는 1부터 0까지의 값으로
1에 가까울 수록 선호도가 높음을 의미한다.
 
서버는 Accept-Encoding 헤더를 통해 확인한 압축 방식으로
응답 바디를 압축하고
서버가 선택한 알고리즘을 Content-Encoding 헤더에 담아 보낸다.

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
Content-Encoding: identity
Content-Encoding: br

 
웹 서버에 따라
Apache는 mod_defalte,
nginx는 ngx_http_gzip_module,
IIS는 <httpCompression> 엘리먼트를 지원한다.

NestJS 서버 응답 압축 방법

일반적으로 nginx와 같은 리버스 프록시 웹 서버가 압축을 지원하는 경우에는
서버에서 이중으로 압축하지 않는 것이 좋지만,
그렇지 않은 경우 NestJS의 압축 미들웨어를 사용할 수 있다.
 

express - compression 미들웨어

express를 사용하는 경우 compression 미들웨어를 사용하면 gzip 압축을 사용할 수 있다.
 
먼저 compression 패키지를 설치한다.

npm i --save compression

 
설치 후 initialization 파일에 글로벌 미들웨어로 등록한다.

import * as compression from 'compression';
// somewhere in your initialization file
app.use(compression());

 

fastify - fastify-compress 미들웨어

fastifyAdapter를 사용한다면, fastify-compress 패키지를 사용해야 한다.

npm i --save @fastify/compress

 
설치 후 역 initialization 파일에 글로벌 미들웨어로 등록한다.

import compression from '@fastify/compress';
// somewhere in your initialization file
await app.register(compression);

 
디폴트로, @fastify/compress 모듈은 Node 11.7.0 이후 버전부터
브라우저가 지원한다면 Brotli 압축 방식을 사용한다.
Brotli 방식은 꽤 효율적이지미나, 꽤 느리다.
Brotli는 디폴트로 최대 압축률을 11로 설정하는데,
압축 시간 단축을 위해 BROTLI_PARAM_QUALITY를 최소 0에서 최대 11까지 조정할 수 있다.
퍼포먼스 향상을 위해 이 값에 대한 세부 조정이 필요하다.
예를 들면 아래와 같이 4로 조정할 수 있다.

import { constants } from 'zlib';
// somewhere in your initialization file
await app.register(compression, { brotliOptions: { params: { [constants.BROTLI_PARAM_QUALITY]: 4 } } });

 
더 간단하게는 fastify/compress가 deflate와 gzip 압축만 사용하도록 할 수도 있다.
응답 크기는 더 커지겠지만, 더 빠른 응답이 가능하다.
압축 알고리즘을 지정하기 위해서는 app.register의 두 번째 인자를 입력하면 된다.

await app.register(compression, { encodings: ['gzip', 'deflate'] });

위와 같이 입력하면 둘 다 사용 가능할 때,
gzip을 우선순위로 사용하게 된다.
 
+ 서버 투 서버로 NestJS 서버에서 Accept-Encoding 헤더를 포함하여
axios 등으로 다른 서버에 요청을 보낼 때
응답이 압축되어 오더라도
axios는 기본적으로 gzip, deflate 등 표준 HTTP 압축 형식을 자동으로 압축 해제하므로
별도로 압축 해제 작업을 수행할 필요는 없다.
 

참고 자료

Compression in HTTP - HTTP | MDN

Compression is an important way to increase the performance of a website. For some documents, size reduction of up to 70% lowers the bandwidth capacity needs. Over the years, algorithms also got more efficient, and new ones are supported by clients and ser

developer.mozilla.org

 

728x90