본문으로 건너뛰기
yutils

Base64 인코딩 — 원리·변형·언제 쓰는가

Base64 는 암호화가 아니라 바이너리→텍스트 인코딩. 패딩·URL-safe 변형·자주 빠지는 함정·크기 오버헤드를 정리.

약 7분 읽기

Base64 는 바이너리 데이터를 64 글자의 ASCII 텍스트로 표현하는 인코딩 방식이다. 이메일 첨부, JWT 의 헤더·페이로드, 데이터 URI, 환경 변수에 섞인 인증서 등 "바이너리를 텍스트만 허용하는 곳에 끼워 넣어야 할 때" 에 거의 모든 곳에서 쓰인다. 이름이 "암호화" 처럼 느껴져 오해가 잦은데, Base64 는 전혀 보안 기능이 아니다. 이 가이드는 원리, 변형, 오버헤드, 자주 빠지는 함정을 정리한다.

왜 인코딩이 필요한가

많은 프로토콜과 포맷이 텍스트만 안전하게 다룬다. SMTP 는 7-bit ASCII 만 보장하던 시절의 유산이 남아 있고, JSON 은 임의 바이너리를 그대로 담지 못한다. 이미지나 키 같은 바이너리를 이런 채널에 실으려면 텍스트로 바꿔야 한다.

Base64 는 RFC 4648 이 정한 표준이다. 8 비트 바이트 3 개(24 비트)를 6 비트 단위로 자르고, 각 6 비트를 64 글자 알파벳에 매핑한다 — 그래서 이름이 Base64. 입력 3 바이트가 출력 4 글자가 되므로 크기는 약 4/3 배 (33% 증가) 한다.

알파벳과 패딩

표준 알파벳은 다음 64 글자다.

A-Z   a-z   0-9   +   /

입력 길이가 3 의 배수가 아니면 출력 끝에 = 패딩이 붙어 4 글자 단위를 맞춘다.

입력 바이트 수출력 글자 수패딩
34없음
24= 하나
14== 두 개

예를 들어 ASCII "Hi" (2 바이트) → "SGk=". Base64 인코딩 / 디코딩 에서 직접 입력해 보면 출력이 그렇게 나온다.

URL-safe 변형 (Base64URL)

표준 알파벳의 +/ 는 URL 에서 다른 의미가 있다 (+ 는 공백, / 는 경로 구분자). 그래서 RFC 4648 §5 가 URL-safe 변형을 정의했다.

  • +-
  • /_
  • 패딩 = 은 흔히 생략 (URL 에 그대로 박을 수 있도록)

JWT 의 세 조각이 정확히 이 변형이다 — +/ 가 나오면 표준 Base64. -_, 또는 = 없는 4 의 배수가 아닌 길이가 나오면 Base64URL. Base64 인코딩 / 디코딩 는 두 변형을 동시에 받아 자동 감지한다.

크기 오버헤드 — 실제 영향

4/3 배 증가는 작은 수처럼 보이지만, 메가바이트급 이미지를 데이터 URI 로 박으면 페이지 크기가 33% 늘고 gzip 압축률도 떨어진다 (이미 압축된 바이너리를 Base64 로 인코드한 결과는 거의 압축되지 않는다).

그래서 다음 규칙이 있다.

  • 작은 아이콘 (~5 KB 미만) 은 data URI 로 인라인 — HTTP 요청 줄이는 이점이 오버헤드를 상쇄.
  • 큰 이미지 는 그대로 파일/CDN. Base64 로 박을 이유 없음.
  • JSON/API 에 작은 바이너리 (서명·해시 등) 를 박을 때는 hex 보다 Base64 가 33% 짧다. Hex 인코딩 / 디코딩 와 비교해 보면 체감.

흔한 사용처

1. 데이터 URI

data:image/png;base64,iVBORw0KGgo... 형식. CSS 의 background-image, HTML 의 <img src>, 이메일 첨부에서 자주 본다. 이미지 → Base64 (Data URI) 가 이미지 파일을 직접 data URI 로 변환.

2. JWT

앞서 봤듯 JWT 의 header·payload 는 Base64URL 로 인코드된 JSON 이다. JWT 디코더 가 자동으로 풀어 준다.

3. Basic 인증 헤더

Authorization: Basic dXNlcjpwYXNz

"user:pass" 를 Base64 한 결과. 암호화가 아니므로 HTTPS 필수 — 평문이나 다름없다.

4. 환경 변수에 키 박기

TLS private key, GCP service account JSON 등 줄바꿈이 섞인 멀티라인 키 를 환경 변수 한 줄에 박을 때 Base64 로 감싼다. CI/CD 시크릿 매니저에 흔한 패턴.

5. URL 토큰

세션 토큰·invite link·CSRF 토큰을 URL 에 박을 때 Base64URL 을 쓴다. URL 인코딩 / 디코딩 와 헷갈리기 쉬운데, 두 인코딩은 목적이 다르다 — URL 인코딩은 특수 문자 를 %XX 로, Base64URL 은 임의 바이너리 를 텍스트로.

자주 빠지는 함정

1. 암호화가 아니다

Base64 로 박힌 문자열은 누구나 한 번에 풀 수 있다. "Base64 로 숨겨뒀어" 는 보안이 아니라 가독성 회피일 뿐. 진짜 보안이 필요하면 AES/암호화 후 Base64.

2. 줄바꿈 / 공백

RFC 4648 표준은 줄바꿈 없는 단일 문자열이다. 하지만 PEM (TLS 인증서) 같은 일부 포맷은 76 글자마다 줄바꿈을 넣는다. 디코드 전에 줄바꿈/공백을 제거하지 않으면 라이브러리에 따라 에러 또는 잘못된 결과.

3. 패딩 누락

Base64URL 에서 패딩을 생략한 토큰을 표준 Base64 디코더에 그대로 넘기면 "invalid length" 에러. 길이가 4 의 배수가 되도록 = 를 다시 붙이거나, 패딩 허용 옵션이 있는 디코더를 쓴다.

4. 문자 인코딩 혼동

한국어 텍스트를 Base64 하기 전에 반드시 UTF-8 바이트로 직렬화. JavaScript 의 btoa() 는 Latin-1 만 받아서 비-ASCII 입력에 에러를 던진다. 표준 패턴은 btoa(unescape(encodeURIComponent(text))) 또는 new TextEncoder().encode(text) 를 거친 뒤 Base64.

5. base64 ≠ MIME base64

RFC 2045 (MIME) 은 줄바꿈을 의무화한 또 다른 변형이다. Python 의 base64.b64encode 는 RFC 4648 이지만 base64.encodebytes 는 MIME — 결과가 다르다. 어느 변형이 필요한지 명시해 두자.

도구로 직접 보기

요약

  • Base64 = 3 바이트 → 4 글자, 33% 크기 증가.
  • 표준 알파벳 + 패딩 =. URL-safe 변형은 +/ 대신 -_ 와 패딩 생략.
  • 인코딩이지 암호화가 아니다 — Basic 인증·JWT payload 모두 평문.
  • JS 환경에서 비-ASCII 텍스트는 UTF-8 직렬화를 먼저 — btoa() 만 쓰면 깨진다.
  • 큰 바이너리는 Base64 로 박지 말고 파일/CDN 으로. 4/3 배 오버헤드와 압축 손실이 누적된다.
가이드 목록으로