가이드

Base64 이미지 압축 방법(Gzip이 대개 실패하는 이유)

디코딩한 픽셀을 최적화해 Base64를 줄이는 방법, Gzip이 Data URL 대체가 아닌 이유, JavaScript로 100 KB를 맞추는 과정을 설명합니다.

Base64가 약 33.3% 큰 이유

Base64는 압축이 아니라 3바이트를 4문자로 표현하는 인코딩입니다. 정확한 길이는 4 × ceil(n / 3)이며 Data URL 접두사는 별도입니다.

세 가지 처리 경로

이미지 바이트→Base64는 표준이지만 커집니다. Base64→Gzip→Base64는 두 단계를 되돌려야 합니다. 디코드→이미지 최적화→Base64는 img src용 표준 이미지를 유지합니다.

다시 텍스트로 만들면 Gzip 이점이 줄어드는 이유

중간 Gzip은 작아 보여도 다시 Base64로 만들면 오버헤드와 Gzip 헤더가 붙습니다. 결과도 이미지가 아닌 Gzip 데이터라 바로 표시할 수 없습니다.

이미지 형식은 이미 압축되어 있습니다

JPEG, PNG, WebP는 각자 압축을 사용합니다. 실제 절감은 픽셀, 손실 품질, 메타데이터 또는 형식을 바꿀 때 생깁니다.

올바른 브라우저 JavaScript

Base64와 magic bytes를 확인하고 ImageBitmap으로 디코드한 뒤 제한된 Canvas에 그려 toBlob으로 인코딩합니다. 운영 코드는 크기 제한, 애니메이션 거부, WebP 감지, 최신 작업 보호도 필요합니다.

브라우저 기본 API 예제

async function compressBase64Image(dataUrl, maxWidth = 1600, quality = 0.82) {
  const [header, payload] = dataUrl.split(',', 2);
  const mime = header.match(/^data:([^;]+);base64$/)?.[1];
  if (!mime || !/^image\/(png|jpeg|webp)$/.test(mime)) throw new Error('Unsupported image');

  const binary = atob(payload.replace(/\s+/g, ''));
  const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
  const bitmap = await createImageBitmap(new Blob([bytes], { type: mime }));
  try {
    const scale = Math.min(1, maxWidth / bitmap.width);
    const canvas = document.createElement('canvas');
    canvas.width = Math.max(1, Math.round(bitmap.width * scale));
    canvas.height = Math.max(1, Math.round(bitmap.height * scale));
    const context = canvas.getContext('2d');
    if (!context) throw new Error('Canvas 2D unavailable');
    context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
    const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/webp', quality));
    if (!blob) throw new Error('Encoding failed');
    const output = new Uint8Array(await blob.arrayBuffer());
    let outputBinary = '';
    for (let offset = 0; offset < output.length; offset += 0x8000) {
      outputBinary += String.fromCharCode(...output.subarray(offset, offset + 0x8000));
    }
    return `data:${blob.type};base64,${btoa(outputBinary)}`;
  } finally {
    bitmap.close();
  }
}

100 KB로 줄이기

100 KB를 102,400 이미지 바이트로 봅니다. JPEG/WebP 품질을 제한적으로 이분 탐색하고, 부족하면 sqrt(목표/현재) 비율로 몇 번만 축소합니다.

품질·해상도·형식

품질은 codec을 바꾸고 리사이즈는 픽셀을 없앱니다. 사진은 JPEG/WebP, 투명 그래픽은 PNG/WebP가 흔히 맞으며 실제 결과를 비교해야 합니다.

PNG 투명도와 JPEG 배경

JPEG에는 alpha가 없습니다. PNG/WebP를 유지하거나 JPEG 변환 전에 배경색을 명시적으로 확인해야 합니다.

GIF·애니메이션 WebP·SVG

Canvas는 보통 한 프레임만 잡습니다. GIF, 애니메이션 WebP, ICO는 거부하고 SVG는 전용 텍스트 최적화를 써야 합니다.

로컬 처리의 의미

업로드와 서버 보관은 피하지만 악성 이미지 안전을 증명하지는 않습니다. 기기, 확장 프로그램, browser codec 위험은 남습니다.

Base64를 그만 쓸 때

큰 이미지는 Blob URL, 별도 캐시 파일, Blob/FormData/ArrayBuffer 바이너리 업로드가 더 적합합니다.

자주 묻는 질문

Base64 자체를 압축할 수 있나요?

가능하지만 일반 압축 결과는 바로 쓸 수 있는 이미지 Base64가 아닙니다.

img src에 바로 쓸 수 있나요?

디코딩 결과가 이미지일 때만 가능합니다. Gzip은 먼저 풀어야 합니다.

100 KB로 줄이는 방법은?

품질을 제한적으로 찾고 필요하면 비율을 유지해 줄입니다.

화질이 떨어지나요?

리사이즈와 손실 품질은 화질을 낮출 수 있습니다.

왜 33% 큰가요?

3바이트를 4문자로 표현하기 때문입니다.

업로드되나요?

사이트 압축기는 브라우저에서만 처리합니다.

Gzip은 언제 쓰나요?

이미지 Data URL 내부가 아니라 바깥 HTTP 응답 압축을 사용하세요.

표준 및 브라우저 자료