사전 압축
개념

압축

gabury1고친 사람 github-actions[bot]

압축은 데이터를 같은 내용 그대로 더 적은 비트로 줄여 적습니다. 같은 문구가 되풀이되는 서버 로그가 그런 데이터입니다. 줄인 것은 곧바로 읽을 수 없습니다. 읽을 때 다시 풀어 원래 형태로 되돌립니다.

쉽고 빠른 이해

압축은 데이터를 더 적은 비트로 줄여 적어 두는 일입니다. 같은 문구가 수없이 되풀이되는 서버 로그가 원래 크기의 몇십 분의 일로 줄어드는 것이 그 결과입니다.

줄이지 않으면 같은 내용을 담으려고 디스크를 더 사고, 같은 파일을 보내려고 회선을 더 오래 잡습니다. 데이터가 커질수록 그 몫이 그대로 비용이 됩니다.

  1. 되풀이되는 대목을 찾아, 다시 적는 대신 앞에 나온 것을 가리키게 바꿉니다
  2. 자주 나오는 값에 짧은 비트를 주고 드문 값에 긴 비트를 줍니다
  3. 읽을 때 이 과정을 거꾸로 밟아 원래 내용을 되살립니다

대가는 줄이고 푸는 데 드는 계산 시간입니다. 이미 줄일 거리가 없는 데이터는 압축해도 안 줄어듭니다. 오히려 조금 커집니다.

상세

부피 큰 겨울 이불을 비닐 봉지에 담아 공기를 빼내면 두께가 손가락 몇 마디로 내려앉습니다. 줄어드는 몫은 솜 사이에 들어 있던 공기가 전부라, 그 공기가 다 빠진 뒤로는 아무리 눌러도 꿈쩍하지 않습니다. 봉지를 열면 이불은 원래 두께로 되돌아옵니다.

압축은 데이터를 같은 내용 그대로 더 적은 비트로 다시 적는 일입니다. 같은 문구가 수없이 되풀이되는 서버 로그, 큰 구역이 한 색으로 덮인 그림 파일이 그런 데이터입니다.

줄여서 나온 결과를 압축본이라 부릅니다. 압축본에서 원래 데이터를 되살리는 일은 압축 해제입니다. 둘은 언제나 짝입니다. 푸는 절차가 없으면 줄인 데이터는 못 쓰는 데이터입니다.

세 낱말이 어떻게 이어지는지를 그림으로 보면 이렇습니다.

flowchart TD
    A["원본 데이터"] --> B["압축 · 줄여 적는다"]
    B --> C["압축본 · 저장하거나 보낸다"]
    C --> D["압축 해제 · 되살린다"]
    D --> E["원본 데이터"]

되풀이와 치우침

데이터를 줄일 수 있는 까닭은 대부분의 데이터가 헐겁게 적혀 있기 때문입니다. 헐겁다는 것은 같은 것이 되풀이되거나, 나올 수 있는 값 가운데 일부만 자꾸 나온다는 뜻입니다.

되풀이는 앞에 나온 것을 가리켜 줄입니다. 같은 문구가 두 번째로 나오면 그 문구를 다시 적는 대신 「몇 글자 앞에 나온 것 몇 글자」라고만 적어 둡니다. 앞의 수는 얼마나 뒤로 돌아갈지를 말하는 거리입니다. 뒤의 수는 거기서 몇 글자를 베낄지를 말하는 길이입니다.

A B C D 뒤에 A B C 가 또 나오는 데이터로 그 가리킴을 그려 보면 이렇습니다.

flowchart TD
    subgraph 앞["앞서 나온 바이트"]
        A["A"]
        B["B"]
        C["C"]
        D["D"]
    end
    subgraph 뒤["이어서 A B C 가 또 나온다"]
        R["세 글자를 다시 안 적는다 · 거리 4 · 길이 3"]
    end
    앞 --> 뒤
    R --> |"4칸 뒤로 가서 3글자를 베껴 온다"| A

압축기가 찾아낸 되풀이를 모아 둔 표를 사전이라 부릅니다. 이 표를 써서 줄이는 방식이 사전 압축입니다.

되풀이가 눈에 보일 만큼 단순한 데이터라면 줄이는 모습도 단순합니다. 같은 글자가 이어지는 구간을 「무슨 글자가 몇 개」로 바꾸면 열 칸이 두 칸이 됩니다.

block-beta
columns 11
  o["원본"] a["A"] b["A"] c["A"] d["A"] e["A"] f["A"] g["A"] h["B"] i["B"] j["B"]
  p["줄임"] k["A7"]:5 l["B3"]:5

이 방식이 런 렝스 인코딩입니다. 실제 압축기는 이보다 훨씬 먼 곳의 되풀이까지 찾아냅니다. 글자 단위가 아니라 긴 구간을 통째로 가리킵니다.

값이 치우친 것은 표시의 길이를 나눠 주어 줄입니다. 모든 값에 같은 길이의 비트를 주는 대신, 자주 나오는 값에 짧은 비트를 주고 드문 값에 긴 비트를 줍니다.

A 가 자주 나오는 데이터 A A A B C 를 두 방식으로 적어 길이를 견주면 이렇습니다.

block-beta
columns 12
  f["고정 길이"]:2 a1["A"]:2 a2["A"]:2 a3["A"]:2 b1["B"]:2 c1["C"]:2
  v["가변 길이"]:2 a4["A"]:1 a5["A"]:1 a6["A"]:1 b2["B"]:2 c2["C"]:3 s["줄어든 몫"]:2

자주 나오는 A 를 짧게 적은 만큼 전체가 짧아졌습니다. 이렇게 값마다 비트 길이를 다르게 주는 방식을 엔트로피 부호화라고 합니다.

줄일 거리가 얼마나 남아 있는지를 재는 잣대는 엔트로피입니다. 엔트로피는 데이터에 들어 있는 정보의 양입니다. 어떤 압축기도 그 양보다 작게는 줄이지 못합니다.

무손실과 손실

압축은 푼 결과가 원본과 같은지로 크게 갈립니다. 이 갈림이 압축기를 고를 때 가장 먼저 정해지는 것입니다.

무손실 압축은 푼 결과가 원본과 비트 하나까지 같습니다. 문서·실행 파일·소스 코드처럼 한 글자만 달라져도 못 쓰게 되는 데이터는 이쪽뿐입니다.

손실 압축은 사람이 잘 못 느끼는 정보를 아예 버리고 줄입니다. 사진에서 눈이 둔한 색의 미세한 변화, 소리에서 큰 소리에 묻혀 안 들리는 작은 소리가 버려집니다. 버린 것은 돌아오지 않지만 무손실보다 훨씬 많이 줄어듭니다.

그래서 고르는 기준은 데이터의 종류가 아니라 「무엇을 잃어도 되나」입니다. 같은 사진이라도 사람이 보고 넘길 것은 손실로 줄이고, 판독해야 하는 의료 영상은 무손실로 다룹니다.

압축률과 속도의 맞바꿈

압축기를 고를 때 보는 축은 둘입니다. 얼마나 작아지느냐와 얼마나 빨리 되느냐입니다. 이 둘은 대개 서로를 밀어냅니다. 더 멀리까지 되풀이를 찾을수록 많이 줄어들고 그만큼 시간이 걸립니다.

압축률이라는 낱말은 두 뜻으로 쓰입니다. 10MB 짜리 파일이 2MB 로 줄었다고 해 봅시다.

원본이 압축본의 몇 배인지를 가리키는 곳에서는 5배입니다. 압축본이 원본의 몇 분의 몇인지를 가리키는 곳에서는 5분의 1입니다. 수치를 볼 때는 어느 뜻인지 먼저 확인해야 합니다.

같은 압축기라도 되풀이를 얼마나 멀리까지 찾을지 고르게 해 둔 것이 많습니다. 멀리까지 찾게 하면 더 줄어드는 대신 줄이는 시간이 늘어납니다. 푸는 시간은 대체로 그대로입니다.

줄이는 쪽이 푸는 쪽보다 오래 걸립니다. 그래서 한 번 줄여 여러 번 읽는 데이터일수록 값이 납니다.

어느 쪽을 고를지는 무엇이 모자란지가 정합니다.

무엇이 모자란가 고르는 쪽
회선은 좁고 프로세서는 남는다 느려도 많이 줄이는 쪽
프로세서는 바쁘고 회선은 넓다 덜 줄어도 빨리 끝나는 쪽
한 번 줄여 오래 보관한다 줄이기는 느려도 되고 풀기가 빨라야 한다

표의 마지막 줄이 백업과 아카이브입니다. 줄이는 일은 한 번뿐입니다. 읽는 일은 몇 년에 걸쳐 드문드문 일어나므로 줄이는 시간은 거의 값으로 안 칩니다.

줄지 않는 데이터

모든 데이터를 줄여 주는 압축기는 없습니다. 어떤 압축기든 줄여 주는 입력이 있으면 도리어 늘어나는 입력도 반드시 있습니다.

까닭은 되돌릴 수 있어야 한다는 데 있습니다. 서로 다른 입력은 서로 다른 압축본이 되어야 합니다. 2비트로 적을 수 있는 것은 네 가지뿐입니다. 3비트 입력 여덟 가지를 전부 2비트에 담으면 둘이 같은 압축본으로 떨어져 못 되돌립니다.

여덟 가지를 네 가지에 담을 때 무슨 일이 생기는지를 그려 보면 이렇습니다.

flowchart TD
    subgraph 입력["3비트 입력 · 여덟 가지"]
        I1["000"]
        I2["001"]
        I3["010"]
        I4["나머지 다섯 가지"]
    end
    subgraph 압축본["2비트 압축본 · 네 가지"]
        O1["00"]
        O2["01"]
        O3["나머지 두 가지"]
    end
    I1 --> O1
    I2 --> O1
    I3 --> O2
    I4 --> O3

000 과 001 이 똑같이 00 이 되면, 00 을 풀 때 어느 쪽이었는지 알 길이 없습니다. 모두를 짧게 담을 방법이 애초에 없습니다.

실무에서 이 벽을 만나는 것은 대개 이미 줄여 둔 데이터입니다. 압축된 파일, 열쇠 없이는 못 읽게 바꿔 둔 암호화 데이터, 난수는 되풀이도 치우침도 거의 없습니다.

이런 것을 다시 압축하면 도리어 조금 커집니다. 압축본 앞에는 무슨 방식으로 줄였는지를 적어 두는 헤더가 붙습니다. 줄일 거리가 없으면 줄어든 몫 없이 그 헤더만 더해집니다.

그래서 압축은 한 번만 겁니다. 이미 줄여 둔 파일을 여럿 묶어 보내면서 또 압축을 거는 것은 프로세서만 쓰고 끝납니다.

다른 변환과의 순서

압축을 다른 변환과 같이 쓸 때는 순서가 결과를 바꿉니다. 같이 놓이는 것은 대개 둘입니다. 직렬화는 메모리 안의 데이터를 바이트로 펴는 일입니다. 암호화는 열쇠 없이는 못 읽게 바꾸는 일입니다.

압축은 그 둘 사이에 놓입니다. 직렬화한 뒤라야 줄일 되풀이가 바이트로 드러납니다. 암호화하기 전이라야 그 되풀이가 아직 남아 있습니다. 잘 된 암호문은 난수와 구별되지 않아서 줄일 거리가 하나도 없습니다.

세 변환을 어느 순서로 놓는지를 그림 하나로 고정하면 이렇습니다.

flowchart TD
    A["메모리 안의 데이터"] --> B["직렬화 · 바이트로 편다"]
    B --> C["압축 · 되풀이를 줄인다"]
    C --> D["암호화 · 못 읽게 바꾼다"]
    D --> E["보내거나 저장한다"]

순서를 지켜도 조심할 데가 하나 있습니다. 압축본의 길이는 원본에 무엇이 들었는지를 조금 드러냅니다. 비밀 값과 남이 넣은 값이 한 덩이에 같이 압축되면 길이 변화만 보고 비밀을 좁혀 갈 수 있어서, 그런 통로에서는 압축을 꺼 두기도 합니다. 이렇게 값 자체가 아니라 곁가지 흔적으로 새는 것을 사이드 채널이라고 부릅니다.

압축이 값을 내는 곳

압축이 값을 내는 데는 크게 셋입니다. 보낼 때와 쌓아 둘 때와 오래 둘 때로 갈립니다.

보낼 때가 첫째입니다. 웹 서버는 응답 본문을 줄여 보냅니다. 줄어든 만큼 대역폭을 덜 씁니다. 응답도 빨리 도착합니다.

브라우저는 Accept-Encoding 헤더로 자기가 풀 수 있는 방식을 알립니다. 서버는 그중 하나를 골라 줄입니다. 무엇으로 줄였는지는 Content-Encoding 헤더에 적습니다. 브라우저는 그 헤더를 보고 풉니다.

주고받는 차례를 그림으로 보면 이렇습니다.

sequenceDiagram
    participant 브라우저
    participant 서버 as 웹 서버
    브라우저->>서버: 풀 수 있는 방식을 알린다
    서버->>브라우저: 골라서 줄인 본문 · 무엇으로 줄였는지
    Note over 브라우저: 받아서 푼다
    Note over 브라우저,서버: 고를 것이 없으면 안 줄이고 그대로 보낸다

쌓아 둘 때가 둘째입니다. 로그나 시계열처럼 같은 모양이 끝없이 쌓이는 데이터는 되풀이가 많아 잘 줄어듭니다. 열 지향 저장이 압축과 특히 잘 맞는 것도 같은 이유입니다. 한 열에는 같은 종류의 값만 모여 있어서 값이 크게 치우칩니다.

오래 둘 때가 셋째입니다. 백업이나 오래된 기록은 거의 읽히지 않으므로, 줄이는 데 시간이 많이 들어도 작게 만드는 쪽이 이깁니다.

관련 항목

압축이 갈리는 방식

무손실 압축 · 손실 압축 · 사전 압축 · 엔트로피 부호화 · 런 렝스 인코딩 · 델타 인코딩 · 블록 압축 · 스트림 압축

압축을 실제로 해내는 알고리즘

허프만 부호 · 산술 부호화 · LZ77 · LZ78 · LZW · Deflate · 버로우즈-휠러 변환 · 양자화

압축본을 담아 주고받는 파일 포맷

gzip · ZIP · tar · zlib · Brotli · Zstandard · LZ4 · Snappy · Parquet

압축이 들어가 있는 미디어 포맷

JPEG · PNG · WebP · MP3 · FLAC · H.264 · GIF

압축의 효과를 재는 잣대

압축률 · 엔트로피 · 정보 이론 · 비트 · 처리량 · 지연 시간

압축과 나란히 놓이는 데이터 변환

직렬화 · 역직렬화 · 암호화 · 인코딩 · 해싱 · Base64 · 체크섬 · 압축 해제

압축을 켜고 끄는 HTTP 헤더와 서버

Content-Encoding · Accept-Encoding · Transfer-Encoding · 웹 서버 · 리버스 프록시 · CDN

압축이 저장량을 줄여 주는 데이터 종류

시계열 · 로그 · 열 지향 저장 · 백업 · 콜드 스토리지 · 아카이빙 · 보존 기간

압축이 안 통하는 데이터와 그 까닭

난수 · 암호문 · 비둘기집 원리 · 헤더 · 사이드 채널

다른 이름: 데이터 압축 · data compression · compression