파일 업로드
  1. base64hub
  2. Base64 가이드
  3. Data URI 성능
성능

Data URI 성능: Base64 인라인이 이득일 때

파일을 Base64 data URI로 인라인하면 요청 하나를 아끼는 대신 바이트, 캐싱, 때로는 렌더링 시간을 치릅니다. 압축은 이 계산을 바꾸는데, SVG에서는 다르게 바꿉니다.

data URI의 비용

data URI는 파일을 URL 안에 넣습니다: data:image/png;base64,iVBORw0KGgo.... URL이 들어가는 곳이면 어디든 쓸 수 있으므로, 아이콘을 HTML src 속성이나 CSS background-image 안에 둘 수 있고 브라우저는 그것을 위해 따로 요청을 보내지 않습니다.

맞바꾸는 것은 다음과 같습니다.

  • 크기. Base64는 페이로드를 원본의 4/3, 약 33% 크게 만듭니다(정확한 공식은 Base64 크기 오버헤드). data:image/png;base64, 접두사가 22바이트를 더합니다.
  • 캐싱. 인라인된 파일은 그것을 담은 문서의 일부로만 캐시됩니다. 모든 HTML 페이지에 인라인한 로고는 페이지마다 다시 내려받습니다. CSS 파일에 인라인하면 그 CSS와 함께 캐시되지만, 아이콘 하나를 바꿔도 스타일시트 전체가 무효화됩니다.
  • 렌더링. 브라우저는 렌더링을 차단하는 CSS가 도착할 때까지 아무것도 그리지 못합니다. 인라인 글꼴이나 이미지 200 KB를 실은 스타일시트는 그 바이트를 내려받는 시간만큼 첫 렌더링을 늦춥니다. HTML에서는 인라인 데이터가 그 뒤의 콘텐츠를 바이트 스트림에서 더 뒤로 밀어냅니다.
  • 지연 로딩 불가. data URI를 쓴 <img>에 loading="lazy"를 붙여도 아무것도 아끼지 못합니다. 바이트가 이미 페이지와 함께 도착했기 때문입니다.

그 대가로 요청 하나를 아낍니다. HTTP/1.1에서는 브라우저가 호스트당 연결을 몇 개만 열었기 때문에 이것이 큰 문제였습니다. HTTP/2와 HTTP/3에서는 많은 요청이 하나의 연결을 공유하므로 절약 효과가 작습니다. 대부분 요청과 응답 헤더, 그리고 브라우저가 늦게 발견하는 파일에 대한 왕복 지연 한 번 정도입니다.

측정: gzip과 Brotli가 Base64에 미치는 영향

33%라는 수치는 압축하지 않은 텍스트 기준입니다. 대부분의 HTML과 CSS는 gzip이나 Brotli로 제공되므로 중요한 것은 압축 후 크기입니다. 직접 측정했습니다.

방법. Debian 13 패키지의 샘플 파일을 썼습니다. 16×16 PNG 아이콘(HighContrast 아이콘 테마), 48×48 RGBA PNG(hicolor 테마), 2880×1800 Xfce 파란색 배경화면 JPEG, 이를 Pillow로 품질 80에서 800×500으로 줄인 JPEG, Roboto Slab Regular WOFF2 글꼴, SVG 아이콘 두 개(479바이트 Adwaita symbolic 아이콘과 7.6 KB GNOME Debian 소용돌이)입니다. 각 파일과 그 Base64 인코딩(data: 접두사 없이 페이로드만)을 Node.js 24 zlib으로 압축했습니다. 미리 압축하는 정적 자산에 흔히 쓰는 설정인 gzip 레벨 9와 Brotli 품질 11입니다. 크기 단위는 바이트입니다.

파일원본Base64원본 gzipBase64 gzip원본 BrotliBase64 Brotli
PNG 16×16281376285306285284
PNG 48×484,5526,0724,5754,6024,5514,589
JPEG 800×5008,19510,9287,4257,1737,1606,908
JPEG 2880×1800197,011262,684182,684183,945180,114181,140
WOFF2 글꼴52,53270,04452,53852,76552,53652,690
SVG 아이콘479640291427247410
SVG 소용돌이7,61010,1483,1234,3982,5823,823

숫자가 보여 주는 것은 다음과 같습니다.

이미 압축된 바이너리는 오버헤드를 거의 다 잃습니다. PNG, JPEG, WOFF2 데이터는 이미 압축되어 무작위에 가까우므로 다시 압축해도 얻는 것이 거의 없습니다(PNG 두 개는 gzip에서 오히려 조금 커졌습니다). 무작위 바이트의 Base64는 각각 8비트인 64개 기호를 쓰고, 허프만 부호화는 이를 기호당 약 6비트로 되돌립니다. 압축된 Base64는 압축된 바이너리의 96–107%, 원본 파일의 84–109%였습니다. 여기 쓴 JPEG는 그 자체로 약간 압축이 되었기 때문에 Base64 버전이 gzip으로 압축한 JPEG보다 오히려 조금 작게 나왔는데, 이 차이는 계획에 반영할 만큼 크지 않습니다. 아주 작은 16×16 PNG에서는 gzip의 고정 헤더와 트레일러 18바이트가 큰 비율로 드러나며, 더 가벼운 Brotli 프레이밍에서는 사라졌습니다.

SVG는 더 나빠집니다. SVG는 반복이 많은 텍스트여서 압축이 아주 잘 됩니다. 소용돌이는 gzip으로 7,610바이트에서 3,123바이트로 줄었습니다. Base64는 3바이트 묶음을 정렬 위치에 따라 값이 달라지는 4개 문자로 뒤섞으므로, 반복되는 부분 문자열이 더 이상 두 번 같아 보이지 않습니다. gzip으로 압축한 Base64 SVG는 gzip으로 압축한 SVG보다 41–47%, Brotli로는 48–66% 컸습니다.

URL 인코딩한 SVG와 Base64

SVG는 이미 텍스트이므로 Base64가 전혀 필요 없습니다. URL 인코딩한 data URI는 읽을 수 있는 상태를 유지합니다.

.flag {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 0v16h2V8h3l1 2h5V2h-4L9 0z'/%3E%3C/svg%3E");
}

최소한 이스케이프해야 하는 것은 %, #(URL 프래그먼트를 시작하므로), <, >, 그리고 속성을 감싸는 따옴표 문자입니다. SVG 안의 큰따옴표를 작은따옴표로 바꾸면 이스케이프하지 않아도 됩니다. ASCII가 아닌 문자는 UTF-8로 퍼센트 인코딩해야 합니다.

같은 SVG 두 개를 같은 방법으로 측정했습니다.

SVG인코딩크기GzipBrotli
아이콘Base64640427410
아이콘최소 URL 인코딩493293253
아이콘encodeURIComponent723326279
소용돌이Base6410,1484,3983,823
소용돌이최소 URL 인코딩7,1793,0572,549
소용돌이encodeURIComponent11,5943,3192,750

“최소"는 공백을 줄이고, 큰따옴표를 작은따옴표로 바꾸고, %, #, <, >만 이스케이프한 것입니다. 원래 SVG와 거의 같은 크기로 압축되었고, 소용돌이는 공백을 줄여 바이트가 빠진 덕분에 오히려 조금 작습니다. 전체 encodeURIComponent는 압축 전에는 더 크지만 여전히 Base64보다 훨씬 잘 압축됩니다. SVG를 data URI로 도구는 두 형태를 나란히 만들어 줍니다.

도구나 API가 Base64만 받거나, SVG에 이스케이프를 신경 쓰고 싶지 않은 문자가 들어 있다면 SVG에도 Base64가 여전히 의미가 있습니다.

콘텐츠 보안 정책과 data:

콘텐츠 보안 정책(CSP)에서 'self'는 data: URL을 포함하지 않습니다. 이를 허용해야 하는 지시문마다 스킴을 적어야 합니다.

Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:

img-src와 font-src에 data:를 허용하는 것은 흔하고 위험이 낮습니다. script-src, default-src, object-src에 허용하는 것은 전혀 다른 문제입니다. 마크업을 삽입할 수 있는 공격자가 외부 호스트 없이 스크립트를 넣을 수 있게 되어 CSP의 의미가 상당 부분 사라집니다. 인라인 이미지가 정책에 걸린다면 img-src만 넓히세요.

브라우저는 링크, 리디렉션, 스크립트 어느 방식으로든 페이지가 최상위 창을 data: URL로 이동하는 것도 차단합니다. 따라서 data URI는 페이지에 삽입할 수는 있어도 최상위 링크로 열 수는 없습니다.

인라인이 가치 있을 때

대개 가치 있는 경우:

  • 아주 작은 아이콘과 UI 이미지. 대략 1–2 KB 이하이고, 특히 첫 페인트에 필요한 것. 요청 오버헤드가 크기에서 차지하는 비율이 크고, 압축이 Base64 비용 대부분을 지워 줍니다.
  • 단일 파일 HTML. 디스크에서 열어도 동작해야 하는 오프라인 보고서, 내보낸 문서, 테스트 커버리지 페이지, HTML 이메일 미리보기.
  • 크리티컬 CSS. 어차피 HTML에 인라인되는 CSS라면 작은 배경 이미지도 함께 실어 보낼 수 있습니다.
  • 생성된 콘텐츠. 스크립트에서 PNG로 렌더링해 HTML 보고서에 넣는 차트 등.

대개 가치 없는 경우:

  • 사진과 몇 킬로바이트가 넘는 모든 것. 캐시 수명을 길게 준 별도 파일이 낫습니다.
  • 여러 페이지가 공유하는 이미지. HTML 응답마다 안에 담겨 다시 내려받게 됩니다.
  • 웹 글꼴. WOFF2 파일은 보통 수십 킬로바이트이고(위에서 측정한 것은 52 KB), CSS에 인라인하면 스타일시트가 렌더링을 더 오래 차단하며, 별도 글꼴 파일은 캐시하고 미리 로드할 수 있습니다.
  • 스크롤해야 보이는 모든 것. loading="lazy"를 쓴 별도 파일은 필요해질 때까지 비용이 없습니다.

이메일은 별개의 경우입니다. 메일 클라이언트마다 data: 이미지 지원이 다르므로 받는 사람들이 실제로 쓰는 클라이언트에서 테스트하세요. HTML 이메일에 이미지를 넣는 믿을 만한 방법은 cid:로 참조하는 인라인 MIME 첨부 파일이며, 이것도 Base64지만 MIME 구조 안에 있습니다. 이메일 속 Base64를 참고하세요.

이미지로 data URI 만들기

PNG, JPEG, WebP, SVG를 끌어다 놓으면 data URI와 그 크기를 받을 수 있습니다. 파일은 브라우저 안에 머뭅니다.

이미지 → Base64

자주 묻는 질문

Base64 이미지는 일반 이미지보다 느린가요?

본질적으로 그렇지는 않습니다. 작은 인라인 이미지는 요청을 줄이지만, 큰 이미지는 HTML이나 CSS를 부풀리고, 따로 캐시될 수 없으며, 렌더링을 늦출 수 있습니다. 몇 킬로바이트 이하에서는 인라인이 도움이 되고 그 이상에서는 손해입니다.

gzip이 Base64의 33% 오버헤드를 없애 주나요?

PNG, JPEG, WOFF2라면 거의 다 없애 줍니다. 저희 측정에서 gzip으로 압축한 Base64는 바이너리 파일과 몇 퍼센트 이내로 차이가 났습니다. SVG와 다른 텍스트는 그렇지 않아서, gzip으로 압축한 Base64 SVG는 gzip으로 압축한 SVG보다 41–47% 컸습니다.

인라인 SVG에는 URL 인코딩과 Base64 중 무엇이 좋은가요?

URL 인코딩입니다. 읽을 수 있고, 편집할 수 있으며, 원래 SVG와 거의 비슷하게 압축됩니다. 최소한 #, %, <, >는 이스케이프하고, 속성이 큰따옴표를 쓴다면 SVG 안에서는 작은따옴표를 쓰세요.

data URI 이미지가 왜 콘텐츠 보안 정책(CSP)에 막히나요?

CSP는 data:를 'self'에 포함되지 않는 별도의 스킴으로 취급합니다. img-src(글꼴은 font-src)에 data:를 추가하세요. script-src나 default-src에는 추가하지 마세요.

계속 읽기