[JavaScript] ArrayBuffer & SharedArrayBuffer

seungjun.dev·2025년 7월 26일

JavaScript

목록 보기
1/8
post-thumbnail

ArrayBuffer

독립적인 데이터 조각

  • 고정된 크기의 연속적인 메모리 공간을 나타내는 객체

  • 데이터를 읽고 쓰기 위해 데이터 뷰가 필요

  • 동작 방식: 메인 스레드와 워커 스레드는 완전한 동일한 내용을 가진, 하지만 서로 다른 메모리 공간에 존재하는 별개의 ArrayBuffer를 갖게 된다

  • 장점: 각 스레드가 독립적인 데이터 복사본을 가지므로 여러 스레드가 동시에 데이터에 접근할 때 발생하는 복잡한 동시성 문제(레이스 컨디션)을 걱정할 필요가 없음

  • 단점: 데이터의 크기가 클수록 복사하는 데 상당한 시간과 자원이 소모됨 -> 성능 저하

ArrayBuffer 사용 과정

1. 메모리 공간 생성

  • new ArrayBuffer() 생성자를 사용하여 고정된 크기의 원시 바이너리 데이터 버퍼를 바이트 단위로 생성
// 16바이트 크기의 버퍼 생성
const buffer = new ArrayBuffer(16);

2. 데이터 View 생성

  • View: ArrayBuffer 자체는 원시적인 바이트 덩어리이므로, Int32Array나 Uint8Array같은 TypedArray를 통해 데이터를 읽고 쓸 수 있는 뷰를 만들어야 한다.

  • TypedArray: Uint8Array, Int32Array 등 특정 숫자 형식으로 버퍼를 다룰 때 사용한다. 버퍼를 일정한 크기(예: 8비트, 32비트)로 나누어 배열처럼 다룰 수 있게 해준다.

  • DataView: 버퍼 내 다른 형식의 데이터를 다룰 때 유연성을 제공하며, 특정 오프셋에서 원하는 형식으로 데이터를 읽고 쓸 수 있다.

3. 워커에 전달

  • 생성된 ArrayBuffer 객체는 worker.postMessage() 를 통해 웹 워커로 전달할 수 있다.
    두 가지 방식이 가능하다.

  • 데이터 복사 (기본): postMessage 호출 시 데이터 전체가 복사되어 워커에 새로운 버퍼가 생성된다.
    이 방식은 데이터 크기가 클 경우 성능 저하를 유발할 수 있다.

  • 소유권 이전 (Transferable): ArrayBuffer소유권 이전이 가능한 객체(Transferable Object)다.
    postMessage 의 두 번째 인자로 버퍼를 전달하면, 데이터 복사 없이 소유권이 워커로 즉시 이전된다. 이 경우, 원래 스레드(예: 메인 스레드)에서는 해당 버퍼에 더 이상 접근할 수 없게 된다. 이 방식이 훨씬 효율적이다.

// buffer의 소유권을 워커로 이전
worker.postMessage(buffer, [buffer]);

4. 데이터 조작

  • 메인 스레드와 워커 스레드는 동시에 동일한 메모리에 접근할 수 없다.
    소유권을 이전받은 스레드만이 버퍼의 데이터를 읽고 쓸 수 있다.

워커는 전달받은 버퍼를 자체 뷰를 통해 수정한다.

수정된 데이터를 다시 메인 스레드로 보내야 할 경우, 워커는 다시 postMessage를 통해 버퍼의 소유권을 메인 스레드로 이전해야 한다.

이러한 방식 때문에 여러 스레드가 동시에 데이터를 수정하는 경쟁 상태(Race Condition)가 발생하지 않으므로, Atomics 객체를 사용한 동기화가 필요 없다.

SharedArrayBuffer

공유되는 메모리 공간

  • ArrayBuffer와 똑같이 고정된 크기의 연속적인 메모리 공간을 나타냄

  • 핵심적인 차이는 데이터를 다른 스레드에 전달할 때에 있다

    • 데이터를 복사하지 않고, 해당 메모리 공간에 대한 참조만 공유
  • 동작 방식: 메인 스레드와 워커 스레드가 완전히 동일한 메모리 주소를 바라보게 됨

    • 한 스레드에서 SharedArrayBuffer의 데이터를 변경하면, 다른 스레드에서도 즉시 그 변경 사항을 확인할 수 있다
  • 장점: 데이터 복사 과정이 없으므로 매우 빠르고 효율 적이다.

    • 대용량 데이터를 여러 스레드에서 동시에 처리하는 고성능 병렬 컴퓨팅에 필수적
  • 단점: 여러 스레드가 동일한 메모리에 동시에 접근 가능하므로, 레이스 컨디션과 같은 문제가 발생할 수 있다.

SharedArrayBuffer 사용 과정

1. 메모리 공간 생성

  • new SharedArrayBuffer()를 사용해 공유할 메모리 공간(버퍼)을 바이트 단위로 생성

2. 데이터 View 생성

  • View: 버퍼 자체는 원시적인 바이트 덩어리이므로, Int32ArrayUint8Array같은 TypedArray를 통해 데이터를 읽고 쓸 수 있는 를 만들어야 한다.

3. 워커에 공유

  • 생성된 SharedArrayBuffer 객체를 worker.postMessage()를 통해 웹 워커로 전달한다.
  • 이때 데이터가 복사되지 않고 참조만 넘어간다.

4. 데이터 조작 및 동기화

  • 메인 스레드와 워커 스레드는 각자의 뷰를 통해 동일한 메모리 공간에 값을 읽고 쓸 수 있다.
  • 이떄 발생하는 레이스 컨디션 문제는 Atomics 객체를 통해 동기화로 해결한다.

Data View

ArrayBufferSharedArrayBuffer는 그 자체로는 의미가 없는 원시 이진 데이터다.
단순히 메모리 공간에 연속된 바이트들을 확보해 놓은 것에 불과하다.
이 안의 데이터를 직접 읽거나 쓸 수는 없다.

이런 데이터를 해석하고 조작할 수 있는 통로 역할을 하는 객체가 데이터 뷰이다.
어떤 자료형(e.g. 8비트 정수, 32비트 부동소수점 수)으로, 어떤 순서로 데이터를 읽고 쓸지 결정하는 역할을 한다.

JS에서 사용하는 데이터 뷰는 두 가지 종류가 있다.

형식화 배열 (TypedArray)

ArrayBufferSharedArrayBuffer 의 전체 데이터를 하나의 통일된 숫자 형식으로 해석하는 뷰이다.
Int8Array, Uint32Array, Float64Array 등이 모두 TypedArray의 한 종류이다.

// 8바이트 크기의 메모리 공간(버퍼)을 생성
const buffer = new ArrayBuffer(8);

// Int32Array 뷰로 버퍼를 해석 (4바이트 정수 2개)
const view_int32 = new Int32Array(buffer);
view_int32[0] = 987654321;
console.log(view_int32[0]); // 987654321

Web Worker

메인 스레드와 완전히 분리된 백그라운드 스레드에서 스크립트를 실행할 수 있게 해주는 기술

  • 웹 워커가 필요한 이유

    • 메인 스레드 블로킹 문제
    • 무거운 작업을 별도의 워커 스레드로 보내 처리함으로써, 메인 스레드는 UI 렌더링이나 사용자 상호작용과 같은 중요한 작업을 멈춤 없이 계속 수행할 수 있음
  • 특징

    • 독립된 스레드에서 실행
      • 진정한 의미의 멀티 스레딩을 할 수 있다
    • DOM 접근 불가
    • 메시지 기반 통신
    • 제한된 API 접근
profile
Web FE Dev | Microsoft Student Ambassadors Alumni

0개의 댓글