avatar
Published on

그런데 왜 brotli를 사용하지 않는 것일까? 🤔

Author
  • avatar
    Name
    yceffort

이 전 블로그 포스팅을 통해서 brotli가 무엇인지, 왜 좋은지, 왜 써야 하는지에 대해서 알아봤다. 그러나 한 가지 더 궁금한 것이 있다. 과연 비단 IE 에서 지원하지 않는다는 이유만으로 쓰지 않는 것일까? 시스템 엔지니어들이 게을러서 brotli 지원을 안해주는 것일까?

Gzip은 웹 압축에 있어서 일종의 디 팩토 같이 자리잡고 있다. 대충 모든 웹사이트의 80% 정도가 gzip으로 서빙되고 있는 것으로 알려져 있다. 참고 근데 압축이 안되있는 사이트가 60% 쯤 정도 된다. (허허)

아무튼 지간에, Gzip도 충분히 효과적인 압축 알고리즘이지만, brotli가 gzip보다 더 크게 압축해 주는 알고리즘으로 등장했다.

https://csswizardry.com/wp-content/uploads/2020/04/react-dom-brotli.png

그리고 이전에 말했듯이, IE에서는 brotli를 지원하지 않지만, 현재 사이트의 93%가 IE외의 환경에서 서비스 되므로 대부분의 사용자에게 brotli를 이용하여 서비스 할 수 있다. 6%는 무시하라는 말처럼 들릴 수도 있지만(?) brotli를 받아줄 수 없는 브라우저의 경우 gzip으로 fallback 되게 할 수 있다. 이는 나중에 다룬다.

그런데 왜, 많은 사이트들이 brotli를 사용하지 않는 것일까? brotli로 전환하는 것은 얼마나 중요한 것일까?

작다는 것이 반드시 더 빠르다는 것은 아니다.

물론, 일반적으로는 작은 파일이 더 빠르게 도착하는 것이 사실이다. 그렇다고 파일 크기를 20% 줄였다고 20% 더 빠르게 도착하는 것은 아니다. 다시 말해 파일 크기는 웹 성능을 측정하는 한가지 측면일 뿐, 이 외에도 리소스 대기시간, 패킷손실과 같은 다른 많은 요소와 상수가 웹 성능에 영향을 미친다. 크기를 절약하는 것은 데이터를 더 빠르게 도착하게 하는 것에 도움이 되지만, latency 에 제한이 있는 경우에는 데이터 청크가 도달하는 속도에 영향을 미치지 않는다.

TCP, Packet, Round trip

먼저 tcp에 대해 알아보자. 서버에서 파일을 받을 때는 한번에 전체 파일을 가져오지 않는다. HTTP가 위치한 TCP는 파일을 세그먼트 또는 패킷으로 나눈다. 이러한 패킷은 순서대로 클라이언트에 전송된다. 패킷은 클라이언트가 모든 패킷을 가질 때 까지, 다음 패킷을 전송하기 전에 각 패킷을 확인하고 전송하며, 클라이언트가 이러한 패킷을 조립하여 하나의 파일로 조립하게 된다. 이러한 일련의 패킷은 라운드 트립 방식으로 전송된다.

각각의 새로운 TCP 연결은 현재 사용 가능한 대역폭이 무엇인지, 연결을 얼마나 신뢰할 수 있는지를 알 수 있는 방법이 없다. (예: 패킷 손실 등) 만약 서버가 한 연결당 1메가 비트 짜리 연결을 통애 메가바이트급 전송을 시도한다면, 서버 연결 요청이 쇄도하여 혼잡이 발생하게 될 것이다. 만약 1메가 바이트의 사용가능한 연결을 바탕으로 1메가 비트의 데이터를 전송하려고 한다면, 용량이 낭비되고 말 것이다.

이를 해결하기 위해 TCP는 slow start를 사용한다. 각각의 새로운 TCP연결은 첫번째 왕복에서 데이터 패킷 10개만 사용하도록 제한된다. (약 14kb) 10개가 성공적으로 도착한다면, 그 다음에는 20개의 패킷을, 그다음에는 40, 80, 160 으로 기하급수적으로 증가하는데 이 증가는 다음 수준에 다다를 때 까지 발생한다.

  1. 패킷손실이 발생. 이 지점에서 서버는 마지막 패킷 수를 절반으로 줄이고 다시 시도
  2. 대역폭 최대에 다달아서 최대 용량을 사용가능한 경우

이러한 전략은 웹 애플리케이션이 만드는 모든 새로운 TCP연결에 활용된다.

다시 브라우저로 돌아와서, 새 TCP 연결의 초기 대역폭 용량은 14kb다. 리액트로 예를 들어서, 아래의 표를 보자.

Round TripTCP Capacity (kb)Cumulative Transfer (kb)React DOM
11414
22842Gzip(37kb) Brotli(33kb)
35698
4112210Uncompressed (119kb)
5224434

(둘다 최대 치로 압축)

압축은 brotli가 4kb나 더 되었지만, TCP 작동 원리에 따라서 모두 두 번째 왕복에 다운로드가 완료 되었다. 따라서, 모든 왕복시간이 거의 균일하다면, Gzip이나 brotli나 모두 전송시간에 차이가 없다는 결론이 나오게 된다.

따라서 요점은 파일 크기가 아니라, TCP, 즉 패킷 및 왕복에 관한 것이다. 파일을 더 작게 만드는 것이 문제가 아니고, 파일을 의미있게 작게 만들어서 더 낮은 왕복 버킷에 집어 넣어야 한다. 결과적으로, brotli가 gzip보다 더욱 효과적이라면 파일을 왕복 임계값 아래로 (위의 예제에서는 14kb급으로), 더욱 공격적으로 압축을 할 수 있어야 한다.

이 규칙은 또한 새로운 TCP 연결에만 적용되며, primed TCP 연결로 가져온 파일은 영향을 받지 않는다. 이는 두가지 중요한 점을 제시한다.

  1. 정적자산을 자체 호스팅 하는것이 중요하다. 이렇게 하면 이미 워밍업되어 있는 (새로 연결되어 있는) TCP에 연결 할 수 있으므로, 시작속도가 느려지는 것을 방지할 수 있다.
  2. 기하 급수적으로 패킷이 커지면 얼마나 거대한 대역폭에 빠르게 도달할 수 있다. 연결을 더 많이 쓰고 재사용할 수록 용량이 빠르게 늘어난다.
Round TripTCP Capacity (kb)Cumulative Transfer (kb)
11414
22842
35698
4112210
5224434
6448882
78961778
817923570
935847154
10716814322
.........
20734003214680050

10 회 왕복정도면, TCP는 7168kb이고, 14322kb를 전송했다. 이는 일반적인 웹 브라우징에 적합하다. 여기서 말하는 일반적인이라는 것은 대역폭 한계에 도달하기 전에 전체 웹페이지와 모든 하위 리소스를 로드 하는 것이다. 따라서, 1gbps 급 속도는 대부분 사용하지 않기 때문에 일상적인 브라우징이 더 빨라졌던가 하는 것은 느끼지 못하게 된다.

실제 세계에서의 실험

brotli로 제공되는 사이트에 brotli 사용을 중지하면 된다. https://www.webpagetest.org/ 에서 content-encodinggzip을 명시해주면 된다.

  • 압축을 완전히 비활성화: accept-encoding: 랜덤문자열
  • brotli비활성화 및 gzip: accept-encoding: gzip
  • brotli: 비워 둔다.

결과: https://docs.google.com/spreadsheets/d/18A_dP1DuavmMjmFnHXf4gdw6ThTne5e6UyzUUgxKI5s/edit#gid=0

  • gzip 크기 감소 vs 비압축: 73% 감소
  • gzip fcp vs 비 압축: 23 % 감소
  • brotli 크기 감소 vs gzip: 5%
  • brotli fcp vs 5zip: 3%

gzip은 비 압축 대비 약 72% 정도의 크기 감소를 이뤄 냈고, 이에 더해 brotli는 gzip 대비 5.7%를 더 감소 시켰다.

결론

gzip에 비해 brotli가 갖는 이점은 미미하다.

brotli를 활성화 하는 것이 CDN 관리자 메뉴의 버튼 하나를 누르는 것 만큼 간단하다면 지금 바로 실행하는 것이 좋다. 최소한의 개선이라도 없는 것보단 낫고, fallback 제공도 잘 되어 있다.

가능한 경우 정적 자산의 경우 가능한 가장 큰 압축 수준을 사용하며, 동적인 요소에 대해서는 중간 정도의 압축을 해주는 것이 좋다. 만약 nginx 를 사용중이라면, 현재 압축수준이 1로 (기본값으로) 되어 있는지 확인해보는 것이 좋다.

brotli를 구현하기 위해 너무 애쓸 필요는 없다. 압축할 수 있는 모든 항목에 대해 gzip이 제공되고 있다면, 그것만으로도 충분할 수 있다.

출처: https://csswizardry.com/2020/04/real-world-effectiveness-of-brotli/#smaller-doesnt-necessarily-mean-faster