Base64 인코딩
7개 도구
인코딩이란 무엇인가?
인코딩은 데이터를 한 표현 방식에서 다른 방식으로 변환하는 과정입니다. 웹 개발에서 인코딩은 제한된 문자 집합을 위해 설계된 채널을 통해 데이터를 안전하게 전송하는 데 사용됩니다 — 예를 들어 텍스트 기반 프로토콜로 이진 데이터를 보내거나 URL에 특수 문자를 포함하는 경우입니다.
문자 인코딩은 텍스트 문자가 바이트에 어떻게 매핑되는지 정의합니다. Base64 인코딩은 이진 데이터를 ASCII 텍스트로 변환합니다. URL 인코딩은 임의의 텍스트를 URL에서 안전하게 사용할 수 있도록 만듭니다. 이 세 가지 인코딩 계층을 이해하면 미묘한 버그와 보안 취약점을 예방할 수 있습니다.
문자 인코딩 역사
문자 인코딩은 문자(글자, 기호)와 그 이진 표현 사이의 매핑입니다. ASCII에서 Unicode로의 발전은 웹의 글로벌화를 반영합니다:
7비트 미국 표준 코드. 128개 문자: 영문자, 숫자, 구두점, 제어 코드. 다른 모든 인코딩의 기반이 되는 기초 인코딩을 정의했습니다.
서유럽 언어를 위한 확장 ASCII. 128개 문자 추가(악센트 문자, 기호). 비라틴 문자 체계에는 적합하지 않습니다.
세계의 모든 문자 체계를 포괄하는 범용 문자 집합. 161개 문자 체계에서 149,000개 이상의 문자에 대한 코드 포인트를 정의합니다. 인코딩은 정의하지 않으며 — 그것은 UTF-8/16/32의 역할입니다.
문자당 1~4바이트를 사용하는 가변 폭 Unicode 인코딩. ASCII 호환(처음 128개 코드 포인트는 단일 바이트). 웹에서 지배적인 인코딩 — 전체 웹사이트의 98% 이상이 사용합니다.
문자당 2 또는 4바이트를 사용하는 가변 폭 인코딩. Windows, Java, JavaScript 문자열 내부에서 사용됩니다. ASCII 호환이 아닙니다.
고정 폭 인코딩: 항상 문자당 4바이트. 단순하지만 공간을 낭비합니다. 일부 데이터베이스 내부에서 사용됩니다. 웹에서는 거의 볼 수 없습니다.
UTF-8이 표준이 된 이유
UTF-8은 ASCII와 하위 호환(처음 128개 문자가 동일하게 인코딩됨), 자기 동기화(스캔으로 문자 경계를 찾을 수 있음), 라틴 문자에 대한 공간 효율성 덕분에 지배적인 인코딩이 되었습니다. 모든 ASCII 문서는 유효한 UTF-8 문서입니다. 이 덕분에 마이그레이션이 원활하게 이루어졌습니다.
Base64 인코딩
Base64는 64개의 출력 가능한 ASCII 문자만 사용하여 이진 데이터를 텍스트 표현으로 변환합니다: A-Z, a-z, 0-9, + 및 /. 이진 데이터가 텍스트만 처리하는 채널을 통과해야 할 때 필요합니다 — 이메일 첨부 파일, data URI, JWT 토큰, HTTP Basic Auth 모두 Base64를 사용합니다.
작동 방식
Base64는 입력 바이트를 3바이트(24비트) 단위로 묶고 각 단위를 4개의 Base64 문자(각 6비트)로 인코딩합니다. 입력이 3바이트의 배수가 아닌 경우 마지막 그룹을 완성하기 위해 = 패딩 문자가 추가됩니다:
| 입력 | 16진수 바이트 | Base64 |
|---|---|---|
| "Man" | 77 61 6E | TWFu |
| "Ma" | 4D 61 | TWE= |
| "M" | 4D | TQ== |
끝에 오는 = 패딩 문자는 마지막 3바이트 그룹을 완성하기 위해 몇 바이트가 부족했는지를 나타냅니다. = 하나는 패딩 1바이트가 필요했음을, ==는 2바이트가 필요했음을 의미합니다. 표준 Base64는 항상 길이가 4의 배수인 출력을 생성합니다.
URL 인코딩
URL은 제한된 범위의 안전한 ASCII 문자만 포함할 수 있습니다. 그 범위를 벗어나는 문자 — 공백, 구두점, 비ASCII 문자, &, =, # 같은 특수 URL 문자 포함 — 는 URL에 삽입하기 전에 퍼센트 인코딩(URL 인코딩)을 적용해야 합니다.
퍼센트 인코딩은 각 안전하지 않은 바이트를 % 뒤에 해당 바이트 값을 나타내는 두 자리 16진수로 대체합니다. 공백은 %20, 앰퍼샌드는 %26이 되는 식입니다.
자주 인코딩되는 문자
| 문자 | 인코딩됨 | 참고 |
|---|---|---|
| Space | %20 | 가장 흔한 문자; application/x-www-form-urlencoded에서 폼 제출 시 +로 사용됨 |
| & | %26 | 쿼리 문자열 구분자; 리터럴 값으로 사용할 때는 인코딩 필요 |
| = | %3D | 쿼리 문자열의 키-값 구분자; 데이터로 사용할 때는 인코딩 필요 |
| + | %2B | application/x-www-form-urlencoded에서 공백으로 해석됨; 리터럴 +를 유지하려면 인코딩 필요 |
| # | %23 | 프래그먼트 식별자; 경로나 쿼리에서 리터럴 데이터로 사용할 때는 인코딩 필요 |
| / | %2F | 경로 세그먼트 구분자; 경로 구분자가 아닌 리터럴 데이터로 사용할 때는 인코딩 필요 |
| : | %3A | 스킴 구분자; 경로 및 쿼리 컨텍스트에서 인코딩 필요 |
| @ | %40 | mailto: 및 인증에서 사용; 리터럴 데이터로 사용할 때는 인코딩 필요 |
encodeURI vs encodeURIComponent
JavaScript는 범위가 다른 두 가지 인코딩 함수를 제공합니다. encodeURI는 전체 URL을 인코딩하며 URL에서 의미를 가지는 문자(:, /, ?, #, @)는 인코딩하지 않습니다. encodeURIComponent는 URL 구성 요소(단일 쿼리 파라미터 값 또는 경로 세그먼트)를 인코딩하며 A-Z, a-z, 0-9, -, _, ., ~ 를 제외한 모든 문자를 인코딩합니다. 개별 값에는 항상 encodeURIComponent를, 전체 URL에는 encodeURI를 사용하세요.
웹 개발에서 인코딩이 사용되는 곳
Authorization: Basic 헤더는 자격 증명을 Base64(사용자명:비밀번호)로 인코딩합니다. 이는 전송 편의를 위한 인코딩으로, 보안이 아닙니다 — Base64는 누구나 쉽게 되돌릴 수 있습니다. Basic Auth와 함께 반드시 HTTPS를 사용하세요.
Data URI는 파일 내용을 HTML 또는 CSS에 직접 삽입합니다: data:image/png;base64,.... 이미지와 폰트를 인라인으로 Base64 인코딩하면 HTTP 요청을 줄일 수 있지만 문서 크기가 증가합니다(약 33% 오버헤드).
이메일은 7비트 ASCII를 위해 설계되었습니다. 이진 첨부 파일(이미지, PDF)은 MIME에 의해 전송 전 Base64로 인코딩됩니다. 이메일 클라이언트는 이메일을 표시할 때 투명하게 디코딩합니다.
JWT 토큰은 세 부분(헤더, 페이로드, 서명) 모두에 Base64url 인코딩(+를 -로, /를 _로 대체하고 패딩을 생략하는 변형)을 사용합니다. 이를 통해 추가 퍼센트 인코딩 없이 토큰을 URL에서 안전하게 사용할 수 있습니다.
URL 쿼리 문자열의 사용자 제공 데이터는 반드시 퍼센트 인코딩해야 합니다. 값에서 & 또는 =를 인코딩하지 않으면 쿼리 문자열 파싱이 조용히 손상됩니다. 개별 값에는 항상 encodeURIComponent를 사용하세요.
비ASCII 도메인(예: münchen.de)은 DNS 시스템과의 호환성을 위해 Punycode(xn-- 접두사)로 인코딩됩니다. 브라우저는 Unicode 형식을 표시하지만 DNS 리졸버에는 Punycode 형식을 전송합니다.
자주 묻는 질문
아닙니다. Base64는 인코딩이지 암호화가 아닙니다. 비밀 키가 없는 가역적 변환입니다. Base64 문자열을 보는 누구나 즉시 디코딩할 수 있습니다. Base64를 보안 수단으로 사용하지 마세요.
Base64는 입력을 3바이트 단위로 처리합니다. 입력이 3바이트의 배수가 아닌 경우 마지막 그룹을 완성하기 위해 = 패딩이 추가됩니다. = 하나는 패딩 1바이트, ==는 패딩 2바이트를 의미합니다. 일부 구현은 패딩을 생략합니다(JWT용 Base64url).
Base64url은 Base64의 URL 안전 변형으로 +를 -로, /를 _로 대체하고 일반적으로 = 패딩을 생략합니다. 이를 통해 퍼센트 인코딩 없이 URL과 HTTP 헤더에서 안전하게 사용할 수 있습니다. JWT는 세 부분 모두에 Base64url을 사용합니다.
개별 값(쿼리 파라미터 값, 경로 세그먼트 값)에는 encodeURIComponent를 사용하세요. URL 구조 문자(/, :, ?, #)를 유지하면서 전체 URL 문자열을 인코딩할 때는 encodeURI를 사용하세요. 잘 모르겠다면 encodeURIComponent를 사용하세요.
UTF-8은 ASCII 호환이며 라틴 문자에 대해 공간 효율적입니다(대부분의 URL, HTML 태그, 코드는 ASCII입니다). UTF-16은 ASCII 콘텐츠에 공간을 낭비하고 하위 호환이 되지 않습니다. HTTP와 HTML은 기본적으로 UTF-8을 사용합니다.
퍼센트 인코딩(URL 인코딩)은 문자를 % 뒤에 두 자리 16진수 바이트 값으로 표현합니다. 예를 들어 공백은 %20(십진수 32, 16진수 20)입니다. 멀티바이트 UTF-8 문자는 각 바이트를 개별적으로 인코딩합니다: é는 %C3%A9가 됩니다.
Base64는 데이터 크기를 약 33% 증가시키고 인코딩/디코딩 CPU 오버헤드를 추가합니다. 처리량이 높은 시스템에서는 바이너리 프로토콜을 선호하세요. URL 인코딩은 오버헤드가 최소하지만 특수 문자가 많을 경우 URL이 상당히 길어질 수 있습니다.
Punycode는 ASCII 호환 DNS 시스템에서 Unicode 문자를 표현하기 위한 인코딩입니다. münchen.de와 같은 국제화 도메인 이름은 DNS 쿼리에서 xn--mnchen-3ya.de로 인코딩됩니다. 브라우저는 Unicode 형식을 표시하지만 내부적으로는 Punycode를 사용합니다.