Base64 인코딩 이해하기: 원리, 용도, 그리고 흔한 오해
Base64는 임의의 바이트 데이터를 ASCII 문자 64개만으로 표현하는 인코딩 방식입니다. 암호화가 아니고, 압축도 아니며, 오히려 데이터 크기를 약 33% 늘립니다. 그런데도 널리 쓰이는 이유는 '바이너리를 텍스트만 통과하는 통로로 안전하게 실어 보낼 수 있다'는 단 한 가지 성질 때문입니다.
어떻게 동작하는가
Base64는 입력 바이트를 3바이트(24비트)씩 묶고, 이를 6비트 단위 4조각으로 자릅니다. 6비트는 0~63의 값을 가지므로 64글자 알파벳(A-Z, a-z, 0-9, +, /)에 하나씩 대응시킬 수 있습니다. 3바이트가 4문자로 바뀌므로 출력 길이는 입력의 4/3배, 즉 약 33% 증가합니다.
입력 길이가 3의 배수가 아니면 마지막 묶음이 비어서, 부족한 자리를 '=' 문자로 채웁니다. 그래서 Base64 문자열 끝에 =가 0개, 1개, 2개 붙습니다. 남은 바이트가 1개면 ==, 2개면 =가 하나 붙습니다.
'H' = 0x48 = 01001000 'i' = 0x69 = 01101001 비트 이어붙이기: 01001000 01101001 6비트로 자르기: 010010 000110 1001(+00 패딩) 십진수: 18 6 36 알파벳 대응: S G k 남은 자리는 '=' → SGk=
사용 방법
- 인코딩할 텍스트나 디코딩할 Base64 문자열을 입력창에 넣습니다.
- Encode 또는 Decode 방향을 선택합니다.
- 결과를 복사해 사용합니다. 디코딩 시 입력이 올바른 Base64가 아니면 오류가 표시됩니다.
- URL이나 파일 이름에 넣어야 한다면 아래의 URL-safe 변형 설명을 확인하세요.
표준 Base64와 URL-safe Base64
표준 Base64가 쓰는 + 와 / 는 URL에서 특별한 의미를 가집니다. +는 쿼리 문자열에서 공백으로 해석될 수 있고, /는 경로 구분자입니다. 그래서 RFC 4648은 이 두 글자를 - 와 _ 로 바꾼 'base64url' 변형을 따로 정의합니다. JWT가 바로 이 변형을 쓰며, 패딩 =도 생략합니다.
| 항목 | 표준 Base64 | base64url |
|---|---|---|
| 62번째 문자 | + | - |
| 63번째 문자 | / | _ |
| 패딩 | = 사용 | 보통 생략 |
| 주 용도 | MIME, 데이터 URI, HTTP Basic 인증 | JWT, URL 파라미터, 파일명 |
한글이 깨지는 이유
Base64는 바이트를 다루는 규격이고, 문자를 바이트로 바꾸는 일은 그 앞 단계인 문자 인코딩의 몫입니다. 즉 '안녕'을 Base64로 만들려면 먼저 UTF-8로 인코딩해 바이트 3개짜리 문자 2개, 총 6바이트를 얻고 그 바이트를 Base64로 변환해야 합니다.
브라우저의 옛 API인 btoa()는 이 단계를 건너뛰고 각 문자를 코드포인트 1바이트로 취급하기 때문에, 0~255 범위를 넘는 한글이나 이모지를 넣으면 InvalidCharacterError를 던집니다. 이 도구는 TextEncoder로 UTF-8 바이트를 먼저 만들어 처리하므로 한글, 일본어, 이모지 모두 정상 동작합니다.
반대로 디코딩 결과가 깨진 글자로 나온다면, 원본이 UTF-8이 아닌 EUC-KR이나 CP949로 인코딩된 바이트일 가능성이 높습니다.
// 인코딩
const bytes = new TextEncoder().encode("안녕");
const b64 = btoa(String.fromCharCode(...bytes));
// 디코딩
const raw = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
const text = new TextDecoder().decode(raw);이런 곳에 씁니다
- 데이터 URI: CSS나 HTML에 작은 아이콘을 base64로 임베드해 요청 수를 줄일 때
- HTTP Basic 인증: Authorization 헤더의 'Basic dXNlcjpwYXNz' 부분이 user:pass의 Base64
- JWT: 헤더와 페이로드가 base64url로 인코딩되어 점(.)으로 연결됨
- 이메일 첨부: MIME은 7비트 통로를 전제하므로 바이너리 첨부를 Base64로 변환
- 설정 파일이나 환경변수에 인증서·키 파일 전체를 한 줄로 넣을 때
- 웹훅 서명 검증에서 HMAC 결과를 텍스트로 비교할 때
성능상 주의할 점
이미지를 데이터 URI로 임베드하면 HTTP 요청은 줄지만 얻는 것보다 잃는 것이 클 수 있습니다. 용량이 33% 늘고, 별도 파일이 아니므로 브라우저가 개별 캐싱할 수 없으며, CSS나 HTML 문서 자체가 커져서 첫 렌더링을 지연시킵니다. 경험적으로는 1~2KB 이하의 작은 아이콘 정도가 임베드할 만하고, 그보다 크면 별도 파일로 두고 캐시 헤더를 붙이는 편이 낫습니다.
자주 묻는 질문
- Base64는 암호화인가요?
- 아닙니다. 키가 없고 누구나 즉시 되돌릴 수 있으므로 보안 수단이 전혀 아닙니다. Base64로 감싼 비밀번호는 평문과 같다고 봐야 합니다. 실제로 HTTP Basic 인증이 안전하지 않다고 하는 이유가 이것입니다. 값을 보호해야 한다면 AES 같은 암호화나, 되돌릴 수 없어야 한다면 해시를 써야 합니다.
- 끝에 붙는 = 는 무슨 의미인가요?
- 패딩입니다. 입력 바이트 수가 3의 배수가 아닐 때 마지막 4문자 블록을 채우기 위해 붙습니다. 남은 바이트가 1개면 == 두 개, 2개면 = 한 개가 붙습니다. 일부 구현은 패딩 없이도 디코딩하지만, 엄격한 파서는 패딩을 요구하므로 임의로 지우면 오류가 날 수 있습니다.
- 디코딩했더니 알 수 없는 문자가 나옵니다.
- 세 가지를 확인하세요. 첫째, 원본이 텍스트가 아니라 이미지나 압축 파일 같은 바이너리일 수 있습니다. 둘째, UTF-8이 아닌 EUC-KR 등 다른 문자 인코딩일 수 있습니다. 셋째, 문자열이 base64url 변형인데 표준으로 디코딩했거나 그 반대일 수 있습니다. - 와 _ 가 보이면 base64url입니다.
- 긴 문자열에 줄바꿈이 들어 있어도 되나요?
- MIME 규격(RFC 2045)은 76자마다 줄바꿈을 넣도록 규정하고, 대부분의 디코더는 공백과 줄바꿈을 무시합니다. 다만 JWT처럼 엄격한 문맥에서는 줄바꿈이 들어가면 토큰이 무효가 됩니다.
- Base64가 데이터를 압축하나요?
- 정반대입니다. 3바이트를 4문자로 바꾸므로 약 33% 커집니다. 압축이 필요하면 gzip으로 먼저 압축한 뒤 Base64로 감싸는 순서여야 합니다. 순서를 바꾸면 Base64 출력은 엔트로피가 높아 압축률이 크게 떨어집니다.
- 파일도 인코딩할 수 있나요?
- 이 도구는 텍스트 입력을 다룹니다. 이미지 파일을 데이터 URI로 만들려면 Image to Base64 도구를 쓰는 편이 편합니다. 명령줄에서는 base64 -i file.png (macOS) 또는 base64 -w 0 file.png (Linux)를 쓸 수 있습니다.
💡 참고: 인코딩과 디코딩 모두 브라우저에서 처리되며 입력값은 서버로 전송되지 않습니다. 그래도 실제 운영 환경의 인증 토큰이나 개인정보를 외부 사이트에 붙여넣는 것은 사내 정책상 금지된 경우가 많으니 확인 후 사용하세요.