structureClone

JavaScript에서 값을 복사할때는 항상 얕은 복사 (shallow copy)를 수행한다. 따라서 primitive data type (string, number, bigint, boolean, undefined, symbol등) 이 아닌 경우 객체의 참조로 처리된다. 얕은 복사의 반대는 깊은 복사 (deep copy)인데, 이는 복사 중 다른 객체를 참조를 찾으면 재귀적으로 참조의 내용도 찾아 복사를 수행한다. 이렇게 참조가 있는 한 객체를 깊은 복사하는 것은 꽤 까다로운 일이다. 결과물인 복사된 객체에서 원본이 가르키는 어떤 참조도 중첩하여 갖게해서는 안되기 때문이다.

JavaScript에서는 이러한 깊은 복사를 수행하는 제공되지 않는다. 그래서, 오랜동안 JSON.stringify/parse 를 이용한 일종의 트릭을 이용한 깊은 복사 방법을 제공하는 라이브러리에 의존해왔다.

그러나 Web Platform에서는 IndexedDB 저장이나, WebWorker간 MessageChannel 메세지 전송등의 Spec.을 구현하고자 할때 JavaScript 직렬화/역직렬화 방법이 필요하였다. 이를 구현한 것이 structuredClone이며, 함수 내부적으로 복사를 어떻게 수행하는지는 structured clone algorithm이라고 부르는 Spec.에 의해 정의되어있다. 모든 타입의 JavaScript 객체가 이 알고리즘에 의해 처리되는 것은 아니며, JavaScript 의 Builtin Type뿐 아니라 Web Spec. 정의된 일부 객체들도 복사가 가능하도록 되어있다.

API

API의 사용 방법은 아래와 같은데, 두번째 파라미터가 정의되었을 때의 동작이 눈여겨 볼만하다.

1
2
structuredClone(value);
structuredClone(value, options);
  • 첫번째 파라미터는 structured clone algorithm이 지원하는 타입의 복사본을 만든다.

  • 2번째 파라미터 optional 한 option 값인데 현재 options.transfer : Array 를 지원한다. 이 옵션을 통해 전달되는 값은 Transferable objects의 배열이 가능하며, Transferable objects의 대표적인 타입으로는 ArrayBuffer, Readable/WritableStream등이 있다. 복사가 아닌 transfer를 이용할 때의 장점은 두가지를 들 수 있다.

    1. 소유권의 이전 - transfer 되는 리소스를 하나의 JavaScript Context에서만 사용할 수 있게 소유권을 제한할 수 있다.

    2. 성능 - transfer 객체와 환경에 따라 zero-copy operation을 이용하여 빠르게 다른 context로 넘길 수 있다. 이는 특히 WebWorker와 같이 한 thread에서 다른 thread로 데이터를 넘길때 유용하다.

1
2
3
4
5
6
7
8
const buffer1 = new ArrayBuffer(16);
const object1 = {
buffer: buffer1,
};
const object2 = structuredClone(object1, { transfer: [buffer1] });

// Creating an array from the original buffer throws a TypeError
const int32View1 = new Int32Array(object1.buffer);
1
2
3
4
const object1 = {
amanita: ['muscaria', 'virosa'],
};
const object2 = structuredClone(object1); // cloned

참고

Share