이미지를 Base64로 변환하는 작업은 개발자가 항상 마주치는 태스크입니다. HTTP 요청을 줄이기 위해 아이콘을 CSS에 직접 임베딩하거나, JSON API 페이로드에 이미지를 첨부하거나, HTML 이메일에 로고를 포함하거나, 데이터베이스 필드에 이미지 데이터를 저장하는 등의 용도가 있습니다. Base64 인코딩은 바이너리 이미지 데이터를 출력 가능한 ASCII 문자열로 변환하여 텍스트 기반 프로토콜을 안전하게 통과할 수 있게 합니다.
Base64 이미지 인코딩이란
Base64는 64개의 출력 가능한 ASCII 문자(AZ·az·0~9·+·/)를 사용하여 바이너리 데이터를 표현하는 인코딩 방식입니다. 이미지 파일은 바이너리 데이터(픽셀 값·압축 테이블·메타데이터를 나타내는 바이트 시퀀스)입니다. Base64는 그 바이트들을 텍스트가 유효한 어디에든 임베딩할 수 있는 텍스트 문자열로 변환합니다.
트레이드오프: Base64 인코딩된 데이터는 원본 바이너리보다 약 33% 더 큽니다. 100 KB PNG는 Base64 문자열로 약 133 KB가 됩니다.
데이터 URI 문법
웹에서 Base64 이미지의 가장 일반적인 사용은 데이터 URI 스킴입니다:
data:[<mediatype>][;base64],<data>
실제 예시:
<!-- PNG 이미지를 HTML에 직접 임베딩 -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" alt="1px dot">
<!-- CSS에서 SVG 아이콘을 데이터 URI로 사용 -->
.icon {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTEyIDJMMiA3bDEwIDUgMTAtNS0xMC01ek0yIDE3bDEwIDUgMTAtNS0xMC01eiIvPjwvc3ZnPg==");
}
미디어 타입은 브라우저에게 데이터 해석 방법을 알려줍니다: image/png·image/jpeg·image/svg+xml·image/webp·image/gif.
Base64 이미지를 사용해야 할 때
적합한 사용 사례
CSS의 아이콘과 로고(작은 것): 2~3 KB SVG나 PNG를 데이터 URI로 임베딩하면 HTTP 요청이 한 번 줄어듭니다. 모든 페이지에서 사용되는 작은 에셋에 체감 성능 개선 효과가 있습니다.
HTML 이메일 템플릿: 이메일 클라이언트는 일반적으로 외부 이미지 요청을 차단합니다. 이미지를 Base64로 임베딩하면 네트워크 접근이나 사용자 허가 없이 표시됩니다.
JSON API 페이로드: 컴퓨터 비전 API·문서 처리 서비스·썸네일 업로드 엔드포인트 등 일부 API는 JSON 요청 본문에서 Base64 인코딩된 문자열로 이미지를 받습니다.
오프라인/자체 완결 HTML: 네트워크 없이 작동해야 하는 HTML 보고서나 문서를 만들 때, Base64 임베딩된 이미지는 모든 에셋이 파일과 함께 포함되도록 보장합니다.
Canvas와 WebGL: JavaScript drawImage() 등의 API는 Base64 데이터 URI를 직접 처리합니다.
Base64를 사용하지 말아야 할 때
큰 이미지: 10~20 KB를 초과하는 이미지에서는 33% 크기 오버헤드가 큽니다. 파일 참조를 사용하세요.
여러 페이지에서 사용되는 이미지: Base64 이미지는 브라우저에서 독립적으로 캐시할 수 없습니다. 외부 이미지 URL은 한 번 캐시되지만, 10개 페이지에 임베딩된 동일한 Base64 문자열은 10번 디코딩됩니다.
성능이 중요한 페이지: 큰 Base64 문자열 파싱은 메인 스레드를 차단합니다. 외부 이미지는 병렬로 다운로드되고 브라우저가 오프스레드에서 디코딩합니다.
코드로 이미지를 Base64로 변환하기
JavaScript(브라우저)
<input> 요소에서 파일 읽기:
const input = document.getElementById('fileInput');
input.addEventListener('change', (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = (event) => {
const base64 = event.target.result;
// base64 = "data:image/png;base64,iVBORw0KGgo..."
console.log(base64);
// 데이터 URI 접두사를 제거하여 순수 Base64 문자열 얻기:
const base64Data = base64.split(',')[1];
console.log(base64Data);
};
reader.readAsDataURL(file);
});
Canvas를 사용하여 이미지 URL을 Base64로 변환:
async function imageUrlToBase64(url) {
const img = new Image();
img.crossOrigin = 'anonymous';
return new Promise((resolve, reject) => {
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
resolve(canvas.toDataURL('image/png'));
};
img.onerror = reject;
img.src = url;
});
}
CORS 함정: 오염된 캔버스 오류
크로스 오리진 이미지를 캔버스에 그린 후 canvas.toDataURL()을 호출하면 다음 오류가 발생할 수 있습니다:
SecurityError: The operation is insecure.
브라우저는 다른 오리진의 픽셀이 포함된 캔버스를 “오염”으로 표시하고, 크로스 오리진 데이터 누출을 방지하기 위해 toDataURL()을 차단합니다.
해결책 1 — crossOrigin = "anonymous" + 서버 CORS 헤더
이미지 서버가 CORS를 지원한다면, src를 할당하기 전에 crossOrigin을 설정하고 서버가 Access-Control-Allow-Origin: *으로 응답하는지 확인합니다:
const img = new Image();
img.crossOrigin = 'anonymous'; // .src 전에 설정 필수
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext('2d').drawImage(img, 0, 0);
console.log(canvas.toDataURL()); // 서버가 CORS 헤더를 보내는 경우에만 작동
};
img.src = 'https://external-server.com/image.png';
crossOrigin = "anonymous"만으로는 부족하며, 서버도 Access-Control-Allow-Origin 응답 헤더를 보내야 합니다.
해결책 2 — fetch() + Blob + FileReader(Canvas 완전 회피)
CORS를 지원하는 서버라면 Canvas를 건너뛰고 fetch()를 사용합니다:
async function imageUrlToBase64(url) {
const response = await fetch(url);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
오염된 캔버스 제한을 완전히 회피합니다. 동일 오리진 URL과 서버가 CORS 헤더를 보내는 크로스 오리진 URL 모두 작동합니다.
참고: ZeroTool 이미지 변환기는 로컬 파일에
FileReader를 사용합니다. 외부 요청 없음, CORS 문제 없음.
JavaScript(Node.js)
const fs = require('fs');
// 파일을 Base64로 변환
const imageBuffer = fs.readFileSync('./image.png');
const base64 = imageBuffer.toString('base64');
const dataUri = `data:image/png;base64,${base64}`;
console.log(dataUri);
확장자로 미디어 타입 감지:
const fs = require('fs');
const path = require('path');
function imageToDataUri(filePath) {
const ext = path.extname(filePath).slice(1).toLowerCase();
const mimeTypes = {
png: 'image/png',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
gif: 'image/gif',
webp: 'image/webp',
svg: 'image/svg+xml',
};
const mime = mimeTypes[ext] || 'application/octet-stream';
const data = fs.readFileSync(filePath).toString('base64');
return `data:${mime};base64,${data}`;
}
Python
import base64
from pathlib import Path
def image_to_base64(file_path: str) -> str:
"""이미지 파일의 Base64 인코딩된 콘텐츠 반환"""
return base64.b64encode(Path(file_path).read_bytes()).decode('utf-8')
def image_to_data_uri(file_path: str, mime_type: str = 'image/png') -> str:
"""HTML이나 CSS 임베딩에 적합한 데이터 URI 반환"""
b64 = image_to_base64(file_path)
return f"data:{mime_type};base64,{b64}"
# 사용 예
print(image_to_data_uri('logo.png', 'image/png'))
커맨드라인
# macOS / Linux
base64 -i image.png
base64 -i image.png | tr -d '\n' # 줄 바꿈 없는 한 줄
# 데이터 URI 생성
echo "data:image/png;base64,$(base64 -i image.png | tr -d '\n')"
Base64 문자열 다루기
Base64를 이미지로 디코딩
// Node.js: Base64 문자열 → 파일
const fs = require('fs');
const base64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA...';
const buffer = Buffer.from(base64Data, 'base64');
fs.writeFileSync('output.png', buffer);
# Python: Base64 문자열 → 파일
import base64
from pathlib import Path
base64_data = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB...'
Path('output.png').write_bytes(base64.b64decode(base64_data))
데이터 URI 접두사 추가·제거
// 접두사 추가
const withPrefix = `data:image/png;base64,${base64String}`;
// 접두사 제거
const withoutPrefix = dataUri.replace(/^data:image\/\w+;base64,/, '');
API에 이미지 전송
많은 컴퓨터 비전 API(OpenAI Vision·Google Cloud Vision·AWS Rekognition)는 Base64 인코딩된 이미지를 받습니다:
import base64
import httpx
with open('photo.jpg', 'rb') as f:
image_b64 = base64.b64encode(f.read()).decode('utf-8')
response = httpx.post(
'https://api.example.com/analyze',
json={
'image': {
'type': 'base64',
'media_type': 'image/jpeg',
'data': image_b64,
}
}
)
이미지 형식 참조
| 형식 | MIME 타입 | 용도 | Base64 사용 |
|---|---|---|---|
| PNG | image/png | 아이콘·스크린샷·로고 | 자주 사용 |
| JPEG | image/jpeg | 사진 | 덜 사용(큰 크기) |
| SVG | image/svg+xml | 벡터 아이콘·로고 | 매우 자주 사용 |
| WebP | image/webp | 더 나은 압축의 사진 | 증가 중 |
| GIF | image/gif | 애니메이션 이미지 | 드물게 |
| ICO | image/x-icon | 파비콘 | 자주 사용 |
SVG는 특별한 언급이 필요합니다. SVG는 XML(텍스트)이기 때문에, Base64 인코딩 없이 원시 SVG 문자열을 URL 인코딩하여 임베딩할 수도 있습니다. 이렇게 하면 33% 크기 오버헤드를 피할 수 있습니다:
/* Base64 방식 */
background-image: url("data:image/svg+xml;base64,PHN2Zy4uLg==");
/* URL 인코딩 방식(더 작고 읽기 쉬움) */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'...%3E%3C/svg%3E");
온라인 이미지 Base64 변환기
코드 없이 빠르게 변환하려면, ZeroTool 이미지 Base64 변환기가 완전히 브라우저 내에서 동작합니다. 이미지 파일을 드래그 앤 드롭하거나 선택하면 Base64 문자열이나 데이터 URI를 즉시 얻을 수 있습니다. 어떤 서버에도 파일이 업로드되지 않습니다.