Base64가 무엇이고 왜 쓰일까?

Base64가 무엇이고 왜 쓰일까?

이메일 첨부 파일이나 JWT 토큰, Data URI 같은 곳에서 빠지지 않고 등장하는 글자가 있는데요. 바로 끝에 =가 붙는 영문과 숫자가 뒤섞인 그 문자열, Base64입니다.

이번 포스팅에서는 Base64가 무엇이고 왜 만들어졌는지부터 짚어보겠습니다. 어떤 원리로 동작하는지, 그리고 오늘날 어디에 자주 쓰이는지도 함께 살펴보겠습니다.

Base64란?

Base64는 임의의 이진 데이터를 64가지 ASCII 문자로만 표현하는 인코딩 방식입니다. 이름 그대로 “기수(base) 64”의 표기 체계인데요. 우리가 평소에 쓰는 10진수가 0부터 9까지 10가지 기호로 수를 표현하듯이 Base64는 64가지 기호로 데이터를 표현합니다.

원본 (이진):  01001000 01100101 01101100 01101100 01101111
              "Hello"

Base64:       SGVsbG8=

이 표현 방식이 왜 필요할까요? 이메일이나 HTTP 헤더처럼 텍스트만 안전하게 다룰 수 있는 환경에서 이미지나 압축 파일 같은 이진 데이터를 주고받아야 할 일이 종종 생기는데요. 이진 데이터를 그대로 넣으면 줄 바꿈 문자나 특수 제어 문자 때문에 깨지기 쉽습니다.

Base64는 이런 문제를 해결하기 위해 데이터를 ASCII 범위 안의 64자만 사용하는 텍스트로 바꿔주는 안전한 포장 방식이라고 보시면 됩니다.

어떤 64가지 문자를 사용할까요?

Base64의 표준 문자표는 RFC 4648에 정의되어 있는데요.

A B C D E F G H I J K L M  ← 0-12
N O P Q R S T U V W X Y Z  ← 13-25
a b c d e f g h i j k l m  ← 26-38
n o p q r s t u v w x y z  ← 39-51
0 1 2 3 4 5 6 7 8 9        ← 52-61
+ /                         ← 62-63

영문 대문자 26자, 영문 소문자 26자, 숫자 10자, 그리고 특수 문자 +/ 두 개를 합치면 정확히 64가지가 됩니다. 이 문자들은 모두 ASCII 영역 안에 있어서 어떤 시스템에서도 깨지지 않고 안전하게 전달됩니다.

어떻게 동작할까요?

Base64는 의외로 단순한 변환을 합니다. 핵심은 6비트 단위로 잘라서 64가지 문자에 매핑하는 것인데요.

컴퓨터의 기본 단위는 8비트(1바이트)이지만 Base64는 6비트를 한 글자로 다룹니다. 6비트는 정확히 2의 6제곱이라서 64가지 값을 표현할 수 있기 때문이죠.

3바이트(24비트)를 받아서 6비트씩 4조각으로 자르면 4개의 Base64 문자가 나옵니다.

원본 "Man":  01001101 01100001 01101110          (3바이트, 24비트)
             └── M ──┘└── a ──┘└── n ──┘
                       ↓ 6비트씩 4조각으로 분할
             010011 010110 000101 101110         (4 × 6비트)
                       ↓ 64가지 문자로 매핑
               19     22      5     46
                T      W      F      u
Base64 "TWFu"

이 변환을 거치면 3바이트가 4문자가 됩니다. 즉 인코딩 후 데이터 크기가 약 1.33배(4 / 3)로 늘어나는 거죠.

참고로 Base64는 글자가 아니라 바이트를 인코딩합니다. “Man” 예시가 깔끔하게 3바이트로 떨어지는 이유는 ASCII 글자 셋이 각각 1바이트씩이기 때문인데요. 한글처럼 다중 바이트 글자라면 먼저 UTF-8 같은 인코딩으로 바이트를 만든 뒤에 Base64를 적용합니다.

패딩 =은 왜 필요할까요?

Base64는 3바이트를 4문자로 변환하는 방식이지만 원본의 길이가 항상 3의 배수일 수는 없습니다. 1바이트나 2바이트가 남으면 어떻게 처리해야 할까요?

이때 등장하는 것이 패딩 문자 =인데요. 모자라는 자리를 =로 채워서 항상 4문자 단위로 맞춥니다.

원본 길이가 3의 배수: "ABC" (3바이트) → "QUJD"   (패딩 없음)
원본 길이가 1 부족:   "AB"  (2바이트) → "QUI="   (패딩 1개)
원본 길이가 2 부족:   "A"   (1바이트) → "QQ=="   (패딩 2개)

여기서 “A” 한 글자가 어떻게 QQ==가 되는지 비트 단위로 따라가 보면 패딩의 정체가 한층 분명해지는데요.

A를 비트로 펼치면 8비트(1바이트)입니다.

A = 0x41 = 0 1 0 0 0 0 0 1

이 8비트를 6비트씩 자르려고 하면 깔끔하게 떨어지지 않습니다. 6비트 한 그룹을 떼고 나면 2비트만 남는데요. 이 2비트만으로는 6비트 그룹이 안 되니 뒤에 0을 4개 붙여서 6비트로 채워줍니다.

첫 번째 그룹: 0 1 0 0 0 0                   = 16
두 번째 그룹: 0 1 0 0 0 0                   = 16
              └원본 2비트┘└0 패딩 4비트┘

두 그룹 모두 Base64 알파벳에서 16번에 해당하는 Q라서 인코딩 결과는 QQ가 됩니다. Base64 출력은 항상 4문자 단위라 비어 있는 두 자리를 =로 채워 QQ==가 완성됩니다.

여기서 헷갈리기 쉬운 부분이 있는데요. = 자체가 0 비트를 표현하는 건 아닙니다. =는 “여기는 원본 데이터가 없었어요”라고 알리는 자리 채우기 마커일 뿐이고 실제 0 패딩 비트는 마지막 진짜 Base64 글자 안에 숨어 있습니다.

= 개수와 0 패딩 비트 사이에는 다음과 같은 약속이 있습니다.

=의 개수 | 원본 바이트 수 | 마지막 글자 안의 0 패딩 비트
   0    |    3바이트     |    0비트
   1    |    2바이트     |    2비트
   2    |    1바이트     |    4비트

그래서 디코더는 = 개수를 먼저 보고 “마지막 글자 안의 뒤쪽 N비트는 패딩이니 버려야겠다”라고 판단합니다. QQ==를 디코딩하는 과정을 따라가 보면 다음과 같은데요.

1. 입력:        Q Q = =
2. = 두 개 발견 → "원본은 1바이트, 마지막 4비트는 패딩이다"
3. Q = 16 = 010000
   Q = 16 = 010000
4. 두 6비트를 이어 붙임: 010000 010000      (12비트)
5. 뒤쪽 4비트 버림:       01000001          (8비트 = 1바이트)
6. 결과: 0x41 = "A"

핵심은 디코더가 0000을 “보고 판단”하는 게 아니라 =를 보고 미리 알고 떨어낸다는 점입니다. 패딩 =이 단순한 자리 채우기를 넘어 디코더에게 메타 정보를 알리는 역할을 겸하는 이유가 여기에 있습니다.

URL에 안전한 Base64URL

표준 Base64에는 +/가 포함되어 있는데, 두 문자는 URL 인코딩에서 특별한 의미를 가집니다. /는 경로 구분자, +는 공백을 표현하는 문자로 해석될 수 있어서 URL이나 파일명에 그대로 쓰면 문제가 생기는데요.

이런 충돌을 피하기 위해 만들어진 변형이 Base64URL입니다. 표준 Base64의 두 문자를 URL에 안전한 문자로 바꾸고 패딩을 생략하는 방식입니다.

표준 Base64:  + → -      (마이너스로 대체)
              / → _      (언더스코어로 대체)
              = 패딩 생략 (대부분 생략)

JWT 토큰이 대표적인 Base64URL 사용처인데요. JWT 헤더와 페이로드는 모두 Base64URL로 인코딩되어 있어서 점(.)으로 토큰을 잘라 첫 두 부분을 Base64URL 디코딩하면 안의 정보를 들여다볼 수 있습니다.

어디에 자주 쓰일까요?

Base64는 만들어진 지 수십 년이 지났지만 여전히 다양한 자리에서 활약합니다.

가장 오래된 사용처는 이메일 첨부 파일입니다. SMTP 프로토콜은 7비트 ASCII만 안전하게 전달할 수 있어서 이미지나 PDF 같은 이진 첨부 파일은 MIME 표준에 따라 Base64로 인코딩되어 전송됩니다.

웹에서 자주 보이는 자리는 Data URI인데요. 작은 이미지나 SVG를 외부 파일 없이 HTML이나 CSS에 직접 박아 넣을 때 Base64 인코딩을 사용합니다.

<img src="data:image/png;base64,iVBORw0KGgoAAAANSU..." />

HTTP Basic 인증 헤더에서도 Base64를 만나게 됩니다. 사용자명:비밀번호 형식의 자격 증명을 인코딩해서 헤더에 실어 보냅니다.

Authorization: Basic YWRtaW46cGFzc3dvcmQ=

JWT 토큰이나 OAuth의 PKCE 같은 인증 프로토콜에서도 Base64URL이 광범위하게 쓰입니다. 이런 곳에서는 토큰을 URL 쿼리 매개변수나 HTTP 헤더에 안전하게 담기 위해 URL 안전 변형을 선호합니다.

이외에도 소스맵이나 암호화 키 저장처럼 이진 데이터를 텍스트 파일에 담아야 하는 거의 모든 자리에서 Base64는 표준 선택지입니다.

Base64는 암호화가 아닙니다

Base64를 처음 보면 알 수 없는 문자열로 가려져 있어서 “암호화”라고 오해하기 쉬운데요. 실제로는 그렇지 않습니다.

Base64는 누구나 쉽게 디코딩할 수 있는 공개 인코딩 방식입니다. 비밀번호를 보호하거나 데이터를 숨기는 용도로는 절대 사용하면 안 됩니다.

atob("c2VjcmV0"); // "secret" — 누구나 즉시 풀 수 있음

Base64로 가린다는 것은 종이에 쓴 글자를 카본지로 한 번 베껴 적은 정도의 수준이라고 보시면 됩니다. 실제로 비밀을 지키려면 AES 같은 진짜 암호화 알고리즘을 써야 합니다.

마치며

지금까지 Base64가 무엇이고 어떻게 동작하는지부터 패딩 규칙, URL 안전 변형, 그리고 다양한 활용처까지 살펴보았습니다.

Base64는 “이진 데이터를 텍스트만 다루는 환경에서 안전하게 옮기는 표준 포장 방식”이라고 정리해두시면 좋겠는데요. 자바스크립트에서 Base64를 다루는 구체적인 방법은 별도 포스팅에서 자세히 다루었으니 참고 바랍니다.

더 자세한 명세는 RFC 4648을 참고하세요.

This work is licensed under CC BY 4.0 CC BY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord