자바스크립트에서 바이너리 데이터 읽는 법

1. 바이너리 데이터(binary data)란

바이너리 데이터(이진 데이터)란, 유닛(unit)이 표현할 수 있는 상태가 두 가지인 데이터를 말합니다. 일반적으로 0과 1로 이루어진 일련의 데이터를 바이너리 데이터라 부릅니다.

2. 자바스크립트에서 바이너리 데이터를 다루는 법

자바스크립트에서 바이너리 데이터를 다루기 위해서는 ArrayBuffer 에 담겨져 있는 데이터를 DataViewTypedArray 를 통해 접근해야합니다.

2-1. ArrayBuffer

**ArrayBuffer 는 바이트로 구성된 배열로, 직접 데이터를 접근할 수 없습니다.** 위에서 말한것처럼 DataViewTypedArray 를 통해서만 데이터를 접근, 수정할 수 있습니다. input type=file 이나 Base64 문자열, 네트워크 요청 및 응답 상황에 사용되는 데이터들로부터 ArrayBuffer 를 생성할 수 있습니다.

2-2. DataView

DataView 는 버퍼 데이터를 접근하기 위한 저수준 인터페이스입니다. DataView 를 이용하면 엔디안을 신경쓰지 않고 데이터를 읽을 수 있습니다. 여러 넘버타입(Unsigned int8,16,32 SignedInt, Float 등등..) 크기 만큼 버퍼에 접근할 수 있습니다. 아래의 TypedArray 를 소개 한 후, 엔디안에 대한 예시를 보며 DataView 의 편리함을 경험해보겠습니다.

2-3. TypedArray

ArrayBuffer 를 통해서 여러 넘버타입의 배열의 생성자를 제공하는 객체라고 볼 수 있습니다. 필요한 바이트 단위(8, 16, 32 비트등)의 배열을 생성하여 데이터의 접근, 수정이 가능합니다. TypedArrayDataView 와 달리 현재 브라우저가 동작하는 시스템에 따라 리틀 엔디안이나 빅 엔디안으로 데이터를 읽거나 씁니다.

1
2
3
4
5
6
7
8
9
10
11
const typedArray1 = new Int8Array(8);
typedArray1[0] = 10;

const typedArray2 = new Int8Array(typedArray1);
typedArray2[1] = 20;

console.log(typedArray1);
// Int8Array [10, 0, 0, 0, 0, 0, 0, 0]

console.log(typedArray2);
// Int8Array [10, 20, 0, 0, 0, 0, 0, 0]

3. 리틀 엔디안, 빅 엔디안

엔디안(Endianness)이란, 메모리에 연속된 데이터를 적재하는 방법을 말합니다. 리틀 엔디안(little-endian) 은 작은 단위가 메모리의 앞주소에 적재되는 것을 말하고, 빅 엔디안(big-endian) 은 큰 단위가 메모리의 앞주소에 적재되는 것을 말합니다.

CPU 아키텍처에 따라서 메모리에 다르게 적재되므로 바이트 단위로 데이터를 저장하고, 다시 읽어보면 현재 엔디안을 알 수 있습니다. 아래에 TypedArray 를 사용하여 현재 엔디안을 확인해 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
const arrayBuffer = new ArrayBuffer(2);
const uInt8Arr = new Uint8Array(arrayBuffer);

uInt8Arr[0] = 0xaa;
uInt8Arr[1] = 0xbb;

const uInt16Array = new Uint16Array(arrayBuffer);

const isLittleEndian = uInt16Array[0] === 0xbbaa;

console.log(isLittleEndian); // 대부분의 브라우저는 little-endian 으로 나옵니다.

[참조]

엔디언 - 위키백과, 우리 모두의 백과사전

4. 예제로 실제 데이터 읽어보기

예제에서는 DataView 를 이용해 바이너리 데이터를 읽어 보겠습니다. 아래 예제는 JPEG 포맷의 이미지 파일의 헤더를 읽는 예제입니다. (모든 JPEG 이미지에 동작하는 코드는 아닙니다.)

JPEG 이미지는 EXIF 라는 파일 포맷으로 이루어져 있습니다. EXIF 는 현재 이미지가 JPEG 인지를 나타내기 위해서, Start Of Image(SOI) 마커와 End Of Image(EOI) 마커를 바이너리 데이터의 첫부분과 마지막 부분에 삽입합니다. SOI 는 0xFFD8 의 값을 가지고 EOI 는 0XFFD9 의 값을 가집니다. 이를 이용해 현재 읽는 데이터가 JPEG 이미지 데이터임 을 알 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fileInput = document.querySelector('#file-input');

fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];

file.arrayBuffer().then((arrayBuffer) => {
// endianess 에 대한 대응을 하지 않기 위해서 DataView 를 사용한다.
const dataView = new DataView(arrayBuffer);

// 참조:https://www.media.mit.edu/pia/Research/deepview/exif.html

let cursor = 0;
const SOIMarker = dataView.getUint16(cursor);
const EOIMarker = dataView.getUint16(dataView.byteLength - 2);

const isJPEG = SOIMarker === 0xffd8 && EOIMarker === 0xffd9;

if (!isJPEG) {
console.error('현재 JPEG 포맷만 대응하고 있습니다.');

return;
}
});
});

[전체 예제 코드]

5. 요약

  1. 자바스크립트에서 바이너리 데이터를 읽을 때에는 DataViewTypedArray 를 사용할 수 있다.
  2. 엔디안에 대한 스트레스를 받기 싫다면, DataView 를 활용하는 것이 좋다.