이전 포스팅에서 다루었던 ArrayBuffer 와 TypedArray(View) 는 ECMA 의 표준 스펙입니다.
바이너리 데이터를 자바스크립트에서 다루는 표준인 셈이에요.
btoa(), atob() 로 base64 인코딩을 하거나,
TextEncoder, TextDecoder 로 utf-8 인코딩을 할 수 있음을 알아보았습니다.
하지만 이것만으로는 브라우저에서, 이미지, 동영상 등 파일을 다루기에는 부족함이 많습니다.
예를들어서, 이것으로 비트맵을 표현하는 이미지를 만들어낸다고 하면, 3비트씩 직접 색상을 일일이 찍어내고, <img> 태그 에 호환이 되도록 만들어야되죠. 또 XMLHttpRequest, fetch 등의 네트워크 통신간에 파일을 업로드, 다운로드하기 위한 규격에도 맞추어야 하구요!
브라우저는 한단게 더 높은 추상화를 거친 Blob 이라는 객체를 제공하고 있습니다.
blob 은 브라우저에서 바이너리 데이터를 OS의 File 처럼 다룰 수 있다고 생각하시면 됩니다.!
Blob 의 구성
blob 은 MIME-type +blob or string or bufferSource 로 구성되어있습니다.
코드로 생성해보면 아래와 같습니다.
String 으로 blob 만들기
const blob = new Blob(['Kong World'],{type:'text/plain'})
console.log(blob) // Blob {size: 10, type: 'text/plain'}
buffer 로 blob 만들기
const KongWorld = new Uint8Array([75, 111, 110, 103, 32, 87, 111, 114, 108, 100]);
const blob = new Blob([KongWorld],{type:'text/plain'});
blob.text().then(console.log); // Kong World
blob 으로 blob 만들기
const blob2 = new Blob([blob,blob], {type:'text/plain'});
blob2.text().then(console.log); // Kong WorldKong World
이렇게 Blob 객체를 만들었는데, 만들기만 하면 단순히 string, buffer 값을 만든것과 다름이 없습니다.
Blob은 서두에 설명했듯 File 처럼 다룰 수 있습니다. 실제로 Blob 을 상속한 File 객체가 따로있지만, 쉽게 생각해서요 ㅎㅎ :)
Blob 은 초기에 생성했던 MIME-type 의 확장자 형태로 값을 메모리에 저장할 수 있습니다.
그렇게 된다면 이미지, html, text, 동영상 등의 형태를 브라우저에 저장할 수 있다는 것이고, 이는 <img> , <video> , <a> 태그 등에 사용될 수 있고, 또 파일로 다운로드, 업로드가 가능하다는 것이 됩니다.
Blob as URL
blob 으로 만들어진 객체를 이곳 저곳에서 활용하기 위해서는 URL 형태의 참조 주소를 얻어내야합니다.
앞서서 blob 은 메모리에 MIME-type 에 맞게 저장된다고 했었죠!
URL.createObjectURL() 메서드를 사용하여 blob 의 url 을 얻어낼 수 있습니다.
URL.createObjectURL() = blob:<origin>/<uuid>
const KongWorld = new Uint8Array([75, 111, 110, 103, 32, 87, 111, 114, 108, 100]);
const blob = new Blob([KongWorld],{type:'text/plain'});
const url = URL.createObjectURL(blob); // blob:http://localhost:63342/05e7073f-8dc3-4cd4-8d58-3acfb1f384c9
url 의 반환값 blob:~~~ url 을 같은 브라우저에 입력하면 브라우저에서 파일을 열람한것 처럼 접근이됩니다.
❗️브라우저의 메모리에 저장하기 때문에, 반드시 같은 브라우저 내에서만 접근이 가능합니다
그렇다면 이 url 을 a 태그에 href 속성으로 넣고, download 가 가능하도록 작성해볼 수 있어요!
const KongWorld = new Uint8Array([75, 111, 110, 103, 32, 87, 111, 114, 108, 100]);
const blob = new Blob([KongWorld],{type:'text/plain'});
const url = URL.createObjectURL(blob); // blob:http://localhost:63342/05e7073f-8dc3-4cd4-8d58-3acfb1f384c9
const a = document.createElement('a');
a.href = url;
a.text= 'Download KongWorld.txt';
a.download = 'KongWorld.txt';
document.body.appendChild(a);
여기서 한가지 주의할 점은, URL.createObjectURL 은 blob 에 대한 고유한 URL 을 만들어 내고, 가비지 컬렉터가 동작하지 않아요. 즉 브라우저의 라이프사이클과 함께 쭉 유지됩니다. 점점 쌓인다면 메모리 누수가 발생하게 되서, 더 이상 사용하지 않는다면 직접 레퍼런스를 제거해주어야합니다.
URL.revokeObjectURL(url)
Blob as DataURL
위 방법은 blob 을 메모리에 저장하고, 그 레퍼런스 값을 얻어 브라우저 내에서만 접근 가능하도록 하는 방식입니다.
한가지 방식이 더 있는데, 바로 dataURL 을 생성하는 것입니다.
dataURL 은 data: prefix 가 붙은 scheme 입니다.
dataURL 의 구성
data:[<mediatype>][;base64],<data>
data: + MIME-type + base64 로 구성됩니다.
직접 base64 로 인코딩하고, 데이터 조합을 만들어도 되겟지만
blob 은 이미 MINE-type 과 데이터를 가지고있기 때문에, 바로 dataURL 로 만들어낼 수 있습니다.
그러기 위해서 blob 을 다양한 포맷으로 읽어내는 FileReader 를 활용합니다.
FileReader 가 이벤트 기반으로 동작하기 때문에, 간단히 Promise 로 감싸서 활용해보았습니다.
const KongWorld = new Uint8Array([75, 111, 110, 103, 32, 87, 111, 114, 108, 100]);
const blob = new Blob([KongWorld],{type:'text/plain'});
readUrlAsync(blob).then(url=>{
const a = document.createElement('a');
a.href = url; // data:text/plain;base64,S29uZyBXb3JsZA==
a.text= 'Download KongWorld.txt';
a.download = 'KongWorld.txt';
document.body.appendChild(a);
})
/**
* @param blob
* @returns {Promise<string>}
*/
function readUrlAsync(blob){
return new Promise(resolve => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
resolve(reader.result);
}
})
}
😄 FileReader 는 다음 포스팅에서 좀 더 자세히 다루어보겠습니다.
URL.createObjectURL(blob) vs DataURL
두가지 방식 중 어떤 것을 사용해도 동일한 결과를 얻어낼 수 있지만, 각 장단점이 존재하기에 적절히 상황에 맞게 선택해서 사용합니다.
구분 | URL.createObjectURL(blob) | DataURL |
단점 | 메모리 누수를 방지하기 위해 revoke 를 일일이 해주어야 하는 | base64 로 encoding 해야하기 때문에, 큰 데이터라면 메모리와 퍼포먼스 저하가 우려되는 |
장점 | encoding/decoding 없이 blob 그 자체에 접근 가능한 | 메모리 누수 걱정이 없음 |
'FE > browser' 카테고리의 다른 글
[browser] 브라우저의 Storage (0) | 2023.10.06 |
---|---|
[browser] Cookie (0) | 2023.10.06 |
[Browser] Service Worker 로 push notification 구현하기 (0) | 2023.09.21 |
[Browser] Web Worker 와 Service Worker (0) | 2023.09.20 |
[Broswer] EventTarget 과 Event, CustomEvent (0) | 2023.09.20 |