브라우저의 개발자도구를 열심히 보다보면 네트워크 탭에서 accept-encoding
이라는 헤더를 볼 수 있다.
이 헤더에 대부분 HTTP 응답 내용의 압축 알고리즘이 명시된다. 요청을 보낼 때 클라이언트가 어떤 압축 알고리즘을 수용할 수 있는지 나타내면서 서버에서 이러한 방식으로 압축해달라고 요청하는 의미가 될 것이다.
요청을 받으면 서버에서는 클라이언트가 원하는 압축 방식중에서 가능한 알고리즘으로 압축한 뒤에 응답을 보내주면서 사용된 압축 알고리즘을 content-encoding
헤더에 명시한다.
이 헤더들의 값으로 gzip
, compress
, br
, deflate
와 같은 알고리즘들이 많이 사용되는데 어떤 방식으로 압축하는지 알아보자.
https://datatracker.ietf.org/doc/html/rfc1951
deflate 알고리즘은 LZ77과 Huffman Coding 알고리즘을 함께 적용하는 알고리즘이다.
앞으로 LZ로 시작하는 알고리즘이 자주 등장할텐데 대부분의 압축 알고리즘은 LZ77, LZ78에서 파생된 알고리즘을 기반으로 하기 때문이다.
deflate 알고리즘은 RFC에서는 LZ77을 사용한다고 하는데, 다른 자료에서는 LZSS를 사용한다고 하는것을 보아 여러 종류가 있는 듯 하다.
둘 다 간략하게 알아보자.
https://en.wikipedia.org/wiki/LZ77_and_LZ78
입력 문자열을 순회하면서 이전에 등장했었던 반복되는 부분문자열을 압축하는 방식
예를 들어 ababc라는 문자열을 압축하면
<0,0,a>
<0,0,b>
<2,2,c>
이렇게 3개의 LLD블록으로 압축된다.
https://en.wikipedia.org/wiki/Lempel–Ziv–Storer–Szymanski
LZ77 알고리즘의 단점을 보완한 알고리즘
위의 LZ77의 예에서 볼 수 있듯이 문자 a를 압축한 결과가 <0,0,a>
로 되면 오히려 압축결과의 크기가 더 커지게 된다.
이런 경우를 방지하기위해 일정 길이 이하의 반복문자는 아예 압축하지 않는 방식으로 최적화한 알고리즘이 LZSS이다.
그래서 LZ77과 똑같이 ababc라는 문자열을 압축하고 길이 1은 압축하지 않는것으로 설정하면
ab<2,2,c>
로 압축될 것이다
https://en.wikipedia.org/wiki/Huffman_coding
허프만이라는 사람이 만든 가변길이 코딩을 만들기 위한 알고리즘이다.
가변 길이 코드란 문자를 표현하는 비트를 최대한 짧게 사용해서 길이를 압축시키는 아이디어다.
반대로 고정 길이 코드는 모든 문자가 똑같은 길이의 코드를 가지는 것을 말한다.
예를 들어, 보통 아스키 코드는 고정 길이 코드로, 8비트로 표현된다.
A=0000 0001
B=0000 0010
C=0000 0011
반면 가변 길이 코드로는 다음처럼 표현할 수 있다.
A=0
B=1
C=01
이제 여기서 중요한 점이 2가지 있다.
허프만 코딩 알고리즘은 이진트리를 만드는 방시그로 이 문제들을 효율적으로 해결한다.
실제로 최적의 코드를 만들 수 있는 것으로 알려져 있다.
허프만 트리를 만드는 과정은 간단하다.
위 과정을 반복하면 된다.
예를 들어 각 심볼의 등장빈도가 다음과 같다고 하자
A=40
B=20
C=67
D=23
정렬하면 다음과 같다
C(67), A(40), D(23), B(20)
D와 B를 자식 노드로 가지는 트리를 만든다
C(67), A(40), DB(43)
다시 정렬
C(67), DB(43), A(40)
DB와 A를 트리로 만든다
C(67), DB-A(83)
다시 정렬하고
DB-A(83), C(67)
최종적으로 만든 트리는 다음처럼 된다
[]
[] C
[] A
D B
루트노드에서 각 단말노드로 가면서 왼쪽 경로는 0, 오른쪽 경로는 1을 붙이면
A=01
B=001
C=1
D=000
이 된다.
다시 deflate 알고리즘을 살펴보면, LZ77알고리즘으로 압축해서 나온 LLD블록들을 다시 허프만코딩으로 압축하면 deflate 알고리즘이 된다.
https://datatracker.ietf.org/doc/html/rfc1952
https://stackoverflow.com/questions/7243705/what-is-the-advantage-of-gzip-vs-deflate-compression
gzip의 압축 알고리즘은 deflate 알고리즘과 같다.
deflate와 비교해서 차이점은 gzip 포맷은 추가적인 헤더와 체크섬을 사용하여 오류검출에 더 강점이 있다는 점이다.
그래서인지 웹사이트에서 deflate보다 gzip이 더 권장되는 것으로 보인다.
compress는 LZW 알고리즘을 의미한다.
LZW도 간단한데, 원래 문자를 표현하는데 사용하는 8비트를 9~12비트까지 확장한다.
그리고 확장한 공간에 반복되는 부분문자열을 그룹화하여 할당하는 방식을 취한다.
만약에 ABAB라는 입력이 들어오면, 앞에서부터 읽으면서 테이블을 만든다.
이 때 두 번째 AB는 앞에서 반복되었으므로 사전 테이블에 256으로 추가한다
A=65, B=66, AB=256
압축 결과는 65 66 256이 된다.
이런 방식으로 압축하는 알고리즘이 compress(LZW)이다.
https://datatracker.ietf.org/doc/html/rfc7932
구글에서 2015년에 발표한 압축 알고리즘으로 웹사이트의 정적 컨텐츠를 압축할 떄 gzip보다 빠르고, 압축률이 더 좋다고 한다. 성능을 위해선 이제 가능한 brotli를 우선적으로 사용하는 것이 좋을 것이다.