개요

값 복사에서 가장 흔한 실수는 얕은 복사와 깊은 복사의 차이를 간과하는 것임 이 글은 JSON 해킹이나 라이브러리 대신 브라우저와 런타임이 제공하는 structuredClone으로 깊은 복사를 안정적으로 수행하는 방법과 주의점을 정리함

얕은 복사

JavaScript의 전개 연산자 …로 만드는 복사는 기본적으로 얕은 복사임 겉 모양과 1단계 속성은 복제되지만 중첩된 객체는 동일 참조를 공유함

const myOriginal = {
  someProp: "with a string value",
  anotherProp: {
    withAnotherProp: 1,
    andAnotherProp: true
  }
}

const myShallowCopy = { ...myOriginal }

myShallowCopy.aNewProp = "a new value"
console.log(myOriginal.aNewProp)
// ^ logs `undefined`

myShallowCopy.anotherProp.aNewProp = "a new value"
console.log(myOriginal.anotherProp.aNewProp)
// ^ logs `a new value`

기본형 값은 값 자체가 복사되지만 객체 같은 참조형은 레퍼런스가 복사됨 따라서 중첩된 구조를 안전하게 분리하려면 깊은 복사가 필요함

깊은 복사

깊은 복사는 객체 그래프를 따라가며 재귀적으로 새 값을 만들어 원본과 내부 상태를 공유하지 않게 하는 것 과거에는 lodash의 cloneDeep이나 JSON.parse(JSON.stringify()) 같은 차선책을 많이 사용함

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal))

JSON 기반 방식은 빠른 편이지만 제약이 큼

  • 순환 참조 구조에서 예외 발생
  • Map, Set, Date, RegExp, ArrayBuffer 등 내장 타입 손실 또는 예외 발생
  • 함수는 직렬화 과정에서 사라짐

structuredClone 소개

웹 플랫폼과 런타임 전반에서 값 전달과 저장을 위해 사용해 온 알고리즘이 구조적 복제 structured clone임 IndexedDB 저장 또는 postMessage를 통한 영역 간 전송이 대표 사례 이제 동일 알고리즘이 structuredClone API로 공개되어 일반 코드에서 직접 사용 가능

const myDeepCopy = structuredClone(myOriginal)

핵심 포인트

  • 순환 참조 처리 가능
  • 주요 내장 타입 지원 범위 넓음
  • 일반적으로 JSON 해킹 대비 더 강력하고 상황에 따라 더 빠름

동작과 제약

structuredClone의 동작과 한계는 다음과 같음

  • 프로토타입 체인 보존 안 됨, 사용자 정의 클래스 인스턴스는 일반 객체로 복제되는 경향
  • 함수는 복제 대상 아님, 존재 시 폐기됨
  • 일부 값은 복제 불가, 대표적으로 DOM 노드 등
  • Error 계열은 환경과 구현에 따라 지원 범위 상이할 수 있음, 필요 시 대상 환경에서 호환성 확인 권장

이 제약이 문제라면 라이브러리 기반 커스텀 깊은 복사 전략이 대안이 될 수 있음 예를 들어 특정 클래스 인스턴스는 toJSON과 reviver 같은 별도 프로토콜로 복원 전략을 설계하거나, 타입별 분기 처리로 수동 복제를 구현하는 방식 고려

지원 범위

현대 브라우저, Node.js, Deno 등 주요 환경에서 structuredClone 사용 가능 레거시 환경 대상이라면 가능 여부를 사전에 확인하거나 폴백 전략 필요

성능 관점

소형 단순 객체는 JSON 방식도 매우 빠른 편이나 타입 손실과 제약이 존재함 구조가 크거나 다양한 내장 타입을 포함하는 경우 structuredClone이 더 일관되고 유리한 선택지인 경우가 많음 오버헤드 없이 직접 제공되는 API이므로 깊은 복사의 기본 선택지로 두는 것이 합리적임

예시와 베스트프랙티스

  • 불변성 유지를 위해 사본을 만들어 변형해야 할 때 기본적으로 structuredClone 사용
  • 성능 임계 경로에서는 실제 데이터 형태와 크기로 벤치마크 후 선택
  • 사용자 정의 클래스가 섞여 있다면 복제 이후 원하는 형태로 재구성하는 어댑터 계층 두기
  • 복제 불가 대상이 포함될 수 있으면 사전 필터링 또는 스키마 설계 단계에서 제거

마무리

깊은 복사를 안전하게 처리해야 하는 상황에서 더 이상 JSON 해킹이나 범용 라이브러리에 의존할 필요가 줄어듦 structuredClone을 기본값으로 채택하고, 제약이 충돌하는 특수 케이스에만 보완 전략을 추가하는 접근 추천

참고자료