웹 개발을 하다 보면 Base64를 자주 마주칩니다. 이미지를 문자열로 바꾸거나, API 인증 헤더를 만들거나. 근데 정확히 왜 필요한 걸까요?
문제: 이진 데이터를 텍스트로 전송하고 싶다
컴퓨터의 모든 데이터는 결국 0과 1입니다. 이미지, 음악 파일, 압축 파일 모두 이진(binary) 데이터죠.
문제는 이메일이나 JSON 같은 시스템은 텍스트만 다룬다는 겁니다. 이진 데이터를 그대로 넣으면 깨지거나 문제가 생깁니다.
해결책? 이진 데이터를 안전한 텍스트 문자로 변환합니다. 그게 Base64입니다.
Base64의 원리
"Base64"라는 이름은 64개의 문자를 사용한다는 뜻입니다.
A-Z (26) + a-z (26) + 0-9 (10) + '+' + '/' = 64
그리고 패딩용 =도 있습니다.
변환 과정
- 원본 데이터를 비트(0과 1)로 변환
- 6비트씩 끊어서 그룹화 (8비트가 아니라 6비트!)
- 각 6비트 그룹을 Base64 문자로 변환
- 3바이트(24비트) 단위로 처리. 남으면
=로 패딩
"Hi" 변환 예시:
H = 01001000, i = 01101001
연결: 01001000 01101001
6비트씩: 010010 | 000110 | 1001(패딩필요)
→ SGk=
실제 활용 사례
1. Data URL (이미지 인라인)
작은 이미지를 HTTP 요청 없이 HTML/CSS에 직접 넣을 수 있습니다.
<img src="data:image/png;base64,iVBORw0KGgo..." />
장점: HTTP 요청 감소. 단점: HTML 파일 크기 증가, 캐싱 불가
2. HTTP Basic 인증
사용자명:비밀번호를 Base64로 인코딩해서 헤더에 넣습니다.
Authorization: Basic dXNlcjpwYXNz
(user:pass를 Base64 인코딩한 결과)
⚠️ 주의: Base64는 암호화가 아닙니다! 누구나 디코딩할 수 있어요. 반드시 HTTPS와 함께 사용하세요.
3. 이메일 첨부파일
이메일은 텍스트 프로토콜(SMTP)을 사용합니다. 첨부파일은 Base64로 인코딩되어 전송됩니다.
4. JWT 토큰
JWT의 header와 payload는 Base64Url로 인코딩됩니다. (Base64와 비슷하지만 URL에 안전한 문자 사용)
Base64의 단점
- 크기 증가: 원본보다 약 33% 커집니다 (3바이트 → 4문자)
- 암호화 아님: 난독화일 뿐, 누구나 디코딩 가능
- 처리 오버헤드: 인코딩/디코딩에 CPU 사용
언제 쓰고 언제 쓰지 말아야 할까?
| O 쓰면 좋은 경우 | X 쓰지 말아야 할 경우 |
|---|---|
| 작은 이미지 인라인 | 큰 파일 전송 (느리고 비효율) |
| JSON에 바이너리 포함 | 민감한 정보 숨기기 (암호화 아님!) |
| 텍스트 프로토콜 호환 | 대용량 데이터 (크기 33% 증가) |
JavaScript에서 사용하기
// 인코딩
btoa("Hello") // "SGVsbG8="
// 디코딩
atob("SGVsbG8=") // "Hello"
참고: btoa/atob는 ASCII만 지원합니다. 한글 등 유니코드는 추가 처리가 필요해요.
유니코드를 Base64로 인코딩하기
한글이나 이모지 같은 유니코드 문자를 btoa()에 직접 넣으면 에러가 발생합니다. UTF-8로 먼저 변환해야 합니다.
// 방법 1: TextEncoder 사용 (권장)
function encodeUnicode(str) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
let binary = '';
bytes.forEach(b => binary += String.fromCharCode(b));
return btoa(binary);
}
encodeUnicode("안녕하세요"); // "7JWI64WV7ZWY7IS47JqU"
// 방법 2: encodeURIComponent 사용
btoa(encodeURIComponent("안녕하세요"));
// "%EC%95%88%EB%85..."을 인코딩한 결과
Python에서는 이런 문제가 없습니다. 바이트열을 명시적으로 다루기 때문에 자연스럽게 처리됩니다.
import base64
# Python은 인코딩을 명시적으로 지정
result = base64.b64encode("안녕하세요".encode('utf-8'))
print(result) # b'7JWI64WV7ZWY7IS47JqU'
Base64의 역사
Base64가 처음 공식적으로 등장한 것은 1987년 RFC 989(Privacy Enhanced Mail)에서입니다. 당시 이메일 시스템은 7비트 ASCII만 처리할 수 있었기 때문에, 바이너리 첨부 파일을 전송하려면 텍스트로 변환하는 방법이 필요했습니다.
이후 여러 RFC를 거치며 Base64 표준이 정립되었고, 현재 사용되는 최종 표준은 2006년의 RFC 4648입니다. 이 문서에서는 기본 Base64뿐만 아니라 URL에 안전한 변형(Base64url), Base32, Base16(Hex) 등도 함께 정의하고 있습니다.
Base64url: URL에 안전한 변형
표준 Base64는 +와 / 문자를 사용하는데, 이 문자들은 URL에서 특별한 의미를 가집니다. +는 공백으로 해석되고, /는 경로 구분자입니다. 그래서 URL이나 파일명에 사용할 때는 Base64url 변형을 씁니다.
표준 Base64: A-Z, a-z, 0-9, +, / (패딩: =) Base64url: A-Z, a-z, 0-9, -, _ (패딩: 생략 가능) 변환 규칙: + → - / → _ = → 제거
JWT(JSON Web Token)가 대표적인 Base64url 사용 사례입니다. 토큰이 URL 쿼리 파라미터나 HTTP 헤더에 들어가기 때문에 URL 안전 문자를 사용해야 합니다.
다른 인코딩 방식과의 비교
바이너리 데이터를 텍스트로 변환하는 방법은 Base64만 있는 것이 아닙니다. 상황에 따라 다른 인코딩이 더 적합할 수 있습니다.
Hex (16진수) 인코딩
각 바이트를 2자리 16진수로 표현합니다. 0-9와 a-f만 사용하므로 어디서든 안전하게 쓸 수 있지만, 원본 대비 크기가 2배가 됩니다. 해시값 표현이나 디버깅 용도로 주로 사용됩니다.
"Hi" → Hex: "4869" (4바이트, 원본의 2배) "Hi" → Base64: "SGk=" (4바이트, 원본의 1.33배)
URL 인코딩 (Percent Encoding)
URL에서 안전하지 않은 문자를 %XX 형태로 변환합니다. "안녕"은 %EC%95%88%EB%85%95이 됩니다. 용도가 URL로 한정되어 있고, 한글처럼 멀티바이트 문자는 크기가 크게 증가하는 단점이 있습니다.
ASCII85 (Base85)
85개의 ASCII 문자를 사용하는 인코딩입니다. Base64보다 효율적이어서 크기가 약 25%만 증가합니다(Base64는 33%). Adobe의 PostScript와 PDF 내부에서 사용됩니다. 하지만 사용할 수 있는 문자가 제한적인 환경(이메일, URL 등)에서는 부적합합니다.
| 인코딩 | 문자셋 | 크기 증가율 | 주 용도 |
|---|---|---|---|
| Base64 | 64개 | ~33% | 이메일, API, 웹 |
| Hex | 16개 | ~100% | 해시값, 디버깅 |
| ASCII85 | 85개 | ~25% | PDF, PostScript |
| URL encoding | 제한적 | 가변 | URL 파라미터 |
Base32와 Base58
Base64 외에도 다양한 Base 인코딩이 있습니다. Base32는 A-Z와 2-7만 사용해서 대소문자 구분이 없는 환경에서 유용합니다. OTP(One-Time Password) 앱의 비밀키가 대표적인 사용 사례입니다. Google Authenticator에서 수동으로 키를 입력할 때 Base32로 인코딩된 문자열을 쓰는 이유가 바로 대소문자 혼동을 방지하기 위해서입니다.
Base58은 비트코인 주소에서 사용됩니다. Base64에서 혼동하기 쉬운 문자들(0, O, l, I)과 URL에서 문제가 되는 문자(+, /)를 제거한 것입니다. 비트코인 주소를 직접 입력하거나 읽을 때 실수를 줄이기 위한 설계입니다.
성능 고려사항
Base64 인코딩/디코딩은 단순한 비트 연산이라 매우 빠릅니다. 대부분의 경우 성능 걱정을 할 필요가 없습니다. 하지만 몇 가지 상황에서는 주의가 필요합니다.
- 큰 파일 인라인: 수 MB 이상의 이미지를 Data URL로 변환하면, HTML/CSS 파일 크기가 급격히 증가합니다. 브라우저가 CSS를 파싱하는 데 시간이 오래 걸리고, 캐싱도 되지 않습니다.
- 반복적인 인코딩: 매 요청마다 같은 데이터를 Base64로 인코딩한다면, 결과를 캐싱하는 것이 좋습니다.
- 메모리 사용량: Base64 문자열은 원본보다 33% 크므로, 대량의 데이터를 처리할 때는 메모리 사용량이 증가합니다. 스트리밍 방식의 인코딩/디코딩을 고려하세요.
보안 관련 오해와 진실
Base64에 대해 가장 흔한 오해는 "인코딩하면 안전하다"는 것입니다. 몇 가지 사실을 정리하겠습니다.
Base64는 암호화가 아닙니다. 키가 없어도 누구나 디코딩할 수 있습니다. Base64는 데이터 형식을 변환하는 것이지, 데이터를 보호하는 것이 아닙니다. 비밀번호나 API 키를 Base64로 인코딩해서 코드에 넣는 것은 보안에 전혀 도움이 되지 않습니다.
HTTP Basic 인증은 안전하지 않습니다. username:password를 Base64로 인코딩해서 보내는 방식인데, 네트워크를 도청하면 그대로 노출됩니다. 반드시 HTTPS와 함께 사용해야 하며, 가능하면 OAuth 2.0 같은 현대적인 인증 방식으로 전환하는 것이 좋습니다.
소스 코드에서 Base64 문자열은 의심하세요. 악성 코드가 탐지를 피하기 위해 페이로드를 Base64로 인코딩하는 경우가 많습니다. 코드 리뷰에서 의미 불명의 Base64 문자열을 발견하면 디코딩해서 내용을 확인하세요.
정리
Base64는 화려하거나 복잡한 기술이 아닙니다. 하지만 웹 개발의 곳곳에서 조용히 일하고 있는 기본기 같은 존재입니다. 이메일 첨부파일, JWT 토큰, Data URL, HTTP 인증 헤더까지, 한 번 원리를 이해하면 이런 것들이 왜 그렇게 작동하는지 자연스럽게 알게 됩니다.
자주 묻는 질문
Base64로 인코딩하면 보안이 강화되나요?
Base64는 보안과 전혀 관련이 없습니다. 인코딩 방식일 뿐 암호화가 아니므로 누구나 쉽게 디코딩할 수 있습니다. 민감한 데이터를 보호하려면 AES, RSA 같은 암호화 알고리즘을 사용해야 합니다.
Base64 인코딩 시 크기가 얼마나 증가하나요?
원본 대비 약 33% 증가합니다. 3바이트의 원본 데이터가 4개의 Base64 문자로 변환되기 때문입니다. 추가로 줄바꿈이 포함되면 조금 더 늘어날 수 있습니다.
Base64와 Base64URL의 차이는?
Base64는 +, /, = 문자를 사용하고, Base64URL은 -, _ 로 대체하며 패딩(=)을 생략합니다. URL이나 파일명에서 안전하게 사용할 수 있어 JWT 토큰 등에 사용됩니다.
모든 프로그래밍 언어에서 Base64를 지원하나요?
네, 대부분의 언어에서 기본 라이브러리로 지원합니다. JavaScript는 btoa()/atob(), Python은 base64 모듈, Java는 java.util.Base64를 사용합니다.
이미지를 Base64로 변환하는 것이 항상 좋은가요?
작은 아이콘(수 KB 이하)에는 Data URI로 사용하면 HTTP 요청을 줄일 수 있어 유리합니다. 하지만 큰 이미지는 33% 크기 증가와 브라우저 캐싱 불가로 인해 일반 파일로 제공하는 것이 효율적입니다.