JavaScript Base64 인코딩 가이드:btoa() vs Buffer 완전 정복
무료 Base64 인코더을 브라우저에서 직접 사용하세요 — 설치 불필요.
Base64 인코더 온라인으로 사용하기 →CSS data URI에 이미지를 삽입하거나, HTTP Authorization 헤더에 자격 증명을 전달하거나, 바이너리 인증서를 환경 변수에 저장할 때, 브라우저와 Node.js 모두에서 JavaScript 데이터를 안정적으로 Base64 인코딩해야 합니다. JavaScript는 두 가지 서로 다른 내장 API를 제공합니다:btoa()는 브라우저 환경용(Node.js 16+에서도 사용 가능),Buffer.from()은 Node.js용입니다 — 각각 Unicode, 바이너리 데이터, URL 안전성에 대해 서로 다른 제약이 있습니다. 코드 작성 없이 빠르게 인코딩하려면, ToolDeck's Base64 Encoder 가 브라우저에서 즉시 처리합니다. 이 가이드는 두 환경의 프로덕션 예제를 모두 다룹니다:Unicode 처리, URL 안전 변형, 파일 및 API 응답 인코딩, CLI 사용법, 그리고 실제 코드베이스에서 반복적으로 발생하는 4가지 실수입니다.
- ✓btoa()는 브라우저 내장 함수이며 Node.js 16+에서도 전역으로 사용 가능하지만, Latin-1(코드 포인트 0–255)만 허용합니다 — Unicode 입력은 DOMException을 발생시킵니다
- ✓Buffer.from(text, "utf8").toString("base64")는 Node.js의 동등한 메서드로, 추가 처리 없이 Unicode를 네이티브로 처리합니다
- ✓URL 안전 Base64는 +를 -로, /를 _로 대체하고 = 패딩을 제거합니다 — Node.js 18+에서 Buffer.from().toString("base64url")로 한 줄에 처리할 수 있습니다
- ✓바이너리 데이터(ArrayBuffer, Uint8Array, 파일)에는 Node.js에서 Buffer를, 브라우저에서는 arrayBuffer() + Uint8Array 방식을 사용하세요 — response.text()는 절대 사용하지 마세요
- ✓Uint8Array.prototype.toBase64()(TC39 Stage 3)는 이미 Node.js 22+와 Chrome 130+에서 사용 가능하며 두 환경을 통합할 예정입니다
Base64 인코딩이란?
Base64는 임의의 바이너리 데이터를 64개의 인쇄 가능한 ASCII 문자로 구성된 문자열로 변환합니다: A–Z, a–z, 0–9, +, 그리고 /. 입력의 3바이트마다 정확히 4개의 Base64 문자로 매핑됩니다. 입력 길이가 3의 배수가 아닌 경우, 1개 또는 2개의 = 패딩 문자가 추가됩니다. 인코딩된 출력은 원본보다 약 33% 더 큽니다.
Base64는 암호화가 아닙니다 — 기밀성을 제공하지 않습니다. 인코딩된 문자열을 가진 누구든 함수 호출 한 번으로 디코딩할 수 있습니다. 그 목적은 전송 안전성입니다:많은 프로토콜과 저장 형식은 7비트 ASCII 텍스트를 위해 설계되어 임의의 바이너리 바이트를 처리할 수 없습니다. Base64가 그 간격을 메웁니다. 일반적인 JavaScript 사용 사례로는 자산 인라인용 data URI, HTTP Basic Auth 헤더, JWT 토큰 세그먼트, 이메일 MIME 첨부 파일, JSON API의 바이너리 blob 저장 등이 있습니다.
deploy-bot:sk-prod-a7f2c91e4b3d8
ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
btoa() — 브라우저 내장 인코딩 함수
btoa()(binary-to-ASCII)는 IE10부터 브라우저에서 사용 가능하며, WinterCG 호환성 이니셔티브의 일환으로 Node.js 16.0에서 전역 함수가 되었습니다. Deno, Bun, Cloudflare Workers에서도 네이티브로 작동합니다. 임포트가 필요 없습니다.
이 함수는 문자열 인수를 하나 받아 Base64 인코딩된 형식을 반환합니다. 대칭적인 역함수 atob()(ASCII-to-binary)로 디코딩합니다. 둘 다 동기적으로 실행되며 입력 크기에 대해 일정한 메모리를 사용합니다.
최소 동작 예제
// Encoding an API credential pair for an HTTP Basic Auth header
const serviceId = 'deploy-bot'
const apiKey = 'sk-prod-a7f2c91e4b3d8'
const credential = btoa(`${serviceId}:${apiKey}`)
// → 'ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg='
const headers = new Headers({
Authorization: `Basic ${credential}`,
'Content-Type': 'application/json',
})
console.log(headers.get('Authorization'))
// Basic ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=atob()로 디코딩
// Round-trip: encode, transmit, decode const payload = 'service:payments region:eu-west-1 env:production' const encoded = btoa(payload) const decoded = atob(encoded) console.log(encoded) // c2VydmljZTpwYXltZW50cyByZWdpb246ZXUtd2VzdC0xIGVudjpwcm9kdWN0aW9u console.log(decoded === payload) // true
btoa()와 atob()는 WinterCG Minimum Common API 의 일부입니다 — 비브라우저 런타임에서 Fetch, URL, crypto를 관리하는 동일한 사양입니다. Node.js 16+, Bun, Deno, Cloudflare Workers에서 동일하게 동작합니다.Unicode 및 비 ASCII 문자 처리
btoa()의 가장 일반적인 함정은 엄격한 Latin-1 경계입니다. U+00FF를 초과하는 코드 포인트를 가진 모든 문자는 즉시 예외를 발생시킵니다:
btoa('Müller & Søren') // ❌ Uncaught DOMException: String contains an invalid character
btoa('résumé') // ❌ 'é' is U+00E9 = 233 — within Latin-1, this one actually works
btoa('田中太郎') // ❌ Throws — all CJK characters are above U+00FF올바른 방법은 먼저 문자열을 UTF-8 바이트로 인코딩한 다음 해당 바이트를 Base64 인코딩하는 것입니다. JavaScript는 바로 이 목적을 위해 TextEncoder를 제공합니다:
TextEncoder 방식 — 모든 Unicode 입력에 안전
// Utility functions for Unicode-safe Base64
function toBase64(text: string): string {
const bytes = new TextEncoder().encode(text)
const chars = Array.from(bytes, byte => String.fromCharCode(byte))
return btoa(chars.join(''))
}
function fromBase64(encoded: string): string {
const binary = atob(encoded)
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0))
return new TextDecoder().decode(bytes)
}
// Works with any language or script
const orderNote = '확인됨:김민준 — 서울 창고, 수량:250'
const encoded = toBase64(orderNote)
const decoded = fromBase64(encoded)
console.log(encoded)
// 7ZmU7J2067KE7J20OuqwgO2Vmesp7J207IaM7J2067KE64qU7IiY6rCA7IqkOiAyNTA=
console.log(decoded === orderNote) // trueBuffer.from(text, 'utf8').toString('base64')를 사용하세요. Unicode를 네이티브로 처리하며 대용량 문자열에서 더 빠릅니다.Node.js의 Buffer.from() — 예제를 포함한 완전 가이드
Node.js에서 Buffer는 인코딩 변환을 포함한 모든 바이너리 데이터 작업의 관용적인 API입니다. TextEncoder보다 수년 앞서 등장했으며 서버 사이드 코드의 선호 수단으로 남아 있습니다. btoa()에 비한 주요 장점:네이티브 UTF-8 지원, 바이너리 데이터 처리, Node.js 18부터 사용 가능한 'base64url' 인코딩 단축키.
기본 텍스트 인코딩 및 디코딩
// Encoding a server configuration object for storage in an env variable
const dbConfig = JSON.stringify({
host: 'db-primary.internal',
port: 5432,
database: 'analytics_prod',
maxConnections: 100,
ssl: { rejectUnauthorized: true },
})
const encoded = Buffer.from(dbConfig, 'utf8').toString('base64')
console.log(encoded)
// eyJob3N0IjoiZGItcHJpbWFyeS5pbnRlcm5hbCIsInBvcnQiOjU0MzIsImRhdGFiYXNlIjoiYW5h...
// Decoding back
const decoded = Buffer.from(encoded, 'base64').toString('utf8')
const config = JSON.parse(decoded)
console.log(config.host) // db-primary.internal
console.log(config.maxConnections) // 100디스크에서 바이너리 파일 인코딩
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
// Read a TLS certificate and encode it for embedding in a config file
const certPem = readFileSync(join(process.cwd(), 'ssl', 'server.crt'))
const certBase64 = certPem.toString('base64')
// Store as a single-line string — suitable for env vars or JSON configs
writeFileSync('./dist/cert.b64', certBase64, 'utf8')
console.log(`Certificate encoded: ${certBase64.length} characters`)
// Certificate encoded: 2856 characters
// Restore the binary cert from the encoded value
const restored = Buffer.from(certBase64, 'base64')
console.log(restored.equals(certPem)) // true오류 처리를 포함한 비동기 파일 인코딩
import { readFile } from 'node:fs/promises'
async function encodeFileToBase64(filePath: string): Promise<string> {
try {
const buffer = await readFile(filePath)
return buffer.toString('base64')
} catch (err) {
const code = (err as NodeJS.ErrnoException).code
if (code === 'ENOENT') throw new Error(`File not found: ${filePath}`)
if (code === 'EACCES') throw new Error(`Permission denied: ${filePath}`)
throw err
}
}
// Encode a PDF for an email attachment payload
const reportBase64 = await encodeFileToBase64('./reports/q1-financials.pdf')
const emailPayload = {
to: 'finance-team@company.internal',
subject: 'Q1 Financial Report',
attachments: [{
filename: 'q1-financials.pdf',
content: reportBase64,
encoding: 'base64',
contentType: 'application/pdf',
}],
}
console.log(`Attachment: ${reportBase64.length} chars`)JavaScript Base64 함수 — 매개변수 참조
Python의 base64 모듈과 달리, JavaScript에는 단일 통합 Base64 함수가 없습니다. 사용하는 API는 대상 환경에 따라 다릅니다. 모든 네이티브 방식의 완전한 참조가 여기 있습니다:
| 함수 | 입력 타입 | Unicode | URL 안전 | 사용 가능 환경 |
|---|---|---|---|---|
| btoa(string) | string (Latin-1) | ❌ U+00FF 초과 시 예외 발생 | ❌ 수동 교체 | Browser, Node 16+, Bun, Deno |
| atob(string) | Base64 string | ❌ 바이너리 문자열 반환 | ❌ 수동 교체 | Browser, Node 16+, Bun, Deno |
| Buffer.from(src, enc) .toString(enc) | string | Buffer | Uint8Array | ✅ utf8 인코딩 | ✅ Node 18+의 base64url | Node.js, Bun |
| TextEncoder().encode(str) + btoa() | string(모든 Unicode) | ✅ UTF-8 바이트 경유 | ❌ 수동 교체 | Browser, Node 16+, Deno |
| Uint8Array.toBase64() (TC39) | Uint8Array | ✅ 바이너리 | ✅ omitPadding + alphabet | Chrome 130+, Node 22+ |
Buffer.from(src, enc).toString(enc) 시그니처는 Base64와 관련된 여러 인코딩 값을 받습니다:
URL 안전 Base64 — JWT, URL, 파일명을 위한 인코딩
표준 Base64는 +와 /를 사용하는데, 이들은 URL에서 예약된 문자입니다 — +는 쿼리 문자열에서 공백으로 디코딩되고, /는 경로 구분자입니다. JWT, URL 매개변수, 파일명, 쿠키 값은 모두 URL 안전 변형이 필요합니다:+ → -, / → _, 후행 = 제거.
브라우저 — 수동 문자 교체
function toBase64Url(text: string): string {
// For ASCII-safe input (e.g., JSON with only ASCII chars)
return btoa(text)
.replace(/+/g, '-')
.replace(///g, '_')
.replace(/=/g, '')
}
function fromBase64Url(encoded: string): string {
// Restore standard Base64 characters and padding before decoding
const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/')
const padded = base64 + '==='.slice(0, (4 - base64.length % 4) % 4)
return atob(padded)
}
// JWT header — must be URL-safe Base64
const header = JSON.stringify({ alg: 'HS256', typ: 'JWT' })
const encoded = toBase64Url(header)
console.log(encoded) // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
const decoded = fromBase64Url(encoded)
console.log(decoded) // {"alg":"HS256","typ":"JWT"}Node.js 18+ — 네이티브 'base64url' 인코딩
// Node.js 18 added 'base64url' as a first-class Buffer encoding
const sessionPayload = JSON.stringify({
userId: 'usr_9f2a1c3e8b4d',
role: 'editor',
workspaceId:'ws_3a7f91c2',
exp: Math.floor(Date.now() / 1000) + 3600,
})
const encoded = Buffer.from(sessionPayload, 'utf8').toString('base64url')
// No + or / or = characters in the output
// eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2U4YjRkIiwicm9sZSI6ImVkaXRvciIsIndvcmtzcGFjZUlkIjoid3NfM2E3ZjkxYzIiLCJleHAiOjE3MTcyMDM2MDB9
const decoded = Buffer.from(encoded, 'base64url').toString('utf8')
console.log(JSON.parse(decoded).role) // editorJavaScript에서 파일 및 API 응답 인코딩
프로덕션 코드에서 Base64 인코딩은 주로 전송되는 파일과 바이너리 콘텐츠를 전달하는 외부 API의 응답에 적용됩니다. 브라우저와 Node.js에서 패턴이 다르며, 바이너리 데이터는 특별한 주의가 필요합니다.
브라우저 — input 요소에서 파일 인코딩
// Modern approach: File.arrayBuffer() (Chrome 76+, Firefox 69+, Safari 14+)
async function encodeFile(file: File): Promise<string> {
const buffer = await file.arrayBuffer()
const bytes = new Uint8Array(buffer)
const chars = Array.from(bytes, b => String.fromCharCode(b))
return btoa(chars.join(''))
}
const uploadInput = document.getElementById('avatar') as HTMLInputElement
uploadInput.addEventListener('change', async (e) => {
const file = (e.target as HTMLInputElement).files?.[0]
if (!file) return
try {
const encoded = await encodeFile(file)
const dataUri = `data:${file.type};base64,${encoded}`
// Preview the image inline
const img = document.getElementById('preview') as HTMLImageElement
img.src = dataUri
img.hidden = false
console.log(`Encoded ${file.name} (${file.size} bytes) → ${encoded.length} Base64 chars`)
} catch (err) {
console.error('Encoding failed:', err)
}
})API에서 Base64 인코딩된 바이너리 가져오기
// GitHub Contents API returns file content as Base64 with embedded newlines
async function fetchRepoFile(
owner: string,
repo: string,
path: string,
token: string,
): Promise<string> {
const res = await fetch(
`https://api.github.com/repos/${owner}/${repo}/contents/${path}`,
{
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/vnd.github.v3+json',
},
}
)
if (!res.ok) throw new Error(`GitHub API ${res.status}: ${res.statusText}`)
const data = await res.json() as { content: string; encoding: string; size: number }
if (data.encoding !== 'base64') {
throw new Error(`Unexpected encoding from GitHub: ${data.encoding}`)
}
// GitHub wraps output at 60 chars — strip newlines before decoding
const clean = data.content.replace(/\n/g, '')
return atob(clean)
}
const openApiSpec = await fetchRepoFile(
'acme-corp', 'platform-api', 'openapi.json', process.env.GITHUB_TOKEN!
)
const spec = JSON.parse(openApiSpec)
console.log(`API version: ${spec.info.version}`)API 디버깅 중 스크립트 없이 인코딩된 응답을 검사하려면, Base64 값을 Base64 Encoder 에 직접 붙여넣으세요 — 디코딩도 지원하며 즉시 출력됩니다. GitHub API 응답, JWT 페이로드, webhook 서명 검사에 유용합니다.
Node.js와 셸에서 명령줄 Base64 인코딩
CI/CD 스크립트, Makefile 대상, 또는 일회성 디버깅에는 전체 스크립트가 거의 필요 없습니다. 시스템 도구와 Node.js 원라이너로 크로스 플랫폼에서 대부분의 경우를 커버할 수 있습니다.
# ── macOS / Linux system base64 ───────────────────────────────────────
# Standard encoding
echo -n "deploy-bot:sk-prod-a7f2c91e4b3d8" | base64
# ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=
# URL-safe variant (replace chars and strip padding)
echo -n "deploy-bot:sk-prod-a7f2c91e4b3d8" | base64 | tr '+/' '-_' | tr -d '='
# Encode a file inline (macOS: -b 0 removes line wrapping; Linux: --wrap=0)
base64 -b 0 ./config/production.json
# or on Linux:
base64 --wrap=0 ./config/production.json
# Decode
echo "ZGVwbG95LWJvdDpzay1wcm9kLWE3ZjJjOTFlNGIzZDg=" | base64 --decode
# ── Node.js one-liner — works on Windows too ───────────────────────────
node -e "process.stdout.write(Buffer.from(process.argv[1]).toString('base64'))" "my:secret"
# bXk6c2VjcmV0
# URL-safe from Node.js 18+
node -e "process.stdout.write(Buffer.from(process.argv[1]).toString('base64url'))" "my:secret"
# bXk6c2VjcmV0 (same here since there are no special chars)
# Decode in Node.js
node -e "console.log(Buffer.from(process.argv[1], 'base64').toString())" "ZGVwbG95LWJvdA=="base64는 기본적으로 76자에서 출력을 줄 바꿈합니다. 이로 인해 다운스트림 파싱이 깨집니다. 환경 변수나 config 필드에 쓸 때처럼 한 줄 결과가 필요한 경우, 반드시 -b 0(macOS) 또는 --wrap=0(Linux)을 추가하세요.고성능 대안:js-base64
내장 API는 대부분의 사용 사례에 충분합니다. 라이브러리를 사용하는 주요 이유는 크로스 환경 일관성입니다:브라우저와 Node.js 모두에서 실행되는 패키지를 배포할 경우, Buffer를 사용하면 환경 감지나 번들러 설정이 필요하고,btoa()는 Unicode 우회 방법이 필요합니다.js-base64(npm 주간 다운로드 1억+)는 두 가지를 투명하게 처리합니다.
npm install js-base64 # or pnpm add js-base64
import { toBase64, fromBase64, toBase64Url, fromBase64Url, isValid } from 'js-base64'
// Standard encoding — Unicode-safe, works in browser and Node.js
const telemetryEvent = JSON.stringify({
eventId: 'evt_7c3a9f1b2d',
type: 'checkout_completed',
currency: 'EUR',
amount: 14900,
userId: 'usr_4e2b8d6a5c',
timestamp: 1717200000,
})
const encoded = toBase64(telemetryEvent)
const urlEncoded = toBase64Url(telemetryEvent) // No +, /, or = characters
const decoded = fromBase64(encoded)
console.log(JSON.parse(decoded).type) // checkout_completed
// Binary data — pass a Uint8Array as second argument
const pngMagicBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
const binaryEncoded = toBase64(pngMagicBytes, true) // true = binary mode
// Validation before decoding
const suspicious = 'not!valid@base64#'
console.log(isValid(suspicious)) // false내부적으로 js-base64는 사용 가능한 경우 네이티브 Buffer를 사용하고, 브라우저에서는 순수 JS 구현으로 폴백합니다. 대용량 Unicode 문자열에서 TextEncoder+btoa 방식보다 2–3배 빠르며, 대칭적인 API(toBase64 / fromBase64)는 btoa와 atob의 방향을 기억해야 하는 인지 부담을 없애줍니다.
Node.js 스트림으로 대용량 바이너리 파일 인코딩
약 50 MB보다 큰 파일을 인코딩해야 할 때, readFileSync()로 전체 파일을 메모리에 로드하는 것이 문제가 됩니다. Node.js 스트림을 사용하면 데이터를 청크로 처리할 수 있지만, Base64 인코딩에는 제약이 있습니다:청크 경계에서 잘못된 패딩을 방지하려면 인코더에 3바이트의 배수를 입력해야 합니다.
import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'
// Stream a large binary file to a Base64-encoded output file
async function streamEncodeToBase64(
inputPath: string,
outputPath: string,
): Promise<void> {
const readStream = createReadStream(inputPath, { highWaterMark: 3 * 1024 * 256 }) // 768 KB chunks (multiple of 3)
const writeStream = createWriteStream(outputPath, { encoding: 'utf8' })
let buffer = Buffer.alloc(0)
await pipeline(
readStream,
async function* (source) {
for await (const chunk of source) {
buffer = Buffer.concat([buffer, chunk as Buffer])
// Encode in complete 3-byte groups to avoid mid-stream padding
const remainder = buffer.length % 3
const safe = buffer.subarray(0, buffer.length - remainder)
buffer = buffer.subarray(buffer.length - remainder)
if (safe.length > 0) yield safe.toString('base64')
}
// Flush remaining bytes (may add 1 or 2 '=' padding chars)
if (buffer.length > 0) yield buffer.toString('base64')
},
writeStream,
)
}
// Usage: encode a 200 MB video attachment
await streamEncodeToBase64(
'./uploads/product-demo.mp4',
'./dist/product-demo.b64',
)
console.log('Stream encoding complete')= 패딩이 생기지 않도록 청크 크기는 3바이트의 배수여야 합니다. 예제에서는 3 * 1024 * 256 = 786,432바이트(768 KB)를 사용합니다 — 메모리 예산에 따라 highWaterMark를 조정하세요. 50 MB 미만 파일에는 readFile() + Buffer.toString('base64')가 더 간단하고 충분히 빠릅니다.일반적인 실수
Base64 인코딩이 포함된 많은 JavaScript 코드베이스를 검토해왔는데, 이 4가지 실수가 일관되게 나타납니다 — 종종 비 ASCII 문자나 바이너리 파일이 프로덕션의 인코딩 경로에 도달할 때까지 발견되지 않습니다.
실수 1 — Unicode를 btoa()에 직접 전달
문제: btoa()는 코드 포인트 0–255의 문자만 허용합니다.ñ, 이모지, CJK 한자 같은 문자는 즉시 DOMException을 발생시킵니다. 해결책: 먼저 TextEncoder로 인코딩하거나, Node.js에서 Buffer.from(text, 'utf8').toString('base64')를 사용하세요.
// ❌ DOMException: The string to be encoded contains // characters outside of the Latin1 range const username = 'Алексей Иванов' const encoded = btoa(username) // throws
// ✅ Encode as UTF-8 bytes first
function safeEncode(text: string): string {
const bytes = new TextEncoder().encode(text)
const chars = Array.from(bytes, b => String.fromCharCode(b))
return btoa(chars.join(''))
}
const encoded = safeEncode('Алексей Иванов')
// 0JDQu9C10LrRgdC10Lkg0JjQstCw0L3QvtCy실수 2 — atob() 호출 전 패딩 복원 잊기
문제: URL 안전 Base64는 = 패딩을 제거합니다. 패딩 없는 문자열을 atob()에 직접 전달하면 문자열 길이에 따라 잘못된 출력이나 예외가 발생합니다. 해결책: atob()를 호출하기 전에 +와 /를 복원하고 올바른 양의 패딩을 다시 추가하세요.
// ❌ atob() may return wrong data or throw // on URL-safe Base64 without padding const jwtSegment = 'eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ' const decoded = atob(jwtSegment) // Unreliable
// ✅ Restore characters and padding first
function decodeBase64Url(input: string): string {
const b64 = input.replace(/-/g, '+').replace(/_/g, '/')
const pad = b64 + '==='.slice(0, (4 - b64.length % 4) % 4)
return atob(pad)
}
const decoded = decodeBase64Url('eyJ1c2VySWQiOiJ1c3JfOWYyYTFjM2UifQ')
// {"userId":"usr_9f2a1c3e"}실수 3 — 원시 버퍼 대신 인코딩된 청크 연결
문제: btoa()나 .toString('base64')를 호출할 때마다 자체 패딩이 추가됩니다. 두 개의 패딩된 Base64 문자열을 연결하면, 패딩은 맨 끝에만 있어야 하므로 잘못된 출력이 생성됩니다. 해결책: 인코딩하기 전에 원시 데이터를 연결하세요.
// ❌ Both parts are padded independently —
// the combined string is not valid Base64
const part1 = Buffer.from('webhook-secret').toString('base64')
// d2ViaG9vay1zZWNyZXQ= ← has padding
const part2 = Buffer.from('-v2').toString('base64')
// LXYy ← correct in isolation
const combined = part1 + part2 // ❌ Invalid — padding in the middle// ✅ Concatenate raw Buffers before encoding
const combined = Buffer.concat([
Buffer.from('webhook-secret'),
Buffer.from('-v2'),
]).toString('base64')
// d2ViaG9vay1zZWNyZXQtdjI= — single valid Base64 string실수 4 — 인코딩 전 response.text()로 바이너리 API 데이터 읽기
문제: response.text()는 원시 바이트를 UTF-8로 해석하고 인식할 수 없는 바이트 시퀀스를 대체 문자 U+FFFD로 바꿉니다. 이미지, PDF, 오디오 등의 바이너리 콘텐츠는 btoa()에 도달하기 전에 조용히 손상됩니다. 해결책: 원시 바이트를 얻으려면 response.arrayBuffer()를 사용하세요.
// ❌ response.text() corrupts binary data
const res = await fetch('/api/exports/invoice.pdf')
const text = await res.text() // ❌ PDF bytes mangled as UTF-8
const encoded = btoa(text) // ❌ Corrupted Base64// ✅ arrayBuffer() preserves raw bytes
const res = await fetch('/api/exports/invoice.pdf')
const buffer = await res.arrayBuffer()
const bytes = new Uint8Array(buffer)
const chars = Array.from(bytes, b => String.fromCharCode(b))
const encoded = btoa(chars.join('')) // ✅ Valid Base64JavaScript Base64 메서드 — 빠른 비교
| 메서드 | Unicode | 바이너리 데이터 | URL 안전 | 사용 가능 환경 | 설치 필요 |
|---|---|---|---|---|---|
| btoa() / atob() | ❌ Latin-1 | ❌ 우회 방법 필요 | ❌ 수동 교체 | Browser, Node 16+, Bun, Deno | 아니오 |
| TextEncoder + btoa() | ✅ UTF-8 | ✅ Uint8Array 경유 | ❌ 수동 교체 | Browser, Node 16+, Deno | 아니오 |
| Buffer.from().toString() | ✅ utf8 | ✅ 네이티브 | ✅ base64url (Node 18+) | Node.js, Bun | 아니오 |
| Uint8Array.toBase64() (TC39) | ✅ 바이너리 | ✅ 네이티브 | ✅ alphabet 옵션 | Chrome 130+, Node 22+ | 아니오 |
| js-base64 | ✅ 항상 | ✅ Uint8Array | ✅ 내장 | 유니버설 | npm install |
입력이 확실히 ASCII만인 경우(16진수 다이제스트, 숫자 ID, 사전 검증된 Latin-1 문자열)에만 btoa()를 선택하세요. 브라우저에서 사용자가 제공한 텍스트에는 TextEncoder + btoa()를 사용하세요. 모든 Node.js 서버 사이드 코드에는 Buffer가 올바른 기본값입니다. 번들러 설정 없이 두 환경에서 실행되어야 하는 라이브러리에는 js-base64가 모든 엣지 케이스를 제거합니다.
자주 묻는 질문
관련 도구
코드 작성 없이 원클릭으로 인코딩하거나 디코딩하려면, 문자열이나 바이너리를 Base64 Encoder 에 직접 붙여넣으세요 — 브라우저에서 표준 및 URL 안전 모드를 즉시 처리합니다.
Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.
Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.