파일 업로드
  1. base64hub
  2. Base64 가이드
  3. Base64와 다른 인코딩
해설

Base64와 Hex, Base32, Base58, Base85 비교

바이너리를 텍스트로 바꾸는 인코딩은 모두 크기, 가독성, 쓸 수 있는 문자 집합 사이에서 균형을 잡습니다. 같은 입력을 아홉 가지 인코딩으로 바꾸고, 각각의 크기 오버헤드와 문자 집합을 함께 정리했습니다.

비교표

이 인코딩들은 모두 임의의 바이트를 손실 없이 출력 가능한 텍스트로 바꾸고 되돌립니다. 몇 개의 문자를 쓰는지, 어떤 문자를 쓰는지, 출력이 얼마나 커지는지가 다릅니다.

인코딩알파벳출력 크기대소문자 구분URL-safe대표 용도
Hex (Base16)0-9 a-f200%아니요예해시, MAC 주소, 바이트 덤프, 색상 코드
Base32 (RFC 4648)A-Z 2-7, 패딩 =160%아니요 (명세상 대문자)예, = 없이TOTP 시크릿, Tor onion 주소, DNSSEC (base32hex)
Base58 (Bitcoin)0 O I l을 뺀 1-9 A-Z a-z약 137%예예비트코인 주소, IPFS CIDv0 (Qm…), 솔라나 키
Base64A-Z a-z 0-9 + /, 패딩 =133%예아니요이메일 첨부 파일, data URI, PEM, JSON 페이로드
Base64URLA-Z a-z 0-9 - _133%예예JWT, URL 토큰, 파일 이름
Ascii85!부터 u까지, 그리고 z125%예아니요PDF와 PostScript 스트림
Z85.-:+=^!/*?&<>()[]{}@%$# 포함 85자125%예아니요ZeroMQ CURVE 키
Base45 (RFC 9285)0-9 A-Z, 공백, $%*+-./:150%아니요 (대문자만)아니요QR 코드, EU 디지털 코로나19 증명서

“출력 크기"는 큰 입력에서 입력 길이에 대한 인코딩 길이의 비율입니다. Base64는 3바이트당 4자, Base32는 5바이트당 8자, Base85는 4바이트당 5자, Base45는 2바이트당 3자가 필요합니다. Base58에는 고정된 블록 크기가 없으므로 비율은 log(256) / log(58), 약 1.366입니다. 특정 입력에 대한 정확한 숫자는 크기 계산기가 한 번에 모두 보여 줍니다.

같은 입력을 각 인코딩으로

12바이트 ASCII 문자열 Hello world!를 아홉 가지 방식으로 인코딩했습니다. 12바이트는 3과 4의 배수이므로 Base64와 Base85 변형에는 패딩이 필요 없습니다.

인코딩출력길이
Hex48656c6c6f20776f726c642124
Base32JBSWY3DPEB3W64TMMQQQ====24 (패딩 없이 20)
Base32hex91IMOR3F41RMUSJCCGGG====24
Base58 (비트코인 알파벳)2NEpo7TZRhna7vSvL17
Base64SGVsbG8gd29ybGQh16
Base45%69 VD82EK4F.KESTC18
Ascii8587cURD]j7BEbo8015
Z85nm=QNzY<mxA+]nf15
Base85 (RFC 1924 / git)NM&qnZy<MXa%^NF15

여기서는 출력에 +나 /가 없으므로 Base64URL이 Base64와 똑같습니다. 차이는 특정 바이트에서만 드러납니다. fb ef be는 Base64로 ++++, Base64URL로 ----입니다.

Python 표준 라이브러리가 표의 대부분을 다룹니다. z85encode는 Python 3.13에서 추가되었습니다.

import base64

data = b"Hello world!"
print(data.hex())                           # 48656c6c6f20776f726c6421
print(base64.b32encode(data).decode())      # JBSWY3DPEB3W64TMMQQQ====
print(base64.b64encode(data).decode())      # SGVsbG8gd29ybGQh
print(base64.a85encode(data).decode())      # 87cURD]j7BEbo80
print(base64.b85encode(data).decode())      # NM&qnZy<MXa%^NF
print(base64.z85encode(data).decode())      # nm=QNzY<mxA+]nf

Base58은 표준 라이브러리에 없지만, 큰 정수 하나의 진법 변환일 뿐이므로 몇 줄이면 됩니다.

B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

def b58encode(data: bytes) -> str:
    n = int.from_bytes(data, "big")
    out = ""
    while n:
        n, r = divmod(n, 58)
        out = B58[r] + out
    zeros = len(data) - len(data.lstrip(b"\0"))
    return "1" * zeros + out

print(b58encode(b"Hello world!"))   # 2NEpo7TZRhna7vSvL
print(b58encode(b"\0\0\x01"))       # 112

큰 정수는 앞쪽의 0을 기억할 방법이 없으므로, 앞쪽 0 바이트는 각각 문자 그대로의 1이 됩니다. 그래서 버전 바이트가 0인 레거시 비트코인 주소는 모두 1로 시작합니다.

각 인코딩이 쓰이는 곳

Hex

Hex는 바이트당 두 글자를 써서 여기서 가장 크지만 가장 읽기 쉽습니다. 각 문자가 정확히 4비트에 대응하므로 오프셋 n의 바이트는 항상 2n번째와 2n+1번째 문자에 있습니다. 해시, 체크섬, 디버그 덤프가 거의 언제나 hex인 이유가 이 성질입니다. 디코더는 DEADBEEF와 deadbeef를 모두 받습니다. 변환은 Base64를 hex로와 hex를 Base64로에서 할 수 있습니다.

Base32

Base32는 대문자와 숫자 2부터 7만 씁니다. 대소문자를 구분하지 않는 시스템(DNS 이름, 일부 파일 시스템, 휴대폰에 코드를 입력하는 사람)을 통과하고, O, I, B와 비슷해 보이는 0, 1, 8을 피합니다. 인증 앱은 TOTP 시크릿을 Base32로 저장하고, Tor v3 onion 주소는 소문자 Base32 56자입니다. RFC 4648은 원래 바이트의 정렬 순서를 유지하는 base32hex(0-9 A-V)도 정의합니다. Base32 인코더로 시험해 보세요.

Base58

Base58은 비트코인에서 시작되었습니다. 헷갈리기 쉬운 0, O, I, l과, 더블클릭 선택과 URL을 깨뜨리는 +와 /를 뺍니다. 비트코인 주소는 그 위에 버전 바이트와 4바이트 체크섬을 더하는데, 이 형식을 Base58Check라고 합니다. IPFS CIDv0 해시도 Base58이며, multihash 바이트 12 20으로 시작하므로 항상 Qm으로 시작합니다. 입력 전체를 하나의 수로 다루기 때문에 변환 시간이 길이의 제곱으로 늘어나므로, Base58은 키나 해시 같은 짧은 값에 쓰입니다. Base58 인코더로 시험해 보세요.

Ascii85, Z85, git의 Base85

Base85 계열은 4바이트를 5자에 담으며, 널리 지원되는 인코딩 중 가장 밀도가 높습니다. 문제는 알파벳입니다. 출력 가능한 문자 85개를 쓰다 보니 변형에 따라 따옴표, 백슬래시, <, >, &가 들어갑니다.

  • Ascii85(Adobe)는 !부터 u까지를 쓰고, 출력을 <~와 ~>로 감싸며, 0 바이트 네 개를 z 하나로 씁니다. PostScript와 PDF(ASCII85Decode 필터)가 이를 씁니다.
  • Z85(ZeroMQ)는 따옴표나 백슬래시 없이 C와 JSON 문자열 리터럴 안에서 안전한 문자를 고릅니다. 입력 길이가 4의 배수여야 합니다. 32바이트 CURVE 키는 40자입니다.
  • RFC 1924 Base85는 IPv6 주소를 위한 만우절 제안이었습니다. 그 알파벳은 git 바이너리 패치와 Python의 b85encode에 살아 있습니다.

Base85 도구가 세 가지를 모두 다루며, Z85는 별도 페이지가 있습니다.

Base45

Base45는 QR 코드를 위해 존재합니다. QR 코드의 영숫자 모드는 정확히 45개 문자(숫자, 대문자, 공백, $%*+-./:)를 문자당 5.5비트로 저장하는데, 바이트 모드는 문자당 8비트입니다. Base45는 2바이트를 이런 문자 3개로 바꾸므로 QR 코드 안에서 약 3%의 비용이 듭니다. Base64 텍스트는 바이트 모드를 써야 해서 33%가 듭니다. EU 디지털 코로나19 증명서가 Base45를 쓴 이유가 바로 이것입니다. Base45 인코더로 시험해 보세요.

고르는 방법

받는 쪽이 기대하는 인코딩을 고르세요. JWT 라이브러리는 Base64URL을, 인증 앱은 Base32를, 비트코인 지갑은 Base58Check를 원합니다. 양쪽을 모두 통제한다면 다음 경험칙이 통합니다.

  • JSON, XML, 이메일, HTTP 본문 안의 바이트: Base64. 모든 언어에 빠른 내장 구현이 있고, 33% 오버헤드는 감수할 만합니다.
  • URL, 쿠키, 파일 이름 안: 패딩 없는 Base64URL.
  • 사람이 값을 읽거나 입력하거나 비교할 때: 짧은 해시는 hex, 대소문자가 사라질 수 있다면 Base32, 짧아 보이면서 닮은 문자가 없어야 한다면 Base58.
  • 바이트 하나하나가 중요하고 디코더를 직접 통제할 때: Z85나 Ascii85.
  • QR 코드: Base45.

균형은 언제나 같습니다. 알파벳이 클수록 출력은 짧아지지만, 어딘가에서 이스케이프가 필요한 문자도 늘어납니다. Base64가 왜 3분의 1만큼 커지는지는 Base64 크기 오버헤드를, URL-safe 변형은 Base64와 Base64URL을 참고하세요.

자주 묻는 질문

바이너리-텍스트 인코딩 중 가장 압축적인 것은 무엇인가요?

흔한 것 중에서는 오버헤드 25%인 Base85(Ascii85와 Z85), 그다음이 33%인 Base64입니다. basE91은 조금 더 낫지만 지원하는 곳이 드뭅니다. 필요하다면 Base91 도구를 보세요.

비트코인은 왜 Base64 대신 Base58을 쓰나요?

Base58은 0, O, I, l, +, /를 빼므로 주소를 소리 내어 읽기, 손으로 옮겨 적기, 텍스트 칸에서 더블클릭하기가 쉽습니다. 대가는 느린 큰 수 변환인데, 25바이트 주소에서는 문제가 되지 않습니다.

Base32와 Base58에서 대소문자가 중요한가요?

RFC 4648 알파벳은 대문자이며, 엄격한 디코더는 소문자를 거부합니다. 많은 디코더는 둘 다 받으며, Python은 casefold=True가 필요합니다. Base58은 대소문자를 구분하므로 a와 A는 서로 다른 숫자입니다. Hex는 사실상 모든 디코더에서 대소문자를 구분하지 않습니다.

Base64를 Base58이나 Base32로 바로 변환할 수 있나요?

네, 바이트로 디코딩한 뒤 다시 인코딩하면 됩니다. Base64를 Base58로와 Base64를 Base32로 변환기는 두 단계를 모두 브라우저에서 로컬로 처리합니다.

계속 읽기