Worker나 IndexedDB에 객체를 전달할 때 JavaScript는 객체 참조를 그대로 공유하는 대신 structured clone 알고리즘을 사용할 수 있습니다. 일부 객체는 transfer 목록에 넣어 복제 대신 전달할 수 있습니다. 두 방식은 같은 “메시지 보내기” 문법으로 보일 수 있지만, 복제본이 생기는지 원본을 계속 사용할 수 있는지가 다릅니다. 이 차이를 모르면 전달 뒤 데이터가 사라진 것처럼 보이거나 예상하지 못한 메모리 비용이 생길 수 있습니다.

복제본과 버퍼 소유권 이동의 개념도

실제 메모리 배치나 성능 수치를 나타내지 않는 개념 그림입니다.

1. Structured clone이 복제하는 값

HTML 표준은 structured serialization과 deserialization 절차를 정의합니다. structuredClone()은 이 알고리즘으로 지원되는 값을 복사하며, Worker의 postMessage()나 IndexedDB 저장처럼 서로 다른 실행 문맥 사이에 데이터를 전달하는 기능도 이를 사용합니다. 전달된 값은 수신 쪽에서 별도 객체로 재구성되므로 일반 JavaScript 객체의 동일 참조를 양쪽이 함께 조작하는 방식과 다릅니다.

“복사”가 임의의 JavaScript 객체를 완전히 그대로 보존한다는 뜻은 아닙니다. 함수 객체는 복제할 수 없고 복제 시 DataCloneError가 발생합니다. 사용자 정의 prototype과 descriptor는 보존되지 않습니다. 직렬화 중 getter가 실행될 수 있지만 접근자 자체는 복원되지 않습니다. 지원되는 그래프의 순환과 반복 참조 관계는 복제본 안에 보존됩니다. 보내려는 데이터는 API가 직렬화 지원하는 값으로 구성하고, 중요한 동작은 객체 메서드에 숨기지 말고 명시적인 데이터 구조로 표현하세요.

2. Transferable은 복제 대신 전달한다

일부 종류의 객체는 복사하지 않고 transfer할 수 있습니다. 예를 들어 큰 ArrayBuffer를 Worker에 보낼 때 worker.postMessage({ buffer }, [buffer])와 같이 transfer 목록을 사용합니다. 수신 쪽은 새 버퍼 래퍼를 받으며 송신 쪽의 기반 데이터는 분리되어 기존 뷰에서도 사용할 수 없습니다. 모든 래퍼 속성 접근이 예외를 던진다는 뜻은 아닙니다. 따라서 이는 “빠른 복사”의 다른 표기가 아니라 소유권 이전입니다.

전송은 두 문맥이 같은 메모리를 동시에 수정하는 공유 메모리와도 다릅니다. SharedArrayBuffer는 transfer할 수 없습니다. 지원되는 직렬화에서도 데이터는 공유되며 격리와 저장 제약이 적용됩니다. Transferable은 지원되는 객체의 소유권을 이동하는 경로입니다. 어떤 객체가 직렬화 가능한지, 전달 가능한지, 공유 가능한지는 각각 구분해야 하며, 하나의 범주가 다른 범주를 자동으로 포함하지 않습니다.

3. transfer 목록을 설계하고 원본 수명 관리

목록은 전송 방식을 정하며 페이로드에 값을 추가하지 않습니다. 이 버퍼 예제에서는 수신자가 접근하도록 페이로드에서 버퍼에 도달할 수 있어야 합니다. typed array 뷰가 아니라 기반 버퍼를 전송합니다. 중복 또는 비지원 항목은 실패할 수 있습니다. structuredClone(value, { transfer: [buffer] })처럼 복제 API에서도 transfer를 사용할 수 있습니다. 전달 후 송신 쪽 코드가 이전 버퍼를 다시 읽을 수 있다고 가정하지 말고, 전송 이후에는 수신자가 소유한다고 데이터 흐름을 설계하세요. 여러 단계에서 같은 버퍼가 필요하다면 복사할지, 소유권을 넘길지, 별도의 공유 메커니즘을 쓸지 먼저 선택해야 합니다.

작은 일반 객체를 무조건 transfer하려 하거나, 모든 메시지를 복제해도 성능 비용이 같다고 가정하지 마세요. 구조화 복제는 지원 값을 전달하는 표준 경로이고, transfer는 특정 종류의 데이터에만 사용할 수 있습니다. 최적화 전에 메시지 크기와 복사 비용을 측정하고, 목록에서 빠진 값도 유형과 목적지에 따라 확인합니다. 모든 transfer가 물리적 복사 없이 이뤄진다고 가정하지 않습니다.

4. 실무자 관점: 전송 경계에서 검증

Surma의 당시 해설은 얕은 복사와 구조화 복제를 구분합니다. 과거 성능 논의는 여기서 측정한 결과가 아닙니다. Worker 메시지 API를 설계할 때 허용할 데이터 모양, 복제 불가 값 처리, transfer 대상, 송신 쪽에서 전송 후 접근 가능한 데이터를 문서화하세요. 수신 쪽은 메시지의 값과 형식을 검증해야 합니다. Structured clone은 신뢰성 검증이나 입력 보안 정책을 대신하지 않습니다. 복제된다고 해서 그 데이터가 안전하거나 애플리케이션의 기대와 일치한다는 뜻은 아닙니다.

테스트에서는 버퍼가 실제로 수신됐는지, transfer 후 송신 측 원본이 분리됐는지, 함수나 unsupported 값이 전달될 때 어떻게 실패하는지 확인합니다. 메모리를 공유해야 한다면 transfer 목록을 억지로 사용하지 말고 공유 메모리 API와 동기화 요구를 별도로 검토하세요. 동작 보존이 필요한 객체는 plain data로 변환한 뒤 필요한 로직을 수신 쪽에서 재구성하는 편이 명확합니다.

참고 자료