URL-Safe Base64 인코딩 완벽 가이드
URL에서 안전하게 사용할 수 있는 Base64 변형과 구현 방법을 알아봅니다.
31분 읽기
표준 Base64의 문제점
표준 Base64는 +, /, = 문자를 사용하는데, 이는 URL에서 특별한 의미를 가집니다.
URL에서 문제가 되는 문자
+: 공백(space)으로 해석될 수 있음/: 경로 구분자로 인식됨=: 쿼리 파라미터 구분자로 사용됨
실제 문제 예시
// 표준 Base64 인코딩 const data = btoa('Hello+World/Test'); console.log(data); // "SGVsbG8rV29ybGQvVGVzdA==" // URL에 포함 const url = `https://example.com/api?data=${data}`; // → https://example.com/api?data=SGVsbG8rV29ybGQvVGVzdA== // 문제: +와 /가 잘못 해석될 수 있음 // 서버에서 받은 데이터: "SGVsbG8 V29ybGQvVGVzdA==" (+ → 공백)
URL-Safe Base64란?
URL-Safe Base64는 이러한 문제를 해결하기 위해 문자를 치환합니다:
| 표준 Base64 | URL-Safe Base64 | 설명 |
|---|---|---|
+ | - | 하이픈으로 대체 |
/ | _ | 언더스코어로 대체 |
= | 제거 또는 유지 | 선택적 패딩 |
JavaScript 구현
표준 → URL-Safe 변환
function toUrlSafeBase64(base64) { return base64 .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); // 끝의 패딩 제거 } // 사용 예 const standard = btoa('Hello+World/Test'); const urlSafe = toUrlSafeBase64(standard); console.log(standard); // "SGVsbG8rV29ybGQvVGVzdA==" console.log(urlSafe); // "SGVsbG8rV29ybGQvVGVzdA"
URL-Safe → 표준 변환
function fromUrlSafeBase64(urlSafe) { // 문자 복원 let base64 = urlSafe .replace(/-/g, '+') .replace(/_/g, '/'); // 패딩 복원 (Base64는 4의 배수 길이여야 함) const padLength = (4 - (base64.length % 4)) % 4; base64 += '='.repeat(padLength); return base64; } // 사용 예 const urlSafe = 'SGVsbG8rV29ybGQvVGVzdA'; const standard = fromUrlSafeBase64(urlSafe); console.log(standard); // "SGVsbG8rV29ybGQvVGVzdA=="
완전한 인코딩/디코딩 함수
// URL-Safe Base64 인코딩 function encodeUrlSafe(str) { const base64 = btoa(unescape(encodeURIComponent(str))); return base64 .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); } // URL-Safe Base64 디코딩 function decodeUrlSafe(urlSafe) { // 표준 Base64로 복원 let base64 = urlSafe .replace(/-/g, '+') .replace(/_/g, '/'); // 패딩 추가 while (base64.length % 4) { base64 += '='; } // 디코딩 return decodeURIComponent(escape(atob(base64))); } // 테스트 const original = 'Hello+World/Test=123'; const encoded = encodeUrlSafe(original); const decoded = decodeUrlSafe(encoded); console.log('원본:', original); console.log('인코딩:', encoded); console.log('디코딩:', decoded);
Node.js에서 사용하기
Node.js의 Buffer는 URL-Safe Base64를 기본 지원합니다:
// URL-Safe Base64 인코딩 const buffer = Buffer.from('Hello+World/Test'); const urlSafe = buffer.toString('base64url'); console.log(urlSafe); // "SGVsbG8rV29ybGQvVGVzdA" // URL-Safe Base64 디코딩 const decoded = Buffer.from(urlSafe, 'base64url').toString('utf-8'); console.log(decoded); // "Hello+World/Test"
실제 사용 사례
1. JWT (JSON Web Token)
JWT는 헤더와 페이로드를 URL-Safe Base64로 인코딩합니다:
// JWT 구조: header.payload.signature const header = { "alg": "HS256", "typ": "JWT" }; const payload = { "sub": "1234567890", "name": "홍길동", "iat": 1516239022 }; // URL-Safe Base64 인코딩 const encodedHeader = encodeUrlSafe(JSON.stringify(header)); const encodedPayload = encodeUrlSafe(JSON.stringify(payload)); console.log(`${encodedHeader}.${encodedPayload}.signature`); // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Iuq5gOu2uOuPmSIsImlhdCI6MTUxNjIzOTAyMn0.signature
2. OAuth State 파라미터
OAuth 인증 시 state 파라미터로 정보를 안전하게 전달:
const state = { returnUrl: '/dashboard', timestamp: Date.now() }; const encodedState = encodeUrlSafe(JSON.stringify(state)); const authUrl = `https://oauth.example.com/authorize?state=${encodedState}`; console.log(authUrl); // https://oauth.example.com/authorize?state=eyJyZXR1cm5VcmwiOiIvZGFzaGJvYXJkIiwidGltZXN0YW1wIjoxNzA5NjM...
3. URL 파라미터로 JSON 전달
const filters = { category: 'electronics', price: { min: 10000, max: 50000 }, tags: ['sale', 'new'] }; const encodedFilters = encodeUrlSafe(JSON.stringify(filters)); const url = `/products?filters=${encodedFilters}`; console.log(url); // /products?filters=eyJjYXRlZ29yeSI6ImVsZWN0cm9uaWNzIiwicHJpY2UiOnsibWluIjoxMDAwMCwibWF4Ijo1MDAwMH0sInRhZ3MiOlsic2FsZSIsIm5ldyJdfQ
4. 파일명 인코딩
// 특수문자가 포함된 파일명을 URL에 안전하게 포함 const fileName = '리포트_2024/02/15.pdf'; const safeFileName = encodeUrlSafe(fileName); const downloadUrl = `/download/${safeFileName}`; console.log(downloadUrl); // /download/66msCO2MqO2KuF8yMDI0LzAyLzE1LnBkZg
패딩(=) 처리 전략
전략 1: 패딩 제거 (권장)
// 대부분의 경우 패딩 제거가 안전 const encoded = base64.replace(/=+$/, '');
장점: URL이 더 짧아짐 단점: 디코딩 시 패딩 복원 필요
전략 2: 패딩 유지
// 패딩을 그대로 유지 const encoded = base64 .replace(/\+/g, '-') .replace(/\//g, '_'); // =는 그대로
장점: 디코딩이 간단
단점: URL에 =가 남아 있음
주의사항
1. 양방향 호환성
표준 Base64와 URL-Safe Base64를 혼용하지 마세요:
// ❌ 나쁜 예 const encoded = toUrlSafeBase64(btoa(data)); const decoded = atob(encoded); // 에러! URL-Safe를 표준으로 디코딩 // ✅ 좋은 예 const encoded = toUrlSafeBase64(btoa(data)); const decoded = atob(fromUrlSafeBase64(encoded));
2. 라이브러리 일관성
프로젝트 전체에서 동일한 URL-Safe 구현을 사용하세요:
// 팀에서 합의한 유틸리티 함수 사용 import { encodeUrlSafe, decodeUrlSafe } from './utils/base64';
3. 패딩 정책 통일
패딩 제거 여부를 프로젝트 전체에서 일관되게 유지:
// 설정 파일에 명시 const BASE64_CONFIG = { urlSafe: true, removePadding: true };
성능 비교
// 벤치마크 const testData = '가'.repeat(1000); console.time('Standard Base64'); for (let i = 0; i < 10000; i++) { btoa(testData); } console.timeEnd('Standard Base64'); // Standard Base64: ~50ms console.time('URL-Safe Base64'); for (let i = 0; i < 10000; i++) { toUrlSafeBase64(btoa(testData)); } console.timeEnd('URL-Safe Base64'); // URL-Safe Base64: ~55ms (약 10% 느림)
성능 차이가 미미하므로 대부분의 경우 걱정할 필요 없습니다.
마치며
URL-Safe Base64는 다음과 같은 경우에 필수적입니다:
- ✅ JWT 토큰
- ✅ OAuth state 파라미터
- ✅ URL 파라미터로 데이터 전달
- ✅ 파일명에 특수문자 포함 시
표준 Base64와 URL-Safe Base64의 차이를 이해하고, 상황에 맞게 올바르게 사용하세요!
Base64 변환이 필요하다면 Base64.kr 변환기를 사용해보세요.