Base64 인코딩 완전 가이드: 원리·변형·JWT·이미지 인라인까지
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
목차
이메일 첨부파일, 이미지 인라인 삽입, JWT 토큰, HTTP Basic 인증, OAuth 토큰까지
Base64는 개발 현장 곳곳에서 사용됩니다. 처음 보면 알아볼 수 없는 문자열이지만 원리를 알면 간단합니다.
이 글은 Base64 인코딩의 원리, Base64URL 변형, JWT/HTTP Basic/이미지 Data URI 등 실무 활용 사례,
한글 처리 함정, 그리고 "Base64는 암호화가 아니다"라는 핵심 보안 개념까지 정리합니다.
Base64란?#
Base64는 바이너리(2진) 데이터를 64개의 ASCII 문자로 표현하는 인코딩 방식입니다. RFC 4648에 표준이 정의되어 있습니다.
사용 문자: A–Z(26자) + a–z(26자) + 0–9(10자) + +와 /(2자) = 64자
패딩 문자: = (길이 맞춤용)
중요: Base64는 인코딩이지 암호화가 아닙니다. Base64 인코딩된 문자열은 누구든 쉽게 디코딩할 수 있습니다. 보안 목적이 아닌 데이터 표현 형식 변환이 목적입니다.
Base64가 필요한 이유#
텍스트 기반 시스템(이메일, HTTP, XML)은 바이너리 데이터를 직접 전송할 수 없거나
특수 문자로 인해 데이터가 깨질 수 있습니다. 대표적 문제:
- 이메일 SMTP는 7비트 ASCII만 안전하게 전송
- HTTP 헤더는 라인 종결자(CRLF), 콜론(
:) 같은 특수문자 포함 불가 - URL은 특정 문자를 퍼센트 인코딩해야 함
- XML/JSON은 일부 제어 문자를 허용하지 않음
Base64는 바이너리를 안전하게 텍스트 채널로 전송하기 위한 표준 방법입니다.
주요 활용처#
- 이메일 첨부파일: SMTP는 텍스트 프로토콜 → 파일을 Base64로 인코딩 후 전송
- 이미지 인라인: HTML/CSS에서 이미지 파일 대신
data:image/png;base64,...형식 사용 - API 인증: HTTP Basic 인증의
username:password를 Base64 인코딩 - JWT 토큰: Header·Payload를 Base64URL로 인코딩 후 점(
.)으로 연결 - OAuth 2.0: 클라이언트 ID·시크릿을 Base64 인코딩
- 암호학 라이브러리: 공개키·서명·해시 출력 시 텍스트 형식으로 변환
인코딩 원리#
3바이트(24비트)를 4개의 6비트 그룹으로 분할해 각각을 Base64 문자로 변환합니다.
예시: "Man" 인코딩#
M a n
01001101 01100001 01101110 ← 24비트(3바이트)
010011 010110 000101 101110 ← 6비트씩 4그룹
T W F u ← Base64 문자 매핑
→ "TWFu"
결과: 3바이트 → 4문자 (용량 약 33% 증가)
패딩#
길이가 3의 배수가 아닐 때는 = 패딩으로 길이를 맞춥니다.
| 원본 길이 | 패딩 | 예시 |
|---|---|---|
| 3바이트 | 없음 | "Man" → "TWFu" |
| 2바이트 | = 1개 | "Ma" → "TWE=" |
| 1바이트 | == 2개 | "M" → "TQ==" |
인코딩 테이블#
값(10진) 문자 값 문자 값 문자 값 문자
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
... ... ... ... ... ... ... ...
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /
Base64 변형들#
Base64 표준(RFC 4648 §4)#
기본 형식. 이메일·HTTP 본문에서 사용.
+ = 62번 문자
/ = 63번 문자
= = 패딩
Base64URL(RFC 4648 §5)#
URL과 파일명에서 안전하게 사용 가능한 변형. JWT·OAuth 등에서 표준.
| 구분 | 62번 | 63번 | 패딩 |
|---|---|---|---|
| Base64 표준 | + | / | = 포함 |
| Base64URL | - | _ | = 보통 제거 |
URL에서 +는 공백, /는 경로 구분자로 해석될 수 있어 이를 대체한 것입니다.
Base32 / Base16#
| 형식 | 알파벳 수 | 길이 비율 | 용도 |
|---|---|---|---|
| Base16(Hex) | 16 | 200% | SHA 해시, MAC 주소 |
| Base32 | 32 | 160% | TOTP(2단계 인증), Bitcoin |
| Base64 | 64 | 133% | 일반 |
| Base85(ASCII85) | 85 | 125% | PDF, Adobe |
Base64가 ASCII 안전성과 효율성의 균형이 좋아 가장 널리 쓰입니다.
도구 사용법#
Base64 도구에서 텍스트를 입력하면 즉시 인코딩/디코딩 결과를 확인할 수 있습니다.
텍스트 인코딩#
입력: Hello, 안녕!
출력: SGVsbG8sIOyViOuFleyVhSE=
(한글은 UTF-8 인코딩 후 Base64 처리되어 결과가 길어집니다.)
디코딩#
입력: SGVsbG8sIOyViOuFleyVhSE=
출력: Hello, 안녕!
한글 처리: 한글은 UTF-8 인코딩 기준으로 처리됩니다. 인코딩 환경에 따라 결과가 다를 수 있으므로 수신 측과 인코딩 방식을 맞춰야 합니다. EUC-KR로 인코딩된 한글을 Base64에 넣고 UTF-8로 디코딩하면 깨집니다.
실무 활용 사례#
1. HTTP Basic 인증#
API 호출 시 Basic 인증 헤더에 사용됩니다.
username:password → Base64 인코딩 → dXNlcm5hbWU6cGFzc3dvcmQ=
HTTP 헤더:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
주의: Basic 인증은 Base64라는 점에서 누구나 디코딩 가능합니다. HTTPS 위에서만 사용해야 합니다.
2. JWT(JSON Web Token)#
JWT는 세 부분이 .으로 구분된 Base64URL 인코딩 문자열입니다.
Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTczMDAwMDAwMH0.<signature>
각 부분 디코딩:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"user1","exp":1730000000}
Signature: HMAC-SHA256 또는 RSA 서명 (Base64URL)
JWT의 헤더와 페이로드는 암호화된 것이 아니라 Base64URL 인코딩된 것입니다. 디코딩하면 내용을 읽을 수 있으므로
민감한 정보(비밀번호·신용카드 번호)는 페이로드에 넣지 않아야 합니다. JWT의 보안은 서명(Signature)으로 변조 방지일 뿐입니다.
JWT 디코더로 토큰 구조를 직접 확인할 수 있습니다.
3. 이미지 인라인 삽입(Data URI)#
작은 아이콘을 파일 요청 없이 HTML/CSS에 직접 포함할 때:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..." />
.icon {
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxu...");
}
장점: HTTP 요청 절감, 작은 아이콘 즉시 로드.
단점: 캐싱 어려움, 33% 용량 증가, 큰 이미지에 비효율(10KB 이상은 별도 파일 권장).
빌드 도구(Webpack url-loader, Vite ?inline)는 일정 크기 이하 이미지를 자동 인라인 처리합니다.
4. 이메일 첨부파일(MIME)#
Content-Type: application/pdf; name="invoice.pdf"
Content-Transfer-Encoding: base64
JVBERi0xLjQKJaqrrK0KNCAwIG9iago8PC9TIC9HbyAvRCBbMCAvWFlaIDAgNzc3IDAgXSA+P
i AvUGFnZXNbb2JqXSAvQ291bnQgMSAvS2lkc1sxIDAgUl0gPj4KZW5kb2JqCg...
이메일 클라이언트(Outlook, Gmail)는 자동으로 인코딩·디코딩합니다.
5. OAuth 2.0 클라이언트 자격 증명#
client_id:client_secret → Base64
→ Authorization: Basic <encoded>
6. 데이터베이스 BLOB 텍스트 표현#
이미지·파일을 데이터베이스에 텍스트 컬럼으로 저장할 때(권장하지 않지만 흔함):
INSERT INTO files (name, content_base64) VALUES ('avatar.png', 'iVBORw0KGgo...');
CDN/오브젝트 스토리지(S3, R2) 사용이 일반적이지만,
작은 데이터(QR 코드 SVG 등)는 인라인 저장도 사용됩니다.
언어별 사용법#
JavaScript#
// 인코딩
btoa("Hello"); // "SGVsbG8="
btoa(unescape(encodeURIComponent("안녕"))); // 한글 안전
// 디코딩
atob("SGVsbG8="); // "Hello"
decodeURIComponent(escape(atob("..."))); // 한글 안전
// Node.js Buffer
Buffer.from("Hello").toString("base64"); // "SGVsbG8="
Buffer.from("SGVsbG8=", "base64").toString(); // "Hello"
// Base64URL (Node.js 16+)
Buffer.from("Hello").toString("base64url");
Python#
import base64
# 인코딩
base64.b64encode(b"Hello").decode("utf-8") # "SGVsbG8="
base64.urlsafe_b64encode(b"Hello").decode() # Base64URL
# 디코딩
base64.b64decode("SGVsbG8=").decode("utf-8") # "Hello"
# 한글
base64.b64encode("안녕".encode("utf-8")).decode() # UTF-8 인코딩 명시
Shell#
# 인코딩
echo -n "Hello" | base64 # SGVsbG8=
# 디코딩
echo "SGVsbG8=" | base64 -d # Hello
# 파일 인코딩
base64 image.png > image.b64
base64 -d image.b64 > image.png
보안: Base64는 암호화가 아닙니다#
가장 흔한 오해입니다. Base64는 누구나 디코딩 가능한 표현 변환입니다. 실제 보안이 필요한 경우 다음을 사용해야 합니다.
| 목적 | 알맞은 도구 |
|---|---|
| 데이터 보호 | AES, RSA 암호화 |
| 무결성 검증 | HMAC, 디지털 서명 |
| 비밀번호 저장 | bcrypt, Argon2 (Base64는 출력 형식일 뿐) |
| 토큰 위변조 방지 | JWT 서명, JWS, JWE |
Base64를 비밀번호·API 키·개인정보 보호 목적으로 쓰면 거의 평문 저장과 같습니다.
흔한 오용 사례#
- 평문 비밀번호 → Base64 → 데이터베이스 저장 ❌
- API 응답에 Base64로 "암호화된" 사용자 정보 ❌
- URL에 Base64로 "숨긴" 사용자 ID ❌(직접 디코딩 가능)
한글·이모지 처리#
JavaScript의 btoa()는 ASCII만 직접 받습니다. 한글은 UTF-8 변환이 필요합니다.
// 잘못된 방법 — 에러 발생
btoa("안녕"); // InvalidCharacterError
// 올바른 방법
btoa(unescape(encodeURIComponent("안녕")));
// 모던(ES2022+)
const utf8 = new TextEncoder().encode("안녕");
btoa(String.fromCharCode(...utf8));
이모지(예: 🚀)는 UTF-8에서 4바이트로 표현됩니다. Base64 결과 길이는 약 8자(33% 증가).
주의사항#
- 용량 증가: Base64 인코딩 시 원본 대비 약 33% 용량이 늘어납니다. 대용량 파일에는 비효율적.
- 보안 아님: 암호화 목적이 아닙니다.
- 줄바꿈 처리: 일부 구현(MIME)은 76자마다 줄바꿈을 넣습니다. JSON·HTTP 헤더에 넣을 때는 줄바꿈 제거 필요.
- 패딩 차이: Base64URL에서 패딩(
=) 제거가 표준이지만, 라이브러리에 따라 다릅니다. 디코딩 시 패딩 자동 보정 옵션 확인.
자주 묻는 질문#
Q. Base64로 인코딩하면 파일 크기가 왜 33% 증가하나요?
A. 3바이트(24비트)가 4개의 Base64 문자(각 8비트, 총 32비트)로 표현되기 때문입니다. 4/3 = 1.333배.
Q. Base64 vs 16진수(Hex) 어느 게 좋나요?
A. 사람이 읽기에는 16진수가 편하지만(예: 해시값) 200% 크기로 비효율적. 텍스트 안전 전송이 목적이면 Base64가 33% 크기로 효율적.
Q. Base64 디코딩 시 "Invalid character" 오류가 나오는 이유는?
A. 입력에 Base64 알파벳 외 문자(예: 공백, 줄바꿈, 한글)가 있거나,
Base64URL 형식을 표준 디코더가 처리하려고 할 때 발생합니다. 입력 정제 후 시도합니다.
Q. JWT를 디코딩하면 비밀번호가 보이나요?
A. JWT 페이로드는 Base64URL 인코딩이므로 누구나 디코딩 가능합니다. 비밀번호·신용카드·개인정보를 페이로드에 넣지 않는 것이 원칙입니다. JWT의 보안은 서명 검증(서버만 검증 가능)으로 변조 방지일 뿐입니다.
Q. 이미지 Base64 인라인은 언제 권장되나요?
A. 10KB 이하 작은 아이콘·로고에 권장. 그 이상은 HTTP 요청·캐싱 효율 면에서 별도 파일이 좋습니다.
Q. Base64로 큰 파일을 인코딩할 때 메모리 문제는?
A. 파일 전체를 메모리에 로드하지 말고 스트리밍 처리. Node.js fs.createReadStream + Transform 스트림, Python base64.encode()(파일 단위) 사용.
Q. base64url과 base64 변환은 어떻게 하나요?
A.
// Base64 → Base64URL
const url = base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
// Base64URL → Base64
let std = url.replace(/-/g, "+").replace(/_/g, "/");
while (std.length % 4) std += "=";
Base64 도구 활용#
Base64 인코딩/디코딩 도구에서 텍스트를 즉시 인코딩·디코딩할 수 있습니다. 한글은 UTF-8로 자동 처리되며, 결과를 클립보드 복사할 수 있습니다.
개발 도구가 더 필요하다면 정규식 가이드, JSON 포맷터 가이드,
JWT 디코더 가이드, Unix 타임스탬프 가이드도 함께 참고할 수 있습니다.