Toolypet
블로그로 돌아가기
Dev

Base64 인코딩의 모든 것 - 원리부터 실전 활용까지

Base64 인코딩이 무엇인지, 왜 필요한지, 어떻게 사용하는지 실전 예제와 함께 완벽하게 알아봅니다. 이미지 인라인 삽입부터 JWT까지.

Toolypet Team

Toolypet Team

Development Team

7 분 읽기

Base64 인코딩의 모든 것

"이 이미지를 API로 어떻게 보내죠?" "이 바이너리 데이터를 JSON에 어떻게 넣죠?"

답은 Base64입니다.

이 가이드에서는 Base64의 원리부터 실전 활용까지 완벽하게 다룹니다.


Base64란?

Base64는 바이너리 데이터를 텍스트로 변환하는 인코딩 방식입니다.

왜 필요한가?

문제Base64 해결책
이메일로 바이너리 전송 불가텍스트로 변환하여 전송
JSON에 바이너리 포함 불가문자열로 변환하여 포함
URL에 특수문자 불가URL-safe 문자만 사용
HTML에 이미지 인라인 삽입Data URL로 변환

특징

  • 64개 문자 사용: A-Z, a-z, 0-9, +, / (표준)
  • 크기 증가: 원본 대비 약 33% 증가
  • 가역적: 인코딩 ↔ 디코딩 완벽 복원
  • 암호화 아님: 누구나 디코딩 가능

Base64 원리

인코딩 과정

원본 문자열: "Man"

1. ASCII 변환
   M = 77 = 01001101
   a = 97 = 01100001
   n = 110 = 01101110

2. 6비트씩 분할
   010011 | 010110 | 000101 | 101110
      19  |   22   |    5   |   46

3. Base64 문자로 변환 (A=0, B=1, ... Z=25, a=26, ...)
   19 = T
   22 = W
   5  = F
   46 = u

결과: "TWFu"

Base64 문자표

인덱스: 문자
0-25:   A-Z
26-51:  a-z
52-61:  0-9
62:     +
63:     /
패딩:   =

패딩 (=)

원본 길이가 3으로 나누어 떨어지지 않으면 패딩 추가

"M"   → "TQ=="  (2개 패딩)
"Ma"  → "TWE="  (1개 패딩)
"Man" → "TWFu"  (패딩 없음)

언어별 구현

JavaScript (브라우저)

// 인코딩
const encoded = btoa('Hello, World!');
console.log(encoded); // "SGVsbG8sIFdvcmxkIQ=="

// 디코딩
const decoded = atob('SGVsbG8sIFdvcmxkIQ==');
console.log(decoded); // "Hello, World!"

// 유니코드 처리 (한글 등)
function encodeUnicode(str) {
  return btoa(encodeURIComponent(str).replace(
    /%([0-9A-F]{2})/g,
    (_, p1) => String.fromCharCode(parseInt(p1, 16))
  ));
}

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

// 유니코드 테스트
const korean = '안녕하세요';
const encodedKorean = encodeUnicode(korean);
console.log(encodedKorean); // "7JWI64WVIQ=="
console.log(decodeUnicode(encodedKorean)); // "안녕하세요"

Node.js

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

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

// 파일 인코딩
const fs = require('fs');

const imageBuffer = fs.readFileSync('image.png');
const base64Image = imageBuffer.toString('base64');
console.log(base64Image.substring(0, 50) + '...');

Python

import base64

# 인코딩
encoded = base64.b64encode(b'Hello, World!').decode('utf-8')
print(encoded)  # "SGVsbG8sIFdvcmxkIQ=="

# 디코딩
decoded = base64.b64decode('SGVsbG8sIFdvcmxkIQ==').decode('utf-8')
print(decoded)  # "Hello, World!"

# 유니코드 (한글)
korean = '안녕하세요'
encoded_korean = base64.b64encode(korean.encode('utf-8')).decode('utf-8')
print(encoded_korean)  # "7JWI64WVIQ=="

# 파일 인코딩
with open('image.png', 'rb') as f:
    base64_image = base64.b64encode(f.read()).decode('utf-8')

Go

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    // 인코딩
    data := "Hello, World!"
    encoded := base64.StdEncoding.EncodeToString([]byte(data))
    fmt.Println(encoded) // "SGVsbG8sIFdvcmxkIQ=="

    // 디코딩
    decoded, _ := base64.StdEncoding.DecodeString(encoded)
    fmt.Println(string(decoded)) // "Hello, World!"

    // URL-safe Base64
    urlSafe := base64.URLEncoding.EncodeToString([]byte(data))
    fmt.Println(urlSafe)
}

실전 활용

1. 이미지 인라인 삽입 (Data URL)

<!-- HTML에서 직접 이미지 삽입 -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" alt="1x1 red pixel">
// 파일 → Base64 Data URL
function fileToDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// 사용
const file = document.getElementById('fileInput').files[0];
const dataUrl = await fileToDataUrl(file);
document.getElementById('preview').src = dataUrl;

2. API로 이미지 전송

// 클라이언트
async function uploadImage(file) {
  const base64 = await fileToBase64(file);

  const response = await fetch('/api/upload', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      filename: file.name,
      contentType: file.type,
      data: base64,
    }),
  });

  return response.json();
}

function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      // data:image/png;base64, 접두사 제거
      const base64 = reader.result.split(',')[1];
      resolve(base64);
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}
// 서버 (Node.js)
app.post('/api/upload', (req, res) => {
  const { filename, contentType, data } = req.body;

  // Base64 → Buffer → 파일 저장
  const buffer = Buffer.from(data, 'base64');
  fs.writeFileSync(`uploads/${filename}`, buffer);

  res.json({ success: true, filename });
});

3. JWT 토큰

// JWT = Header.Payload.Signature (각각 Base64URL 인코딩)

const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';

// Payload 디코딩
const payload = token.split('.')[1];
const decoded = JSON.parse(atob(payload));
console.log(decoded);
// { sub: "1234567890", name: "John Doe", iat: 1516239022 }

4. Basic 인증

// HTTP Basic Authentication
const username = 'user';
const password = 'password';

const credentials = btoa(`${username}:${password}`);

fetch('/api/protected', {
  headers: {
    'Authorization': `Basic ${credentials}`,
  },
});

// 헤더: Authorization: Basic dXNlcjpwYXNzd29yZA==

5. 이메일 첨부파일 (MIME)

Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"

JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIK
Pj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0K
...

Base64 변형

URL-safe Base64

표준URL-safe
+-
/_
=생략 가능
// URL-safe 인코딩
function toUrlSafeBase64(str) {
  return btoa(str)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

// URL-safe 디코딩
function fromUrlSafeBase64(str) {
  // 패딩 복원
  const pad = str.length % 4;
  const padded = pad ? str + '='.repeat(4 - pad) : str;

  return atob(
    padded
      .replace(/-/g, '+')
      .replace(/_/g, '/')
  );
}

Base64 vs Base32 vs Base16

인코딩문자 수크기 증가사용 사례
Base6464+33%일반적
Base3232+60%대소문자 미구분 필요 시
Base16 (Hex)16+100%해시, 색상 코드

주의사항

1. 암호화가 아님

// ❌ 보안 목적으로 사용하면 안 됨
const password = btoa('mySecretPassword');
// 누구나 atob()으로 복원 가능!

// ✅ 암호화가 필요하면 별도 암호화 후 Base64
const encrypted = await encryptAES(data, key);
const encoded = btoa(encrypted);

2. 크기 증가

// 원본: 1MB 이미지
// Base64: ~1.33MB (33% 증가)

// 대용량 파일은 직접 업로드가 효율적
// FormData + multipart/form-data 사용

3. 유니코드 처리

// ❌ 브라우저 btoa()는 Latin-1만 지원
btoa('한글'); // Error: The string contains characters outside of the Latin1 range

// ✅ 유니코드 처리 필요
function encodeUnicode(str) {
  return btoa(unescape(encodeURIComponent(str)));
}

4. 줄바꿈

// 일부 시스템은 76자마다 줄바꿈 추가
// MIME 표준: 76자 줄바꿈

// 줄바꿈 제거
const cleaned = base64String.replace(/[\r\n]/g, '');

성능 최적화

스트리밍 인코딩

// 대용량 파일을 청크로 처리
async function* encodeFileInChunks(file, chunkSize = 1024 * 1024) {
  const reader = file.stream().getReader();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    // 청크 단위로 Base64 인코딩
    yield Buffer.from(value).toString('base64');
  }
}

웹 워커 활용

// worker.js
self.onmessage = function(e) {
  const { action, data } = e.data;

  if (action === 'encode') {
    const result = btoa(data);
    self.postMessage({ result });
  }
};

// main.js
const worker = new Worker('worker.js');
worker.postMessage({ action: 'encode', data: largeString });
worker.onmessage = (e) => console.log(e.data.result);

디버깅 도구

Base64 Encoder/Decoder

  1. 텍스트 또는 파일 입력
  2. 인코딩/디코딩 버튼 클릭
  3. 결과 복사

CLI

# 인코딩
echo -n "Hello, World!" | base64
# SGVsbG8sIFdvcmxkIQ==

# 디코딩
echo "SGVsbG8sIFdvcmxkIQ==" | base64 -d
# Hello, World!

# 파일 인코딩
base64 image.png > image.txt

# 파일 디코딩
base64 -d image.txt > restored.png

FAQ

Q1: Base64는 왜 33% 커지나요?

A: 3바이트(24비트)를 4문자(4×6=24비트)로 표현하기 때문입니다.

  • 3바이트 → 4문자
  • 증가율: (4-3)/3 = 33%

Q2: 패딩(=)은 왜 필요한가요?

A: 디코더가 원본 길이를 알기 위해 필요합니다. 패딩 개수로 마지막 바이트 수를 추론합니다.

Q3: Base64URL은 언제 사용하나요?

A: URL이나 파일명에 사용할 때입니다. 표준 Base64의 +, /는 URL에서 특수 의미를 가지므로 -, _로 대체합니다.

Q4: 이미지 인라인 삽입의 장단점은?

A:

  • 장점: HTTP 요청 감소, 캐싱 단순화
  • 단점: HTML 크기 증가, 개별 이미지 캐싱 불가
  • 권장: 작은 아이콘(2KB 이하)에만 사용

Q5: Base64 문자열인지 어떻게 확인하나요?

A: 정규식으로 형식 검사 후 디코딩 시도:

function isBase64(str) {
  const regex = /^[A-Za-z0-9+/]*={0,2}$/;
  if (!regex.test(str)) return false;

  try {
    atob(str);
    return true;
  } catch {
    return false;
  }
}

마무리

Base64 핵심 정리:

  1. 용도: 바이너리 → 텍스트 변환
  2. 크기: 원본 대비 33% 증가
  3. 보안: 암호화 아님 (누구나 디코딩 가능)
  4. 변형: URL-safe는 -, _ 사용
  5. 활용: Data URL, JWT, Basic Auth, MIME

관련 도구

도구용도
Base64 Encoder/DecoderBase64 인코딩/디코딩
JWT DecoderJWT 토큰 분석
JSON FormatterJSON 포맷팅
Base64인코딩개발데이터APIJavaScript

저자 소개

Toolypet Team

Toolypet Team

Development Team

The Toolypet Team creates free, privacy-focused web tools for developers and designers. All tools run entirely in your browser with no data sent to servers.

Web DevelopmentCSS ToolsDeveloper ToolsSEOSecurity