JavaScript로 메모리에 직접 접근하는 방법에 대하여
서론
JavaScript로 메모리에 직접 접근할 수 있을까?
메모리에 직접 접근한다는 말의 의미를 다음과 같이 정의한다면, 가능하다.
- 원하는 크기의 메모리 블록을 할당할 수 있다.
- 그리고 해당 메모리 블록을 원하는 방식으로 잘라 데이터를 읽거나 쓸 수 있다.
메모리 할당
할당된 메모리를 표현하는 JavaScript 객체는 ArrayBuffer다.
다음 코드로 원하는 크기의 메모리를 할당할 수 있다.
const buffer = new ArrayBuffer(1024) // 1024 바이트의 메모리를 할당
메모리에 데이터 읽기 쓰기
할당된 메모리는 View를 통해서 조작한다. View는 ArrayBuffer가 할당한 메모리 블록을 들여다보는 창이다. View 기능을 위해 TypedArray 객체가 사용된다. JavaScript 랭귀지가 지원하는 TypedArray에는 Int8Array, Uint8Array, Int16Array, Uint16Array 등이 있다. TypedArray는 단지 ArrayBuffer를 들여다보는 창일뿐이다. ArrayBuffer를 기반으로 한 TypedArray 객체를 여러 개 생성해도 메모리 중복이 발생하지 않는다 (낭비되지 않는다). 여러 개의 TypedArray 객체가 동일한 메모리 블록을 들여다볼 뿐이다.
const buffer = new ArrayBuffer(1024) // 1024 바이트 메모리 할당
const byteArray = new Uint8Array(buffer) // buffer를 바이트 배열로 다루는 view 생성
byteArray[0] = 1 // 1 바이트 메모리 쓰기
const data = byteArray[0] // 1 바이트 메모리 읽기
ArrayBuffer와 TypedArray 사이의 변환
ArrayBuffer와 TypedArray 사이의 변환은 숨 쉬는 것만큼 쉽다.
const buffer = new ArrayBuffer(1024) // 1024 바이트 메모리 할당
const byteArray = new Uint8Array(buffer) // ArrayBuffer로부터 TypedArray 생성
const buffer2 = byteArray.buffer // TypedArray로부터 ArrayBuffer 획득
console.log(buffer === buffer2) // true
// TypedArray로부터 획득한 버퍼가 원본 버퍼와 같은 것인지 확인
그래서 crypto 객체 같은 데이터 처리 모듈은 처리할 데이터를 인자로 받을 때, ArrayBuffer와 TypedArray를 구분하지 않고 모두 허용한다.
const buffer = new ArrayBuffer(1024) // 1024 바이트 메모리 할당
const byteArray = new Uint8Array(buffer) // ArrayBuffer로부터 TypedArray 생성
// (생략) 메모리 블록에 데이터 채우기
// (생략) 암호화에 필요한 iv, key 확보
const encryptedResult = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv }, key,
buffer
) // OK
const encryptedResult2 = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv }, key,
byteArray
) // OK
요약
메모리에 직접 접근하는 작업을 위한 전통적인 랭귀지는 C/C++였다. 이제 JavaScript로도 메모리에 직접 접근하는 작업이 가능하다. 랭귀지 사이의 우월을 논하는 것이 점점 의미 없어지고 있다. 어쩌면 이는 JavaScript의 성능과 활용도가 크게 향상됐음을 의미한다. 수많은 기계에 이미 탑재되어 있는, JavaScript를 지원하는 웹브라우저야말로 웹3 세상을 여는 강력한 플랫폼이 될 것이다.
Ref.