3000개의 이미지, 어떻게 다룰 것인가? (feat. Blob)

Lybell·2024년 3월 10일
0

Prismic 기술 노트

목록 보기
1/1
post-custom-banner

본 아티클은 2023년 9월 Prismic이라는 프로젝트를 개발하면서 생긴 기술적인 논의 메모를 재구성한 메모입니다.


개요(Prismic 프로젝트의 특성)

Prismic은 최대 3000개 이상의 대규모 이미지를 쉽게 카테고라이징할 수 있게 돕는 목적으로 만들어졌다. 그러므로, 3000개 이상의 이미지가 동시에 웹 메모리에 올라와도 애플리케이션이 프리징되지 않아야 할 필요가 있다. 1개의 이미지를 1MB라고 가정하면, 3000개의 이미지는 약 3GB정도 될 것이다. 3GB 정도 되는 파일이 메모리에 올라왔을 때, 브라우저가 프리징되는지 가능성을 알아보기 위해, 간단한 실험을 해 보았다.

실험

실험 1

간단히 3GB짜리 통짜 파일을 업로드했다. 업로드 자체만으로 브라우저가 크래시되는 일은 없었다. 또한, 1000개의 파일을 업로드하여 blob 객체가 1000개 메모리에 있더라도, 브라우저의 메모리가 고갈날 일은 없었다.
따라서, 단순 업로드 자체는 파일의 용량과 상관없이 자바스크립트가 감당할 수 있음을 알았다.

실험 2

function onFileInput(e)
{
  const blob = e.target.files[0];
  if(blob === null) return;
  blob.text().then(e=>console.log(e));
}
document.getElementById("test").addEventListener("change", onFileInput);

500MB짜리 파일을 업로드해서 1초 후에 전문을 텍스트로 읽어오기를 시도하였다. 브라우저가 파일 처리를 시도하다 콘솔이 멈추더니 out of memory로 메모리 부족 오류가 생겼다.

실험 3

function addDom(rawText)
{
  const newElem = document.createElement("p");
  newElem.innerText = rawText.slice(0, 20);
  document.body.appendChild(newElem);
}
function onFileInput(e)
{
  const blobs = e.target.files;
  for(let blob of blobs) {
    blob.text().then(addDom);
  }
}
document.getElementById("test").addEventListener("change", onFileInput);

이번에는 1MB짜리 파일을 1000개 업로드한 뒤, 1000개의 파일의 시작 부분을 dom에 추가하였다. 실험 결과, 여유롭게 실행되는 모습을 볼 수 있었다.
참고로 동일한 실험을 별도의 텍스트 배열에 추가하고, 그 배열의 시작부를 dom에 출력하려고 하면 메모리 오류를 반환한다.

가능성 확인

Prismic의 경우, 3000개의 이미지의 비트맵 정보가 필요한 것이 아닌, 3000개의 이미지의 주소 참조만 blob 데이터로 저장하고, 필요할 때 단 하나의 이미지의 미리보기를 렌더링할 것으로 구현되므로, 메모리 부족 관련 성능 문제는 걱정하지 않아도 될 듯 하다. 따라서, 구현 가능성이 존재한다!

Blob 데이터는 어디에 저장될까?

크롬 개발자 도구로 blob이 메모리 상에 점유되는 용량을 분석하면 매우 작은 용량을 점유하는 것으로 드러난다. blob을 콘솔로 출력해 보아도 그 어디에도 내용은 없다.

let p="";
for(let i=0;i<100000;i++){
    p+=Math.random()*89+10;
}
let blob = new Blob([p]);
p = 123;

이 코드를 실행시키면, 대용량의 문자열이 생성되어 메모리에 저장되었다가(이 때 concanated string이 많이 생성되어 메모리 용량이 치솟다 최종 문자열을 제외한 나머지는 가비지 컬렉션된다.), 블롭화되고 원본 대용량 문자열은 가비지 컬렉션된다. 그런데도 let text = await blob.text(); 와 같은 방식으로 텍스트를 읽어올 수 있으며, 놀랍게도 해당 텍스트는 외부 텍스트로 취급되어 점유하는 메모리는 고작 20byte밖에 되지 않는다.

과연, blob의 내용은 실제로 어디에 저장되어 있으며, 어떻게 blob의 메타데이터만으로 원본 데이터를 읽어올 수 있는 것일까?

크롬 기준으로 blob의 실제 내용은 자바스크립트의 힙이 아닌 별개의 공간에 저장된다. 크롬 자체적으로 blob을 저장하는 메모리가 존재하며, blob이 메모리 이상으로 너무 크거나 blob 메모리의 최대 용량이 전부 차면 디스크에 저장한다.

blob 데이터는 자바스크립트 코드 등에서 참조가 있는지 없는지를 계산하여, 참조가 없으면 가비지 컬렉션을 수행한다. 만약 object url로 blob을 변환했으면, 해당 object url은 blob의 내부 데이터를 “참조”하게 되어 가비지 컬렉션이 일어나지 않는다. 이는 해당 object url 문자열을 더 이상 참조하지 않더라도 blob URL store라는 곳에 내부적으로 저장되므로 가비자 컬렉션이 일어나지 않는다. 브라우저 창이 꺼지면 연결된 blob은 전부 가비지 컬렉션된다.

크롬 기준으로 현재 브라우저 창이 점유하는 실제 blob 오브젝트 데이터의 내역은 chrome://blob-internals/에서 확인할 수 있다.

파일의 경우는 blob 메모리에 실제 데이터 자체를 저장하는 것이 아니라, blob 데이터에 로컬 파일 시스템을 참조하는 경로만 저장된다. 실질적으로 File 객체의 실제 데이터는 파일 경로에 그대로 존재하며, 어딘가로 복사되지는 않는다. 파일을 읽어올 때는 File System API가 blob 메모리에 저장된 파일 경로 포인터를 기반으로 직접 로컬 파일에서 데이터를 읽어온다.

profile
홍익인간이 되고 싶은 꿈꾸는 방랑자
post-custom-banner

0개의 댓글