[가이드] 11 min read

[가이드] 브라우저에서 Base64 인코딩된 이미지 미리보기 방법

페이지에 Base64로 인코딩된 이미지를 표시하는 검증된 다섯 가지 방법 — 직접 src 할당, FileReader, canvas, blob 변환, 그리고 프레임워크별 패턴. 디버깅 팁 포함.

April 2026 | javascript

// 30초 만에 답하기

Base64로 인코딩된 이미지 문자열을 가지고 있고 그저 페이지에 보여주고 싶다면, 가장 짧은 길은 data: URI로 img.src에 할당하는 것입니다. JavaScript 세 줄이면 끝납니다.

const img = new Image();
img.src = 'data:image/png;base64,' + base64String;
document.body.appendChild(img);

// If the string already starts with 'data:image/...;base64,'
// — just assign it directly without prepending anything:
img.src = base64StringWithDataUriPrefix;

// 방법 1: img.src — 직접적인 접근

모든 <img> 엘리먼트는 데이터 URI를 일반 URL과 동일하게 취급합니다. URI를 src에 할당하면 브라우저가 디코딩, 형식 감지, 렌더링을 알아서 처리해 줍니다.

이 방법은 브라우저가 기본 지원하는 모든 이미지 형식에 작동합니다: PNG, JPEG, GIF(애니메이션 및 정적), WebP, SVG, BMP, ICO. Base64 문자열은 어디서든 올 수 있습니다 — API 응답, localStorage, 클립보드 붙여넣기, 데이터베이스 행 등.

데이터 URI의 MIME 타입은 오래된 브라우저와 일부 임베디드 WebView에서 중요합니다. 원시 Base64 페이로드만 가지고 있고(data: 접두사 없음) 형식을 알지 못한다면, 매직 바이트를 탐색하거나(방법 5 참조) image/png를 기본값으로 둘 수 있습니다. 대부분의 브라우저는 MIME 타입이 잘못되어도 스니핑하여 복구합니다.

// Plain HTML — no JavaScript needed if the string is static
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." alt="preview">

// Dynamic JavaScript — load from a variable, fetch, or input field
function renderBase64Image(base64, mimeType = 'image/png', containerId = 'preview') {
  const container = document.getElementById(containerId);
  const img = new Image();

  img.onload = () => {
    container.innerHTML = '';
    container.appendChild(img);
  };

  img.onerror = () => {
    container.textContent = 'Invalid image data';
  };

  // Tolerate both raw Base64 and full data: URIs
  img.src = base64.startsWith('data:')
    ? base64
    : `data:${mimeType};base64,${base64}`;
}

renderBase64Image(myBase64String, 'image/jpeg');

// 방법 2: 사용자 업로드 파일을 위한 FILEREADER

사용자가 <input type="file">를 통해 이미지를 업로드한다면, 직접 Base64 인코딩할 필요가 없습니다. FileReader.readAsDataURL()img.src에 바로 넣을 수 있는 완전한 데이터 URI를 반환해 줍니다.

<input type="file" id="fileInput" accept="image/*">
<img id="preview" alt="">

<script>
document.getElementById('fileInput').addEventListener('change', (e) => {
  const file = e.target.files[0];
  if (!file) return;

  const reader = new FileReader();

  reader.onload = (event) => {
    // event.target.result is a complete data URI
    // e.g. 'data:image/png;base64,iVBORw0KGgo...'
    document.getElementById('preview').src = event.target.result;
  };

  reader.onerror = () => {
    console.error('FileReader failed:', reader.error);
  };

  reader.readAsDataURL(file);
});
</script>

// 방법 3: 생성되거나 수정된 이미지를 위한 CANVAS

<canvas>에서 이미지를 그리거나 처리할 때, canvas.toDataURL()은 한 번의 호출로 결과를 데이터 URI로 내보냅니다. 차트 스크린샷, 서명 패드, QR 코드, 워터마크 등 클라이언트 측에서 생성하여 이미지로 표시하거나 다운로드하려는 모든 것에 유용합니다.

// 1. Draw on canvas
const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#00FF41';
ctx.fillRect(0, 0, 200, 200);
ctx.fillStyle = '#000';
ctx.font = '20px monospace';
ctx.fillText('Hello, Base64!', 20, 100);

// 2. Export to data URI
const pngDataURL = canvas.toDataURL('image/png');
// pngDataURL === 'data:image/png;base64,iVBORw0KGgo...'

// 3. Display directly
const img = new Image();
img.src = pngDataURL;
document.body.appendChild(img);

// JPEG with quality control (0–1)
const jpegDataURL = canvas.toDataURL('image/jpeg', 0.85);

// WebP if supported
const webpDataURL = canvas.toDataURL('image/webp', 0.9);

// 방법 4: 원격 BASE64 페이로드를 위한 FETCH + BLOB

Base64 페이로드가 크거나(50 KB 초과) 여러 번 표시할 예정이라면, URL.createObjectURL()을 통해 Blob URL로 변환하는 것이 전체 데이터 URI를 DOM에 유지하는 것보다 메모리 효율이 더 좋습니다. blob URL은 짧고(blob:https://example.com/12345-abcde), 더 이상 필요 없을 때 해제할 수 있으며, DOM 문자열에서 33%의 Base64 오버헤드를 회피합니다.

// Convert a Base64 string to a Blob, then to a blob: URL
function base64ToBlobURL(base64, mimeType = 'image/png') {
  // Strip the data: prefix if present
  const payload = base64.includes(',') ? base64.split(',')[1] : base64;

  // Decode Base64 to binary string
  const binary = atob(payload);

  // Convert binary string to Uint8Array
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }

  // Wrap in a Blob and create an object URL
  const blob = new Blob([bytes], { type: mimeType });
  return URL.createObjectURL(blob);
}

// Usage
const blobURL = base64ToBlobURL(myBase64, 'image/jpeg');
const img = new Image();
img.src = blobURL;
document.body.appendChild(img);

// IMPORTANT: free the memory when you're done with the image
img.onload = () => URL.revokeObjectURL(blobURL);

// 방법 5: 매직 바이트로 형식 자동 감지

원시 Base64 페이로드(data: 접두사 없음)를 받았는데 PNG, JPEG, GIF, WebP인지 알 수 없다면, 디코딩된 첫 바이트들을 살펴보세요. 모든 형식에는 고유한 시그니처가 있습니다.

function detectImageFormat(base64) {
  // Look at the first ~12 characters of the Base64 string
  const head = base64.replace(/^data:[^,]+,/, '').slice(0, 16);

  if (head.startsWith('iVBORw0KGgo')) return 'image/png';
  if (head.startsWith('/9j/'))         return 'image/jpeg';
  if (head.startsWith('R0lGOD'))       return 'image/gif';
  if (head.startsWith('UklGR'))        return 'image/webp';
  if (head.startsWith('PHN2Zy') ||
      head.startsWith('PD94bWw'))      return 'image/svg+xml';
  if (head.startsWith('Qk'))           return 'image/bmp';
  if (head.startsWith('AAABAA'))       return 'image/x-icon';

  return 'application/octet-stream'; // unknown
}

// Build a correct data URI even when the user only gave us the payload
function toDataURI(base64) {
  if (base64.startsWith('data:')) return base64; // already complete
  const mime = detectImageFormat(base64);
  return `data:${mime};base64,${base64}`;
}

// REACT — 컴포넌트에서 BASE64 이미지 표시하기

React에서는 데이터 URI를 다른 문자열 prop과 동일하게 다루세요. 입력이 거의 변하지 않는다면 변환을 메모이제이션하세요.

import { useMemo } from 'react';

function Base64Image({ base64, mimeType = 'image/png', alt }) {
  const src = useMemo(() => {
    if (!base64) return '';
    return base64.startsWith('data:')
      ? base64
      : `data:${mimeType};base64,${base64}`;
  }, [base64, mimeType]);

  if (!src) return <div>No image</div>;
  return <img src={src} alt={alt} loading="lazy" />;
}

// Usage
<Base64Image
  base64={apiResponse.avatar}
  mimeType="image/jpeg"
  alt="User avatar"
/>

// For large images, prefer blob URLs (Method 4) and useEffect for cleanup:
function LargeBase64Image({ base64 }) {
  const [blobURL, setBlobURL] = useState('');

  useEffect(() => {
    const url = base64ToBlobURL(base64, 'image/png');
    setBlobURL(url);
    return () => URL.revokeObjectURL(url); // cleanup on unmount
  }, [base64]);

  return blobURL ? <img src={blobURL} alt="" /> : null;
}

// VUE 3 — COMPOSITION API 예제

동일한 아이디어, Vue 문법입니다.

<script setup>
import { computed } from 'vue';

const props = defineProps({
  base64: String,
  mimeType: { type: String, default: 'image/png' },
  alt: { type: String, default: '' }
});

const src = computed(() => {
  if (!props.base64) return '';
  return props.base64.startsWith('data:')
    ? props.base64
    : `data:${props.mimeType};base64,${props.base64}`;
});
</script>

<template>
  <img v-if="src" :src="src" :alt="alt" loading="lazy" />
  <div v-else>No image</div>
</template>

// SVELTE — 반응형 구문

<script>
  export let base64 = '';
  export let mimeType = 'image/png';
  export let alt = '';

  $: src = base64
    ? (base64.startsWith('data:') ? base64 : `data:${mimeType};base64,${base64}`)
    : '';
</script>

{#if src}
  <img {src} {alt} loading="lazy" />
{:else}
  <div>No image</div>
{/if}

// 에러 처리 — 무엇이 깨지고, 왜 그런가

이미지가 깨진 아이콘 플레이스홀더로 로드된다. Base64 페이로드가 잘렸거나, 문자열 중간에 공백이 있거나, 표준 데이터 URI 안에 URL-안전 문자(-_)가 사용된 경우입니다. str.replace(/\s+/g, '')로 공백을 제거하고, str.replace(/-/g, '+').replace(/_/g, '/')로 URL-안전 형식을 표준으로 변환하며, 길이가 올바른지 확인하세요(Base64 길이는 적절한 = 패딩과 함께 4의 배수여야 합니다).

이미지가 렌더링되지만 형식이 잘못되었다. data:image/png;base64, 접두사는 PNG라고 말하지만 실제 바이트는 JPEG입니다. 브라우저는 매직 바이트를 스니핑하여 어쨌든 올바르게 렌더링하지만, 일부 헤드리스 브라우저와 임베디드 WebView는 그렇지 않습니다. 방법 5를 사용해 바이트로부터 실제 형식을 감지하세요.

이미지가 DOM에서 거대하고 페이지가 느리게 느껴진다. React 컴포넌트의 1 MB Base64 문자열은 모든 상태 업데이트마다 다시 렌더링됩니다. blob: URL(방법 4)로 전환하세요. 이미지 크기와 무관하게 DOM에 50자 문자열로 존재합니다.

Chrome에서는 작동하지만 Safari에서는 안 된다. Safari는 더 엄격한 CSP 기본값을 가지며 일부 컨텍스트(<a download>, 서비스 워커 페치)에서 data: URI를 거부합니다. Safari에서 테스트하고 필요하다면 blob: URL로 폴백하세요.

Base64 문자열 자체가 유효하지 않다. 받은 'Base64'가 이중 인코딩되었거나, 16진 인코딩되었거나, 단순히 손상되었을 수 있습니다. 코드를 디버깅하기 전에 먼저 우리의 Base64 to Image 디코더에 붙여넣어 유효한지 확인하세요.

// 성능: data: URI vs blob: URL — 빠른 벤치마크

<img>에 표시되는 500 KB JPEG의 경우:

src의 data: URI: DOM에 668 KB 문자열(33% Base64 오버헤드). 브라우저가 URI를 파싱하고, Base64를 디코딩한 뒤, JPEG를 디코딩합니다. 중급 노트북 기준 첫 페인트 약 80 ms.
URL.createObjectURL을 통한 blob: URL: DOM에 56자 문자열. 브라우저가 메모리 내 blob을 페치하고 JPEG를 디코딩합니다. 첫 페인트 약 40 ms. 메모리 약 50% 절약.

5 KB 아이콘의 경우 차이는 보이지 않습니다 — 둘 다 5 ms 미만에 렌더링됩니다. 핵심: 약 50 KB 미만이라면 편한 쪽을 사용하세요. 그 이상이라면 blob URL을 선호하세요.

// 미리보기를 격리해서 테스트하기

버그가 의심된다면 먼저 애플리케이션 코드에서 Base64 문자열을 격리하세요. 빠른 세 가지 점검:

1. 우리 온라인 도구에 붙여넣기. 문자열을 Base64 to Image 변환기에 넣어보세요. 거기서 이미지가 올바르게 렌더링된다면 데이터는 정상이고 버그는 코드에 있습니다. 거기서도 렌더링되지 않는다면 데이터가 상류에서 손상된 것입니다.

2. 새 탭에서 URL로 열기. 데이터 URI(data:image/png;base64, 접두사 포함)를 복사하고, 새 브라우저 탭에 붙여넣은 뒤 Enter를 누르세요. 최신 브라우저는 이미지를 직접 표시합니다. (일부는 주소창에서 data:를 차단하므로, 그렇다면 1단계의 인라인 테스트를 사용하세요.)

3. 길이 확인. 유효한 Base64 문자열은 길이가 4로 나누어떨어집니다(공백 제거 후). 패딩(=)이 차이를 채웁니다: 끝에 0~2개의 = 문자. 길이가 4로 나누어떨어지지 않으면 문자열이 잘리거나 늘어난 것입니다.

// Quick validation snippet
function looksLikeValidBase64(str) {
  const cleaned = str.replace(/^data:[^,]+,/, '').replace(/\s+/g, '');
  if (cleaned.length === 0) return false;
  if (cleaned.length % 4 !== 0) return false;
  if (!/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) return false;
  return true;
}

// 30초 치트 시트

가장 빠른 방법: 데이터 URI를 img.src에 할당. 끝.

파일 업로드에서: FileReader.readAsDataURL(file).

canvas에서: canvas.toDataURL('image/png').

크거나 반복되는 이미지의 경우: blob으로 변환하고 URL.createObjectURL(blob) 사용.

형식을 알 수 없는 경우: 데이터 URI를 만들기 전에 매직 바이트(iVBORw0KGgo = PNG, /9j/ = JPEG 등)를 스니핑하세요.

디버깅: 먼저 Base64 to Image 변환기에 붙여넣으세요. 데이터가 문제인지 아닌지 가려냅니다.

관련 읽을거리: 데이터 URI 이해하기 · Base64 이미지 임베딩 · JavaScript의 Base64: 브라우저와 Node