이진 데이터
컴퓨터는 우리가 사용하는 모든 데이터를 0, 1 로 저장합니다.
이러한 데이터를 이진 데이터라고 하며 Binary 데이터라고도 합니다.
왜 사용할까??
웹 개발을 진행하다 보면 이진 데이터를 다루어야 할 때를 간혹 마주할 수 있습니다.
브라우저에선 주로 파일 생성, 업로드, 다운로드 또는 이미지 처리와 관련이 깊습니다.
우리가 평소에 프로그래밍 하면서 직접 이진데이터를 다루는 일은 별로 없습니다.
우리가 프로그래밍 할 때에는 고급 언어를 사용하기 때문에 알아서 프로그램이 내부적으로 이진 데이터로 변환하고 읽고 처리하기 때문입니다. 하지만 파일이나 이미지, 비디오 같은 멀티미디어 같은 데이터를 다루어야 할 때는 이 멀티미디어 데이터를 정수, 문자 다루듯 해야 합니다.
Base64
우리가 지금 보고 있는 브라우저는 0, 1로 이루어져 있는 것이며 폴더나 파일 역시 이진데이터로 이루어져 있습니다.
그럼 컴퓨터 안에 저장된 데이터를 꺼내 쓰고 싶을 때 어떻게 해야 할까요?
메모리에 저장된 0, 1 로 이루어진 데이터를 변수에 적재해 놓고, 필요하면 우리는 변수를 불러 덧셈, 뺄셈을 하는 것으로 컴퓨터 안의 이진 데이터를 다루고 있습니다. 숫자나 스트링이 아닌, 이미지나 비디오 같은 복잡한 멀티미디어 파일들은 어떻게 변수에 메모리에 저장해야 할까요?
변수에 이미지 URL 을 저장하는 건 링크라는 징검다리를 저장하는 거지 이미지 데이터 그 자체를 저장하는 것은 아닙니다.
이때 등장하는 것이 Base64 라는 개념입니다.
Base64는 0 , 1로 이루어진 이진 데이터(Binary)를 인코딩하여 텍스트 형식으로 변환하는 것을 말합니다.
아래 사진과 같이 이미지 경로가 아닌 Base64 로 이루어진 긴 문자열 이미지 데이터를 본 적이 있을 텐데 이것이 바로 Base64입니다. 0,1로 이루어진 이미지 데이터 자체를 Base64 텍스트 기반 포맷으로 변환해 줌으로써 우리가 직접 이미지 데이터를 다룰 수 있는 것입니다.
즉, 어떤 경로를 이용해서 보여주는 것이 아니라 그냥 코드 자체에서 이미지를 보여주는 것이라고 생각하시면 됩니다.
Base64 를 사용하는 경우는 어떤 경우가 있을까요?
1. 크기가 작은 이미지를 URL이나 파일 불러오는 것 없이 HTML에 직접 삽입하는 경우
2. 간단한 페이지를 작성해 임시로 이미지를 사용하는 경우
3. 이미지가 들어간 메일 내용을 HTML 으로 작성해서 보내는 경우
이렇게 Base64 로 변환하면 직접 바이너리를 다룬다는 특징도 있지만, 바이너리 데이터를 텍스트로 바꾸는 64진법 인코딩을 통해, 바이너리 데이터 대비 33% 의 데이터 양이 증가하게 된다는 단점이 있습니다.
하지만 데이터의 길이가 증가함에도, Base64를 사용하는 가낭 큰 이유는 앞서 소개하듯이 Binary 데이터를 텍스트 기반 규격으로 다룰 수 있기 때문입니다.
또한 순수 바이너리 형식으로 남아있는것 보다 전송 / 저장이 훨씬 쉽다는 점도 꼽을 수 있습니다.
이진 데이터는 손상될 확률이 높습니다.
이미지 URL 을 Base64로 변환하기
이미지를 Base64 로 바꾸어주는 방법은 대표적으로 두 가지가 있습니다.
첫 번째로 이미지를 Base64로 바꾸어주는 사이트가 있습니다.
https://elmah.io/tools/base64-image-encoder/
두 번째로는 자바스크립트 코드를 이용해 변환하는 방법입니다.
먼저 Fetch API로 이미지를 읽어 들이고, 이미지를 Blob 형식으로 변환합니다.
그리고 FileReader 객체를 통해 Blob 데이터를 Base64 형식으로 읽어 들여 변환시킵니다.
Blob
컴퓨터에서 큰 덩어리의 정보를 담고 있는 것으로 생각할 수 있습니다.
이 덩어리 안에는 텍스트, 이미지, 오디오, 비디오 등 다양한 종류의 정보가 들어갈 수 있습니다.
그러니까, 우리가 인터넷에서 사진을 다운로드 할 때 그 사진은 하나의 Blob으로 저장됩니다.
FileReader
Blob , File 같은 자료형 객체로부터 데이터를 읽어 들이기위한 목적으로 사용되는 객체입니다.
(async () => {
const data = await fetch('https://play-lh.googleusercontent.com...');
const blob = await data.blob();
const reader = new FileReader();
reader.onload = () => {
const base64data = reader.result;
console.log(base64data)
}
reader.readAsDataURL(blob);
})()
Blob
Blob 은 Binary Large Object 의 약자로 주로 이미지, 오디오, 비디오와 같은 멀티미디어 파일 바이너리를 객체 형태로 저장한 것을 의미합니다. 멀티미디어 파일들은 대다수 용량이 큰 경우가 많기 때문에, 이를 데이터베이스에 효과적으로 저장하기 위해 고안된 자료형이라 볼 수 있습니다.
예를 들어 데이터베이스에 이미지 파일을 그대로 데이터로 저장하고 싶을 때, 바로 Blob 포맷으로 변환한 뒤 저장하는 것입니다.
앞서 Base64 포맷으로 이미지 바이너리 파일을 브라우저에 넣으면 다음과 같이 문자열이 굉장히 길어지기 때문에 가독성이 안 좋을 뿐만 아니라, Base64 이미지를 이곳저곳 여러 개 사용할 경우 결과적으로 용량 문제 때문에 문서 자체를 로딩하는데 많은 시간이 걸려 오히려 느려질 수 있다면 단점을 지니고 있습니다.
하지만 Blob 데이터는 적절하게 Object URL 로 변환만 해준다면 다음과 같이 심플하게 브라우저에서 사용할 수 있습니다.
이미지 URL 을 Blob으로 변환
기본적인 문법은 다음과 같습니다.
const data = await fetch('https://play-lh.googleuserconte...');
const blob = await data.blob(); // 이미지 blob 객체 얻기
예시는 아래와 같습니다.
<img src="" alt="" style="width:150px; display: block;">
<a download="img.jpg" href="#">이미지 다운로드</a>
<script>
fetch('https://www.busin...')
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
console.log(url)
});
</script>
이러한 Blob 이미지는 현재 탭의 브라우저 메모리에 저장됩니다.
이러한 원리 떄문에, Base64 와는 달리 짧은 문자열만으로도 원래의 Blob 객체에 접근이 가능하고 그에 따른 이미지 등의 파일을 가져올 수 있습니다. 그래서 변환된 URL 은 항상 현재문서에서만 유효합니다. ( 현재 브라우저 메모리에 적재 )
변환된 URL 은 현재 문서를 새로고침하거나 아니면 다른 페이지에서 사용하려고 한다면 제대로 사용할 수 없습니다.
이와 관련한 메모리 이슈도 존재합니다.
Blob 객체가 URL 로 변환되어 매핑이 이루어진 채 메모리에 저장되게 되면, 명시적으로 해당 URL 이 해제되기 전까지 브라우저는 해당 URL 이 유효하다고 판단하기 때문에 자바스크립트 엔진에서 가비지 컬렉션이 이루어지지 않습니다.
따라서 Blob URL 을 사용한 이후, 더 이상 사용하지 않을 시점이라고 판단되면 명시적으로 해제해 주는 것이 좋습니다.
// Create Blob URL
const objectURL = window.URL.createObjectURL(blob);
// Revoke Blob URL after DOM updates..
window.URL.revokeObjectURL(objectURL);
Blob에서 Base64 변환
앞서 말하였듯 Blob 객체로 Base64의 형태로 변환이 가능하고, 변환된 문자열을 바로 src 로써 사용할 수 있습니다.
이러한 URL 은 보통 Data URL 이라고 부르는데, 이렇게 변환된 Base64 형태의 URL 은 위에서 URL.createObjectURL 메서드를 통해 변환한 형태와는 달리 어디에서나 사용이 가능합니다. ( 문자열 자체가 이미지 데이터이기 때문이다. )
fetch('https://play-lh.googleusercontent.co...')
.then((res) => res.blob())
.then((blob) => {
const reader = new FileReader();
reader.onload = () => {
const base64data = reader.result;
console.log(base64data)
}
reader.readAsDataURL(blob);
})