[코드] 6분 읽기

[코드] JavaScript에서 Base64

모던 브라우저 API를 사용한 Base64 인코딩 및 디코딩을 위한 실용적인 JavaScript 예제.

2025년 1월 | development

// 기본 인코딩 & 디코딩

JavaScript는 Base64 작업을 위한 내장 함수를 제공합니다. btoa()와 atob() 함수가 기본적인 인코딩과 디코딩을 처리합니다:

이 함수들은 ASCII 텍스트에서는 잘 작동하지만, 유니코드 문자에는 특별한 처리가 필요합니다.

// 기본 Base64 인코딩
const text = '안녕 세상';
const encoded = btoa(text);
console.log(encoded); // 7JWI64WVIOyEuOyDgQ==

// 기본 Base64 디코딩
const decoded = atob(encoded);
console.log(decoded); // 안녕 세상

// 문자열이 유효한 Base64인지 확인
function isValidBase64(str) {
    try {
        return btoa(atob(str)) === str;
    } catch (err) {
        return false;
    }
}

// 유니코드 지원

유니코드 문자열의 경우, 먼저 UTF-8 바이트로 변환해야 합니다. 모던 브라우저는 TextEncoder와 TextDecoder API를 제공합니다:

이 접근법은 이모지, 국제 문자, 그리고 다른 유니코드 콘텐츠를 올바르게 처리합니다.

// 유니코드 안전한 Base64 인코딩
function encodeBase64(str) {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(str);
    const binary = String.fromCharCode(...bytes);
    return btoa(binary);
}

// 유니코드 안전한 Base64 디코딩
function decodeBase64(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
    }
    const decoder = new TextDecoder();
    return decoder.decode(bytes);
}

// 이모지와 함께하는 예제
const emoji = '안녕 👋 세상 🌍';
const encoded = encodeBase64(emoji);
const decoded = decodeBase64(encoded);
console.log(decoded); // 안녕 👋 세상 🌍

// 파일 처리

파일을 Base64로 변환하는 것은 업로드와 데이터 URI에서 일반적입니다. 클라이언트 측 파일 처리를 위해 FileReader API를 사용하세요:

이것은 HTML에 직접 임베드되거나 API로 전송될 수 있는 데이터 URI를 생성합니다.

// 파일을 Base64 데이터 URI로 변환
function fileToBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = error => reject(error);
        reader.readAsDataURL(file);
    });
}

// 파일 입력과 함께 사용
document.getElementById('fileInput').addEventListener('change', async (e) => {
    const file = e.target.files[0];
    if (file) {
        try {
            const base64 = await fileToBase64(file);
            console.log(base64);
            // 결과: data:image/png;base64,iVBORw0KGgoAAAA...
        } catch (error) {
            console.error('오류:', error);
        }
    }
});

// URL 안전 BASE64

표준 Base64는 URL 인코딩이 필요한 +와 / 문자를 사용합니다. URL 안전 Base64는 이들을 -와 _로 바꿉니다:

이것은 토큰, ID, 그리고 URL을 통해 전송되는 다른 데이터에 필수적입니다.

// URL 안전 Base64로 변환
function toUrlSafeBase64(base64) {
    return base64
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
}

// URL 안전 Base64에서 변환
function fromUrlSafeBase64(urlSafeBase64) {
    let base64 = urlSafeBase64
        .replace(/-/g, '+')
        .replace(/_/g, '/');
    
    // 필요한 경우 패딩 추가
    const padding = 4 - (base64.length % 4);
    if (padding !== 4) {
        base64 += '='.repeat(padding);
    }
    
    return base64;
}

// 예제
const text = 'URL 안전 인코딩 테스트';
const encoded = btoa(text);
const urlSafe = toUrlSafeBase64(encoded);
console.log(urlSafe); // VVJMIOyViOyghCDsnbjsvaztlZjrgrQg7YW066Ow7Yq4

// 대용량 데이터 스트리밍

대용량 파일이나 데이터 스트림의 경우, 메모리 문제를 피하기 위해 청크 단위로 처리하세요:

이 접근법은 브라우저 프리징을 방지하고 대용량 데이터셋을 효율적으로 처리합니다.

// 스트림 기반 Base64 인코딩
class Base64Encoder {
    constructor() {
        this.buffer = '';
    }
    
    encode(chunk) {
        this.buffer += chunk;
        const completeBytes = Math.floor(this.buffer.length / 3) * 3;
        const toEncode = this.buffer.slice(0, completeBytes);
        this.buffer = this.buffer.slice(completeBytes);
        
        return btoa(toEncode);
    }
    
    flush() {
        if (this.buffer.length > 0) {
            const result = btoa(this.buffer);
            this.buffer = '';
            return result;
        }
        return '';
    }
}

// 사용법
const encoder = new Base64Encoder();
let result = '';
result += encoder.encode('첫 번째 청크');
result += encoder.encode(' 두 번째 청크');
result += encoder.flush();
console.log(result);

// 오류 처리

Base64 작업에 대해서는 항상 적절한 오류 처리를 구현하세요:

견고한 오류 처리는 애플리케이션 크래시를 방지하고 더 나은 사용자 경험을 제공합니다.

// 오류 처리가 포함된 안전한 Base64 작업
function safeEncode(input) {
    try {
        if (typeof input !== 'string') {
            throw new Error('입력은 문자열이어야 합니다');
        }
        return btoa(input);
    } catch (error) {
        console.error('인코딩 오류:', error.message);
        return null;
    }
}

function safeDecode(base64) {
    try {
        // Base64 형식 검증
        if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64)) {
            throw new Error('유효하지 않은 Base64 형식');
        }
        return atob(base64);
    } catch (error) {
        console.error('디코딩 오류:', error.message);
        return null;
    }
}

// 사용법
const encoded = safeEncode('안녕 세상');
const decoded = safeDecode(encoded);
if (encoded && decoded) {
    console.log('성공:', decoded);
}