- Published on
data URI 스킴과 Base64 인코딩을 이용해 브라우저에서 이미지 보여주기
- Authors
- Name
- 마롱
웹 페이지에서 이미지를 보여줄 땐 <img>
태그의 src
속성에 이미지의 경로(URL)를 삽입한다. 이때, src
속성에는 서버상에 위치하는 이미지의 상대 경로를 넣는 것이 일반적이다. 브라우저는 웹 페이지를 로딩할 때 src
속성값에 있는 경로에서 이미지를 가져와 보여준다.
그런데 만약 이미지가 웹 서버에 존재하지 않고 다른 곳(예: 외부 스토리지)에 존재한다면 어떻게 보여줘야 할까?
프로젝트 진행 중, 유저가 특정 경로에 존재하는 이미지를 요청할 때마다 해당 이미지를 웹 서버에 가져와 저장하지 않으면서 웹 페이지에 일회성으로 보여주는 방법이 필요했다. 또한 .tif
확장자 이미지를 브라우저에서 보여줘야 했는데 특정 브라우저에서는 해당 확장자를 지원하지 않는 문제가 있었다.
나는 여러 고민과 검색을 통해 data URI 스킴과 Base64 인코딩을 이용하면 해당 기능을 구현할 수 있겠다고 생각했다. 이 글에서는 data URI 스킴과 Base64 인코딩에 대해 알아보고, Spring Boot을 이용해 브라우저에서 이미지를 보여주는 데모 프로젝트를 만들어보려고 한다.
1. data URI 스킴이란?
data URI 스킴이 무엇인지 알아보기 전에 우선 URI, URL, 스킴의 개념을 정리해보자.
URI, URL, 스킴
URI(Uniform Resource Identifier): 리소스 식별자
URI는 두 가지 부분집합인 URL과 URN으로 구성된다.
- URN(Uniform Resource Name): 리소스 이름, 리소스가 어디에 있는지와 상관없이 그 이름만으로 리소스를 식별
- URL(Uniform Resource Locator): 리소스 위치, 리소스가 어디에 있는지를 설명해서 리소스를 식별
URN만으로 실제 리소스를 찾을 방법이 보편적이지 않아 보통 URI를 URL과 같은 의미로 사용한다.
URL(Uniform Resource Locator): 리소스 위치
URL은 위에서 말한 것 처럼 브라우저가 정보를 찾는데 필요한 리소스의 위치를 알려준다. 여기서 리소스란 웹에서 사용되고 있으며 식별할 수 있는 모든 자원을 의미한다.
URL의 문법은 아래와 같다.
- 리소스는 각자 다른 스킴(예: HTTP, FTP, SMTP)을 통해 접근할 수 있으며, URL 문법은 스킴에 따라 다르다.
URL 스킴의 문법은 일반적으로 9개의 컴포넌트로 구성된다. 9개 컴포넌트를 모두 가진 URL은 거의 없으며 URL의 가장 중요한 세 가지 컴포넌트는 스킴, 호스트, 경로(
<스킴>://<호스트>/<경로>
)다.
<스킴>://<사용자 이름>:<비밀번호>@<호스트>:<포트>/<경로>;<파라미터>?<질의>#<프래그먼트>
스킴(Scheme)
스킴은 리소스를 가져오려면 어떤 프로토콜을 사용하여 서버에 접근해야 하는지를 알려준다. 스킴의 예시로는 http, ftp, mailto, javascript 등이 있다.
- http:
http://www.joes-hardware.com/seasonal/index-fall.html
- ftp:
ftp://ftp.lots-o-books.com/pub/complete-price-lists.xls
- javascript:
javascript:alert("hello"!);
data URI 스킴
그러면 이제 data URI 스킴에 대해 알아보자. 보통 작고 변하지 않는 데이터를 삽입하고 싶을 때, 데이터를 참조하는 링크를 넣는 대신 데이터를 '바로' 넣는데 사용하는 스킴이다.
문법
data:[<mediatype>][;base64],<data>
<mediatype>
: MIME 타입을 말한다. 기본값은text/plain;charset=US-ASCII
이다.cf. MIME 타입
- 본래 멀티미디어 이메일(MIME, Multipurpose Internet Mail Extensions(다목적 인터넷 메일 확장))을 위해 개발되었으나 HTTP를 비롯해 여러 다른 프로토콜에서 재사용되어 왔다.
- HTTP에서는
Content-Type
과Accept
헤더에서 엔터티 본문 콘텐츠를 설명하기 위해 사용 된다.
;base64
: 데이터가 Base64로 인코딩되었음을 의미한다.
예시
"A brief note"라는
text/plain
타입의 문자열data:,A%20brief%20note
base64로 인코딩된
image/gif
타입의 이미지<img src="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH hhx4dbgYKAAA7" alt="Larry">
2. Base64 인코딩이란?
Base64 인코딩은 이진 데이터를 문자열로(binary to text) 인코딩하는 알고리즘 중 하나이다. 6개의 비트(2^6 = 64개의 문자)를 사용하기 때문에 Base64라고 한다.

Base64는 이진 데이터를 안전하게 만들어준다.
- 사용자의 입력이나 이진 데이터를 안전한 형식으로 만들어준다. HTTP 파서를 망가뜨릴 수 있는 콜론(
:
), 줄 바꿈, 이진값이 포함되지 않은 데이터가 HTTP 헤더 필드 값에 담겨 전송될 수 있게 해준다. - MIME 멀티미디어 전자 메일 표준의 일부로 개발되었다. MIME은 Base64 인코딩을 이용해서 오래된 이메일 게이트웨이 사이에서도 복잡한 텍스트나 이진 데이터를 전송할 수 있었다.
Base64는 8비트를 6비트로 표현한다.
8비트의 연속으로 이루어진 이진 데이터를 6비트씩 쪼갠 뒤, 각 6비트 조각을 64개의 문자 중 하나에 할당한다.
64개의 문자는 알파벳 대문자, 소문자, 숫자,
+
,/
를 포함한다.6비트의 정보를 표현하기 위해 8비트 문자를 사용하기 때문에 Base64로 인코딩된 문자열은 원래 데이터보다 33% 커진다.
- 따라서 Base64 인코딩을 하면 원본 데이터보다 용량이 커지기 때문에 성능상의 단점은 유념해야 한다.
Base64 패딩
- 8비트 바이트들이 6비트 조각으로 고르게 나뉘지 않는다면 그 길이를 24비트(6비트와 8비트의 최소공배수)로 만들고 남은 공간은 0비트로 채운다.
- 이렇게 0비트로 채워진(패딩된) 문자열을 인코딩할 때, 완전히 패딩된 6비트의 그룹은 특별한 문자인
=
로 표현한다.
3. Spring Boot를 이용한 데모 프로젝트
data URI와 Base64 인코딩을 이용해 이미지를 보여주는 Spring Boot 데모 프로젝트를 만들었다. 전체 코드는 GitHub 저장소를 참고하면 된다.
Base64 인코딩은 자바에서 제공하는 java.util.Base64
를 사용해 구현했다.
// FileUtil.java
import java.util.Base64;
public static String convertImageToBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
data URI 스킴 <mediatype>
자리에 MIME 타입을 넣기 위해 원본 파일명에서 MIME 타입 정보를 알아냈다. Java에서 MIME 타입을 알아내는 방법은 이 블로그를 참고했다.
// FileUtil.java
import java.net.FileNameMap;
import java.net.URLConnection;
public static String getMimeType(String filename) {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
return fileNameMap.getContentTypeFor(filename);
}
DTO에 인코딩된 문자열, MIME 타입, 원본 파일명 세 가지 정보를 담아 반환해주었다.
// Base64ResponseDto.java
public class Base64ResponseDto {
private String base64Str;
private String mimeType;
private String filename;
}
클라이언트 쪽에서는 세 가지 정보를 이용해 <img>
태그 속성에 값을 적절히 채워준다.
// index.js
$(function() {
$("#submitBtn").on("click", function() {
$.ajax({
url: "/image/base64",
method: "GET",
data: {
imagePath: $("input[name=imagePath]").val()
}
}).done(function(data, textStatus, jqXHR) {
const { base64Str, mimeType, filename } = data;
const myImage = $(".myImage");
myImage.prop("src", `data:${mimeType};base64,${base64Str}`);
myImage.prop("alt", filename);
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseJSON.message);
});
});
})
(+) 크롬 개발자 도구에서 이미지 파일의 data URI 얻는 방법
개발자 도구 > Sources 탭 > 이미지 파일 선택 > 오른쪽 마우스 클릭 > Copy image as Data URI
를 선택하면 해당 이미지의 data URI를 얻을 수 있다.

클립보드에 복사된 URI를 크롬 주소창에 붙여넣으면 이미지 파일을 확인할 수 있다.

참고자료
- 웹 문서
- 강의
- 김영한, 〈모든 개발자를 위한 HTTP 웹 기본 지식 - URI와 웹 브라우저 요청 흐름〉, 인프런
- 도서
- 데이빗 고울리 외 4인, 이응준·정상일, 《HTTP 완벽 가이드》, 인사이트, 2014