코드베이스 곳곳에서 Base64 문자열을 본 적이 있을 겁니다 — JWT 토큰, HTML <img> 태그, HTTP Authorization 헤더, 또는 파일 데이터를 담은 JSON 페이로드에서요. SGVsbG8sIFdvcmxkIQ==처럼 알아볼 수 없는 문자처럼 보이지만, 이것이 존재하는 이유는 명확합니다. 한 번 이해하면 Base64는 더 이상 수수께끼가 아니라 도구 상자에서 진정으로 유용한 도구가 됩니다.

Base64란 무엇인가

Base64는 이진-텍스트 인코딩 방식입니다. 그게 전부입니다. null 문자, 제어 코드, 0에서 255 사이의 임의 값을 포함할 수 있는 임의의 이진 데이터를 취해서 64개의 출력 가능한 ASCII 문자만을 사용하여 표현합니다. 결과물은 텍스트 기반 채널을 통해 손상되거나 잘못 해석되지 않고 안전하게 전달될 수 있는 문자열입니다.

공식 명세는 RFC 4648에 있으며, IETF에서 발행했습니다. 표준 Base64와 URL 안전 변형을 모두 정의합니다. 패딩 규칙이나 알파벳 선택에 대한 논쟁을 해결해야 할 때 북마크해 두면 유용합니다.

Base64 알파벳

64자 알파벳은 다음으로 구성됩니다: 대문자 A–Z (26자), 소문자 a–z (26자), 숫자 0–9 (10자), 그리고 두 기호: +/. 총 정확히 64자 — 이름의 유래입니다. = 기호는 입력 길이가 3의 배수가 아닐 때 끝에 패딩 문자로 사용됩니다.

text
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

각 문자는 정확히 6비트의 데이터를 인코딩합니다(2^6 = 64가지 가능한 값). 이것이 인코딩의 핵심 메커니즘이며, 약 33%의 크기 오버헤드가 발생하는 이유입니다.

인코딩 작동 방식

알고리즘은 3바이트 → 4개의 Base64 문자 단위로 작동합니다. 3바이트 = 24비트. 이 24비트를 4개의 6비트 그룹으로 나누면, 각 그룹이 Base64 알파벳의 한 문자에 매핑됩니다.

text
Input:  "Man"
Bytes:  M=77 (01001101)  a=97 (01100001)  n=110 (01101110)
Bits:   010011 010110 000101 101110
Index:  19     22     5      46
Output: T      W      F      u     →  "TWFu"

입력이 3으로 나누어 떨어지지 않으면 패딩이 적용됩니다. 남은 두 바이트는 세 개의 Base64 문자와 하나의 =가 됩니다. 남은 한 바이트는 두 개의 Base64 문자와 ==가 됩니다. 패딩은 디코더에게 끝에 실제로 몇 바이트가 있는지 알려줄 뿐 — 데이터 자체의 일부는 아닙니다.

크기 오버헤드: 3바이트 입력이 4문자 출력이 됩니다. 이는 크기가 33% 증가함을 의미합니다. 작은 문자열에는 무시할 수 있는 수준이지만, 큰 이진 파일 — 10MB 이미지가 약 13.3MB의 Base64가 됩니다 — 에서는 오버헤드가 중요하므로 직접 임베딩하기 전에 신중히 생각해야 합니다.

JavaScript에서 인코딩 및 디코딩

브라우저는 btoa()atob()를 전역으로 노출합니다. 간단하지만 주의할 점이 있습니다: Latin-1 문자(바이트 값 0–255)만 처리합니다. 멀티바이트 유니코드 문자열을 직접 전달하면 InvalidCharacterError가 발생합니다.

js
// Basic browser encoding/decoding
const encoded = btoa('Hello, World!');  // "SGVsbG8sIFdvcmxkIQ=="
const decoded = atob('SGVsbG8sIFdvcmxkIQ==');  // "Hello, World!"

// Safe Unicode version — encode to UTF-8 first
function encodeUnicode(str) {
  return btoa(
    encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, hex) =>
      String.fromCharCode(parseInt(hex, 16))
    )
  );
}

function decodeUnicode(str) {
  return decodeURIComponent(
    atob(str)
      .split('')
      .map(c => '%' + c.charCodeAt(0).toString(16).padStart(2, '0'))
      .join('')
  );
}

const emoji = encodeUnicode('Héllo wörld 🌍');
console.log(emoji);                  // "SMOpbGxvIHfDtnJsZCDwn4yN"
console.log(decodeUnicode(emoji));   // "Héllo wörld 🌍"

Node.js — Buffer 사용

Node.js에서 관용적인 접근 방식은 Buffer API입니다. 유니코드를 기본으로 올바르게 처리하며 위의 브라우저 해결책보다 훨씬 깔끔합니다.

js
// Encoding
const encoded = Buffer.from('Hello, World!', 'utf8').toString('base64');
console.log(encoded);  // "SGVsbG8sIFdvcmxkIQ=="

// Decoding
const decoded = Buffer.from('SGVsbG8sIFdvcmxkIQ==', 'base64').toString('utf8');
console.log(decoded);  // "Hello, World!"

// Encoding a file to Base64 (e.g. to embed in a JSON payload)
const fs = require('fs');

function fileToBase64(filePath) {
  const fileBuffer = fs.readFileSync(filePath);
  return fileBuffer.toString('base64');
}

function base64ToFile(base64String, outputPath) {
  const fileBuffer = Buffer.from(base64String, 'base64');
  fs.writeFileSync(outputPath, fileBuffer);
}

const avatarBase64 = fileToBase64('./uploads/avatar.png');
const payload = {
  userId: 'usr_8f3k2',
  avatarData: avatarBase64,
  mimeType: 'image/png'
};

Python — base64 모듈

Python 표준 라이브러리에는 base64 모듈이 포함되어 있으며, 인코딩, 디코딩, URL 안전 변형을 처리합니다. 함수는 bytes 객체와 함께 작동하므로 명시적 문자셋으로 문자열을 인코딩/디코딩해야 합니다.

python
import base64

# Encoding a string
message = "Hello, World!"
encoded = base64.b64encode(message.encode("utf-8"))
print(encoded)           # b'SGVsbG8sIFdvcmxkIQ=='
print(encoded.decode())  # 'SGVsbG8sIFdvcmxkIQ=='

# Decoding
decoded_bytes = base64.b64decode("SGVsbG8sIFdvcmxkIQ==")
print(decoded_bytes.decode("utf-8"))  # 'Hello, World!'

# Encoding a file
with open("report.pdf", "rb") as f:
    pdf_base64 = base64.b64encode(f.read()).decode("utf-8")

# Embedding it in a JSON-compatible structure
import json
payload = {
    "filename": "report.pdf",
    "content": pdf_base64,
    "encoding": "base64"
}
print(json.dumps(payload, indent=2))

URL 안전 Base64

표준 Base64는 URL에서 특수 문자인 +/를 사용합니다. 표준 Base64 문자열을 쿼리 파라미터에 넣으면 먼저 퍼센트 인코딩하지 않으면 미묘한 손상이 발생합니다. URL 안전 Base64(RFC 4648 §5에도 정의됨)는 +-로, /_로 교체하여 이를 해결합니다. URL 컨텍스트에서 패딩 = 기호는 종종 완전히 생략됩니다.

js
// Standard → URL-safe conversion in JavaScript
function toBase64Url(base64) {
  return base64
    .replace(/+/g, '-')
    .replace(///g, '_')
    .replace(/=+$/, '');  // strip padding
}

function fromBase64Url(base64url) {
  // Restore padding
  const padded = base64url + '=='.slice(0, (4 - (base64url.length % 4)) % 4);
  return padded.replace(/-/g, '+').replace(/_/g, '/');
}

const standard = btoa('some binary  data');
const urlSafe  = toBase64Url(standard);

console.log(standard);  // "c29tZSBiaW5hcnkAAGRhdGE="
console.log(urlSafe);   // "c29tZSBiaW5hcnkAAGRhdGE"  (safe for URLs)

Python에서는 base64.urlsafe_b64encode()base64.urlsafe_b64decode()를 사용하세요 — 이들은 자동으로 치환을 수행합니다.

실제 일반적인 사용 사례

  • HTML/CSS에 이미지 임베딩data: URI(예: src="data:image/png;base64,iVBORw0KGgo...")는 별도의 HTTP 요청을 제거합니다. 작은 아이콘과 스프라이트에는 좋지만, 33% 크기 오버헤드로 인해 큰 이미지에는 적합하지 않습니다.
  • JWT 토큰JSON Web Token의 헤더와 페이로드 섹션은 URL 안전 Base64로 인코딩된 JSON 객체입니다. JWT에서 볼 수 있는 세 개의 점으로 구분된 부분은 Base64Url(헤더).Base64Url(페이로드).Base64Url(서명)입니다.
  • HTTP 기본 인증Authorization: Basic ... 헤더는 username:password를 Base64로 인코딩합니다. RFC 7617에 정의되어 있습니다. 이것은 인코딩이지 암호화가 아닙니다 — 항상 HTTPS를 사용하세요.
  • JSON에 이진 데이터 임베딩 — JSON에는 이진 유형이 없으므로, API를 통해 전송되는 파일, 이미지, 인증서는 일반적으로 문자열 필드에 Base64로 인코딩됩니다. 이 워크플로우에 대해서는 파일을 Base64로이미지를 Base64로 도구를 참조하세요.
  • 이메일 첨부 파일 — 이메일의 기반 형식인 MIME은 Base64를 사용하여 이진 첨부 파일을 인코딩함으로써 텍스트 전용 메일 릴레이 서버를 통한 전송에서 살아남게 합니다. Base64가 설계된 원래 문제입니다.
  • 데이터베이스에 이진 데이터 저장 — 텍스트 컬럼이나 JSON 문서 내에 작은 블롭 (인증서, 썸네일)을 저장해야 할 때, Base64가 표준적인 선택입니다.

Base64는 암호화가 아닙니다

코드 리뷰 시 특히 사람들이 자주 혼동하는 부분입니다. Base64는 인코딩이지 암호화가 아닙니다. 인코딩은 데이터 표현을 변경하는 가역적 변환입니다. 암호화는 비밀 키를 사용하여 데이터를 뒤섞어서 키를 가진 사람만 되돌릴 수 있도록 합니다.

누구든지 몇 초 안에 Base64 문자열을 디코딩할 수 있습니다 — Base64 디코더에 붙여넣으면 끝입니다. 비밀번호, API 키, 개인 데이터 같은 민감한 값을 "숨기기" 위해 Base64를 절대 사용하지 마세요. 기밀성이 필요하다면 실제 암호화를 사용하세요(AES, RSA, 또는 브라우저에서는 Web Crypto API 같은 라이브러리, Python에서는 cryptography).

또 알아둘 점: Base64는 압축이 아닙니다. 출력은 항상 입력보다 약 3분의 1 더 큽니다. 크기를 줄여야 한다면, 먼저 압축하고(gzip, Brotli, zstd) 채널에서 텍스트가 필요하다면 압축된 바이트를 Base64 인코딩하세요.

Base64 작업을 위한 도구

일상적으로 Base64를 다룬다면, 빠른 도구가 실질적인 차이를 만듭니다. Base64 인코더로 텍스트 문자열을 인코딩하고, Base64 디코더로 Base64 값을 디코딩하고 검사하고, 파일을 Base64로로 API 페이로드나 저장을 위해 이진 파일을 변환하고, 이미지를 Base64로로 HTML이나 CSS에 이미지를 직접 임베딩하기 위한 데이터 URI를 생성하세요.

마무리

Base64는 하나의 특정 문제를 해결하기 위해 존재합니다: 텍스트 전용 채널을 통해 손상 없이 이진 데이터를 전달하는 것입니다. 64자(A–Z, a–z, 0–9, +, /)의 알파벳을 사용하여 3바이트 입력을 4개의 출력 가능한 ASCII 문자에 매핑함으로써 이를 수행합니다. 결과는 이메일, HTTP 헤더, JSON 필드, URL(URL 안전 변형 사용)에 안전합니다. 절충점은 고정된 33% 크기 증가입니다. 암호화도 아니고 압축도 아닙니다 — 투명하고 가역적인 인코딩입니다. 이것을 이해하면, 언제 사용해야 하는지와 다른 도구가 더 적합한 경우를 즉시 알 수 있게 됩니다.