Web: JavaScript Blob 알아보기

Heechan
HcleeDev
Published in
9 min readApr 30, 2022
Photo by Shahadat Rahman on Unsplash

최근에 회사에서 엑셀 파일을 업로드, 다운로드하는 화면을 만들어야 하는 일이 있었다. iOS에서는 파일을 종종 다루긴 했는데, 웹에서는 이번에 처음 파일을 다뤄보게 되었다. 그런데 엑셀 파일을 만들어서 다운로드 되도록 하는 과정에서 Blob이라는 존재를 처음 알게 되었다.

이번주는 Blob이 뭔지, JavaScript에서 제공하는 Blob은 대충 어떻게 사용하는건지 짧게 알아보자.

Blob이란?

Blob은 Binary Large Object의 줄임말이다. 직역하면 이진수의 큰 객체라는 말인데, 텍스트 혹은 이진 데이터를 담고 있는 큰 객체로 생각할 수 있다.

이 Blob은 주로 이미지, 오디오, 비디오 등의 다른 타입에 비해 클 가능성이 높은 타입들을 관리할 때 많이 사용된다. 생각해보면 이미지나 오디오 같은 멀티미디어를 표현하기엔 일반적인 데이터 타입인 Int, String 이런 것보다 그냥 Binary로 정보를 전달하는게 나을 것이다.

Blob의 기본적인 목적은 위에서 말한 멀티미디어를 저장하는데 있다. 위키피디아에는 Blob을 이렇게 소개하고 있다.

A binary large object (BLOB) is a collection of binary data stored as a single entity.

‘Blob이란 이진 데이터의 Collection을 하나의 Entity로 저장하고 있는 객체다’라고 설명하는 것으로 보인다. (사실 영어에 약해서 해석이 좀 미묘하게 다를 수도 있을 것 같긴 하다)

이런 큰 용량의 데이터의 경우에는 이진 데이터가 굉장히 많은 저장소 공간에 퍼져있을 것이다. 이런게 연속적으로 퍼져있을 것이고, 그러면 한 페이지(시스템 프로그래밍의 그 페이지 개념)에 다 넣어져있을 수 없다. 아마 잘게 쪼개져 각자 다른 주소의 페이지에 나뉘어져 있을 것이다.

이렇게 연속적으로 퍼져있는 데이터를 하나의 Entity로 편하게 관리할 수 있도록 도와주는 역할을 Blob이 하고 있는 것으로 보인다.

출처: https://www.w3.org/TR/FileAPI/#typedefdef-blobpart

Blob 인터페이스는 이렇게 생겼다고 한다. constructor 에서는 연속된 BlobPart를 받고 있는데, 이 BlobPart는 다른 Blob 객체거나, USVString이거나, ArrayBuffer여야 한다. 주로 어지간한 파일을 우리가 코드 상에서 만들 때는 데이터를 ArrayBuffer로 만들어 Blob에 넣어주는 편이다.

그리고 sizetype 이라는 프로퍼티도 가지고 있다. size 는 이 Blob 객체가 가리키고 있는 byte 단위의 크기를, type 은 이 Blob 객체가 가리키는 데이터의 타입을 나타낸다. 예를 들면 text/plain 이런 타입이 있다.

slice 는 현재 Blob을 또 다른 하위 Blob으로 토막낼 수 있는 기능이다. 그 밑에 있는 메서드들은 현재 Blob이 가리키고 있는 데이터를 읽을 수 있도록 도와주는 메서드들이다. text() 는 USVString을 반환함으로써 문자열을 줄 것이고, arrayBuffer() 는 ArrayBuffer를 반환해줄 것이다.

그런데 이 두 가지 모두 Promise로, 비동기 작업이 진행되고 있다. 그 이유는 Blob이 가리키고 있는 데이터는 deserialize 작업이 필요하고, 이를 읽어내는 작업도 필요하다. 위에서 말했듯 Blob은 용량이 큰 멀티미디어 파일을 관리하기 위한 녀석인만큼, 이를 동기적으로 처리했다가는 좋은 꼴은 못볼 것이다.

따라서 비동기적으로 처리를 하고 있고, 데이터 로드가 완료되면 문자열이든 버퍼든 데이터를 전달해줄 것이다.

그런데 stream() 이라는 메서드는 조금 특이하다. 똑같이 데이터를 읽도록 도와주는 메서드인데, Promise를 이용한 비동기가 아닌 ReadableStream 이라는 특이한 타입을 반환한다.

이 타입을 이용하면 데이터를 원하는 수준의 크기로 쪼개서 읽을 수 있다.

이런 식으로 fetch 를 통해 받아오는 데이터들을 ReadableStream을 이용해 잘라서 이용할 수도 있다. 아마 용량이 큰 멀티미디어 파일의 경우에는 위 코드처럼 getReader() 를 이용해 body 에 점점 쌓이고 있는 데이터를 Streaming 해서 이용할 수 있는 것으로 보인다.

이런걸 보면 Blob이 왜 연속적인 이진 데이터를 읽고 저장하는데 사용되는건지 좀 더 이해가 되려고 한다.

FileReader와 File

그런데 사실 Blob 파일을 읽는 경우에는 stream()보다는 FileReader를 이용하는 것이 일반적이다.

출처: https://developer.mozilla.org/ko/docs/Web/API/FileReader

FileReader는 위에도 나와있듯 Blob 객체나 File 객체의 데이터를 읽어주는 역할을 하는 객체다.

Blob 객체랑 File 객체는 다르지 않나 싶을 수 있겠지만, 사실은 거의 속성이 비슷하다고 볼 수 있다.

애초에 File이라는 인터페이스는 Blob을 상속하고 있다. 데이터를 받는 것도 Blob과 똑같이 sequence<BlobPart> 로 받고 있다. 여기에는 파일의 namelastModified 같은 추가적인 정보도 가지고 있다는 차이가 있다.

이렇게 보면 막 크리티컬한 차이가 있는 것은 아니고, 그냥 Blob과 거의 유사하게 사용할 수 있다고 보면 된다.

다만 이 File은 namelastModified 가 있기에 사용자에게 좀 더 용이하게 다가갈 수 있다는 장점이 있다. 주로 사용하는 OS의 파일 시스템에서 파일 이름, 최근 수정 일자를 보여주는 경우가 대부분이고, 사용자도 파일을 이름과 최근 수정 일자로 구분하는 경우가 많기 때문에 이쪽이 활용성이 더 좋긴 하다.

FileReader에는 다양한 핸들러와 메서드가 있지만, 여기선 간단하게 onload 라는 핸들러만 살짝 살펴보자.

기본적으로 이 onload 는 어떤 Event가 일어났을 때 그 Event 객체를 받아서 처리하도록 되어있다. 주로 우리가 <input> 태그를 이용해 파일을 업로드했을 때, <input> 에서는 파일이 업로드되었다는 Event가 발생한다. 이 Event 객체에 들어있는 파일을 FileReader에 넘기면 FileReader는 그 파일을 읽고 onload 핸들러를 실행시킨다.

근데 그 핸들러는 우리가 만들어서 넣어두면 된다.

const reader = new FileReader();
reader.onload = () => { 우리가 만들어둔 핸들러 }

이런 느낌으로 reader.onload 에 우리가 만들어둔 핸들러를 할당시켜두면 나중에 파일이 로드가 되었을 때 우리가 원하는 동작을 해줄 것이다.

사용 예시

사용 예시로 Blob으로 파일을 생성하고 다운로드할 수 있는 간단한 코드를 소개하고자 한다.

일단 obj 라는 이름으로 데이터를 담은 객체를 하나 만들었다. 이를 3번째 라인에서는 JSON 데이터로 변환했다.

4번째 라인에서는 새로운 Blob 객체를 생성한다. 위에서 말한 BlobPart 타입 중에서는 USVString에 해당될 str 을 넣어줬다.

그리고 옵션에는 utf-8 기반의 JSON 타입을 명시해줬다. 그러면 이 타입은 이제 다른 곳에서 이 파일을 사용할 때도 어떤 타입의 데이터인지 구분할 수 있는 요소가 된다.

여기서 타입은 MIME Type이라고 불리는 기준에 따르고 있다. 그 목록은 이 링크에서 확인할 수 있다. 만약 엑셀 파일 데이터를 넣는거라면 저 목록에서 검색해서 application/vnd.ms-excel라는 값을 얻어서 넣어주면 될 것이다.

8번째 줄에서 중요한 것이 나오는데, window.URL.createObjectURL 메서드를 이용해 Blob 객체에 대한 주소를 만들 수 있다. 여기서 만들어진 Blob URL을 이용하면 사용자가 파일에 접근할 수 있도록 할 수 있다.

밑에 있는 tempLink 와 관련된 코드들은 <a> 태그를 만들어 href 값에 Blob URL을 넣고 이를 클릭하도록 해 사용자가 파일에 접근할 수 있도록 하는 코드다. 그냥 바로 보이도록 할 수도 있는데 여기서는 setAttribute 를 통해 filename.txt 라는 이름으로 다운로드가 되도록 설정해두었기에 다운로드될 것이다.

그리고 마지막 줄은 메모리 관리를 위해 더 이상 사용하지 않는 경우에는 revokeObjectURL() 처리를 해주었다.

결론

Blob이라는게 되게 생소했는데, 알고보니 엄청나게 많이 쓰이는 녀석이었다는 생각이 든다. 어지간한 통신을 할 때 다 이걸 기반으로 할테니까… 우리 회사 서비스 같이 데이터가 중요한 경우에는 이런 부분도 잘 알아야겠구나 하는 생각도 들었다.

참고한 것

https://www.w3.org/TR/FileAPI/#blob-section

--

--

Heechan
HcleeDev

Junior iOS Developer / Front Web Developer, major in Computer Science