Base64 인코더 / 디코더

Base64는 왜 정확히 33% 부풀어오르는가 (수학적 설명)

Base64는 8비트 바이트 스트림(이진 데이터)을 받아 64개의 문자(A–Z, a–z, 0–9, 그리고 보통 +와 /)만 써서 텍스트로 다시 인코딩합니다. 64라는 숫자 선택은 수학적으로 의도된 것입니다 — 64는 정확히 2^6이라 출력 문자 하나가 6비트 입력을 표현합니다. 인코더는 소스 비트스트림을 6비트 단위로 다시 묶고, 각 묶음을 64자 알파벳 중 하나에 매핑합니다. 크기 오버헤드는 산수에서 자연스럽게 나옵니다. 입력 3바이트는 24비트이고, 24는 6의 배수이므로 정확히 4개의 6비트 그룹으로 나뉩니다. 즉 입력 3바이트 → 출력 4문자. 비율은 4/3, 약 +33.33% 증가. 입력 길이가 3의 배수가 아니면 인코더가 마지막 그룹을 0비트로 패딩하고 = 문자를 한두 개 붙여 수신측에 마지막 quartet에 실제로 몇 바이트가 들어있는지 알려줍니다. =가 1개면 2바이트, ==면 1바이트, =가 없으면 깔끔한 3바이트 그룹입니다. 이 설명에서 몇 가지 관찰이 따라옵니다 — base64 문자열의 길이는 항상 4의 배수입니다. 패딩은 많은 구현에서 사실상 옵션입니다(디코더가 길이의 mod 4로 추론 가능). 그래서 JWT나 현대의 URL-safe 컨텍스트에서 "패딩 제거된" Base64를 자주 봅니다. 엄격한 RFC 4648 준수는 패딩이 있어야 하고, 많은 현실 소프트웨어는 입력엔 관대하지만 출력엔 엄격합니다. 33%라는 숫자는 점근적 수치입니다. 매우 작은 입력에서는 패딩 라운딩 때문에 오버헤드가 더 큽니다 — 1바이트를 인코딩하면 4문자가 나오므로 그 바이트만 보면 300% 부풀이입니다. 수백 바이트 이상이 되면 오버헤드가 33.33%에 점근합니다. 출력 가능한 ASCII 안에 머무르면서 이보다 더 잘하는 스킴은 없습니다 — Base85(Adobe PDF에서 사용)는 25%까지 줄이지만 백슬래시·따옴표 같은 문자를 써서 JSON·URL에선 깨집니다. Base64가 실용적인 sweet spot입니다.
Input bytes:    M       a       n
                |_______|_______|_______|
Binary:        01001101 01100001 01101110
Re-grouped:    010011 010110 000101 101110
Lookup A-Z..:  T      W       F      u
Output:        "TWFu"

// 1 byte: pads with ==
btoa("M");   // "TQ=="

// 2 bytes: pads with =
btoa("Ma");  // "TWE="

// 3 bytes: clean
btoa("Man"); // "TWFu"

// length always % 4 === 0
btoa("hello world").length;  // 16
btoa("a").length;            // 4 (was 1 byte → 4 chars)

URL-safe Base64 vs 표준 Base64, 그리고 btoa 함정

RFC 4648은 두 가지 알파벳을 정의합니다. 표준 Base64는 마지막 두 문자로 +와 /를 쓰고 패딩으로 =를 씁니다. URL-safe Base64("Base64URL")는 +를 -로, /를 _로 바꾸고, 보통 끝의 = 패딩도 제거합니다. 이유는 단순합니다 — +, /, = 모두 URL, HTTP form body, 일부 파일시스템에서 예약된 의미가 있습니다. 표준 Base64 문자열을 escaping 없이 query parameter에 넣으면 첫 + 자리에서 깨지고, 서버는 그것을 literal space로 디코딩해버립니다. JWT가 헤더·페이로드·시그니처 모두 Base64URL만 쓰는 이유입니다. 토큰은 HTTP Authorization 헤더, 쿠키, URL을 타고 다녀야 하는데 모두 표준 알파벳과 궁합이 나쁩니다. 잘못된 방언을 쓰면 수신측에서 parse 오류가 나거나, 더 나쁘게는 어떤 코드 경로에서만 +가 space로 정규화되는 식으로 "거의 작동"하는 토큰이 만들어집니다. 브라우저의 btoa / atob는 URL-safe Base64보다 오래되었고 표준 알파벳만 만듭니다. 더 나쁘게도 Latin-1 입력만 받습니다 — U+00FF를 넘는 문자(거의 모든 CJK, 이모지)를 가진 문자열을 넘기면 InvalidCharacterError가 던져집니다. 2026년의 올바른 경로는 (1) TextEncoder로 문자열을 UTF-8 바이트로 인코딩, (2) 바이트를 Base64 인코딩, (3) 필요하면 URL-safe로 변환 후 패딩 제거. 디코딩은 역순. Node.js는 더 깔끔합니다 — Buffer.from(str, 'utf-8').toString('base64url')이 패딩 없는 URL-safe Base64를 바로 만듭니다. 브라우저에서는 TC39 stage 3에 올라간 Uint8Array.prototype.toBase64() / fromBase64()(2026년 기준)가 마침내 깔끔한 내장을 제공합니다. 최소 지원 브라우저에 도달하기 전까지는 아래의 TextEncoder + btoa + replace 우회가 정석입니다. 보안 노트: Base64를 절대 보안 의미의 "암호화"로 착각하지 마세요. 암호화도 아니고, 의미 있는 난독화도 아닙니다. 누구든 5초와 임의의 도구만 있으면 바이트를 복원할 수 있습니다. 운송 용도로만 쓰세요. 실제 암호화의 대용품이 아닙니다.
// Browser: UTF-8 safe Base64URL (no padding)
function toBase64Url(str) {
  const bytes = new TextEncoder().encode(str);
  let bin = '';
  for (const b of bytes) bin += String.fromCharCode(b);
  return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

function fromBase64Url(s) {
  s = s.replace(/-/g, '+').replace(/_/g, '/');
  while (s.length % 4) s += '=';
  const bin = atob(s);
  const bytes = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
  return new TextDecoder().decode(bytes);
}

// btoa breaks on non-Latin-1
btoa('한글');                  // InvalidCharacterError
toBase64Url('한글');           // "7ZWc6riA"  (works)

// Node 16+
Buffer.from('한글', 'utf-8').toString('base64url'); // "7ZWc6riA"

Base64를 쓰지 말아야 할 세 가지 실제 사례

Base64는 좁은 적용처가 있고, 개발자들은 그 밖으로 끌고 나가서 손해를 자주 봅니다. 흔히 득보다 실이 큰 세 가지 사례 — (1) data URL로 이미지 인라이닝. 4KB 아이콘을 <img src="data:image/png;base64,...">로 넣고 싶은 유혹이 큽니다. 바이트 비용은 +33%지만 진짜 데미지는 캐시 무효화입니다. 인라이닝된 자산은 그게 들어있는 페이지 번들의 일부가 되고, 아이콘을 한 번 업데이트하면 그 자산이 들어있는 HTML 또는 CSS 전체의 캐시가 깨집니다. 게다가 브라우저는 그 아이콘을 페이지 간 공유할 수 없습니다 — 각 페이지가 인라인 바이트를 다시 다운로드합니다. HTTP/2·HTTP/3에서는 외부 파일의 요청당 오버헤드가 작아 1~2KB만 넘어도 인라이닝이 모든 지표에서 집니다. 변하지 않을 것이 보장된, 같은 critical path에 등장하는 아주 작은 SVG / data URI(CSS) 정도에서만 인라이닝하세요. (2) "공간 절약"을 위해 구조화 데이터를 Base64 blob에 인코딩. Base64는 데이터를 33% 키우지 줄이지 않습니다. JSON.stringify → Base64 → Gzip 같은 파이프라인을 하고 있다면 중간 단계를 빼세요. gzip은 raw 텍스트에서 잘 작동하고, Base64 레이어는 gzip에게 잉여 패턴을 더 줄 뿐이라 인코딩 시간과 압축률을 모두 깎습니다. Base64는 텍스트 전용 채널로 바이너리를 옮길 때 쓰는 도구입니다. 채널이 이미 텍스트라면 필요 없습니다. (3) JWT나 세션 토큰을 로그에 남는 URL에 저장. Base64URL 알파벳은 URL-safe지만 어떤 프라이버시 의미에서도 log-safe가 아닙니다. 웹 서버·프록시·CDN은 기본적으로 전체 요청 URL을 로그합니다. ?token=에 세션 토큰을 끼운 "로그아웃 링크"는 그 토큰을 access log에 영원히 흘립니다 — Base64 래퍼는 보호 기능이 없습니다. 그 값이 공개되어도 되는 것(일회용, 단기)으로 만들거나, HTTP body, HttpOnly 쿠키, 프록시가 redact 하도록 설정된 Authorization 헤더로 옮기세요. 요점: Base64는 운송 문제(transport)를 푸는 도구지 저장·보안·압축 문제를 푸는 도구가 아닙니다. 바이너리 페이로드와 텍스트 전용 채널이 만났을 때만 정확히 사용하세요.
// BAD: data URL for a logo on every page
<img src="data:image/png;base64,iVBORw0KGgoAAAA...(20KB)..." />
// → busts HTML cache on every change, no cross-page reuse

// GOOD: external file with long cache TTL
<img src="/static/logo.v3.png" />
// → CDN caches once, reused everywhere

// BAD: redundant Base64 in a compression pipeline
const blob = btoa(JSON.stringify(data));
const compressed = gzip(blob);  // gzip is now LESS effective

// GOOD: gzip the JSON directly
const compressed = gzip(JSON.stringify(data));

// BAD: token in URL → leaked to access logs
<a href="/logout?token=eyJhbGciOi...">

// GOOD: Authorization header
fetch('/logout', { method: 'POST', headers: { Authorization: 'Bearer ' + token } });
최종 수정:

도구 소개

Base64는 바이너리 데이터를 64개의 ASCII 문자로 표현하는 인코딩 방식입니다. 이메일(MIME), HTTP 헤더, JSON 페이로드, HTML/CSS의 데이터 URL 등 텍스트 전용 채널에서 바이너리 콘텐츠를 안전하게 전송할 때 사용됩니다. 약 33%의 크기 오버헤드가 발생하지만 어떤 텍스트 환경에서도 데이터가 손상되지 않습니다.

사용 방법

  1. Encode(인코딩) 또는 Decode(디코딩) 모드를 선택합니다.
  2. 평문, JSON, 임의 문자열 등 입력값을 입력란에 붙여넣습니다.
  3. 입력하는 즉시 결과가 자동 갱신되므로 별도 버튼이 필요 없습니다.
  4. 양방향 변환을 검증하려면 스왑 버튼으로 입력/출력을 뒤집어 확인합니다.
  5. 복사 버튼을 눌러 결과를 코드, 터미널, HTTP 클라이언트에 붙여넣습니다.

주요 사용 사례

  • data: URL로 작은 이미지를 CSS/HTML에 직접 삽입하여 HTTP 요청 수를 줄이기
  • HTTP Basic 인증의 Authorization 헤더에 자격 증명을 인코딩
  • JSON 페이로드에 바이너리 파일 내용을 담아 저장/전송
  • MIME 이메일 첨부 파일 인코딩 (SMTP 중계 시 손상 방지)
  • JWT 헤더/페이로드 디코딩 (Base64URL JSON)
  • 비출력 문자를 거르는 시스템에서 바이너리를 안전하게 전송

자주 묻는 질문

Q. Base64는 암호화인가요?

A. 아닙니다. 누구나 즉시 디코딩할 수 있는 인코딩일 뿐입니다. 비밀 정보를 숨길 때는 AES 같은 암호 알고리즘을 사용하세요.

Q. Base64 문자열 끝의 = 는 무엇인가요?

A. 출력 길이를 4의 배수로 맞추기 위한 패딩입니다. URL-safe 변형은 패딩을 생략하기도 하지만 표준 Base64는 유지합니다.

Q. Base64URL은 무엇이 다른가요?

A. + 를 - 로, / 를 _ 로 치환하여 URL/파일명 안에 안전하게 넣을 수 있는 변형입니다. JWT가 이 변형을 사용합니다.

Q. 데이터 크기는 얼마나 늘어나나요?

A. 3바이트가 4 ASCII 문자가 되므로 원본 대비 약 33% 증가합니다.