"자바스크립트에서 깊은 복사를 어떻게 하나요?"는 면접 단골 질문입니다. 과거에는 JSON.parse(JSON.stringify())가 대표적이었지만, 이제는 structuredClone이라는 표준 API가 있습니다.

얕은 복사 vs 깊은 복사

JS
const original = {
  name: "정훈",
  scores: [90, 85, 95],
  address: { city: "서울" },
};

// 얕은 복사 — 중첩 객체는 참조를 공유
const shallow = { ...original };
shallow.scores.push(100);
console.log(original.scores); // [90, 85, 95, 100] — 영향 받음!

// 깊은 복사 — 완전히 독립적인 사본
const deep = structuredClone(original);
deep.scores.push(100);
console.log(original.scores); // [90, 85, 95] — 영향 없음

structuredClone 기본

JS
const original = {
  name: "정훈",
  date: new Date("2026-01-01"),
  data: new Map([["key", "value"]]),
  set: new Set([1, 2, 3]),
  regex: /hello/gi,
  nested: { deep: { value: 42 } },
};

const clone = structuredClone(original);

// 모든 타입이 올바르게 복사됨
console.log(clone.date instanceof Date);   // true
console.log(clone.data instanceof Map);    // true
console.log(clone.set instanceof Set);     // true
console.log(clone.regex instanceof RegExp); // true
console.log(clone.nested.deep.value);      // 42

// 완전히 독립적
clone.nested.deep.value = 0;
console.log(original.nested.deep.value);   // 42

JSON.parse(JSON.stringify()) 방식의 한계

JS
const obj = {
  date: new Date(),        // Date → 문자열이 됨!
  regex: /test/gi,         // RegExp → 빈 객체 {}
  map: new Map([["a", 1]]), // Map → 빈 객체 {}
  set: new Set([1, 2]),    // Set → 빈 객체 {}
  fn: () => {},            // 함수 → 사라짐!
  undef: undefined,        // undefined → 사라짐!
  nan: NaN,                // NaN → null
  inf: Infinity,           // Infinity → null
};

const jsonClone = JSON.parse(JSON.stringify(obj));
// date: "2026-03-28T..." (문자열)
// regex: {} (빈 객체)
// map: {} (빈 객체)
// fn: 없음
// undef: 없음
// nan: null

structuredClone이 처리할 수 있는 타입

JS
// 지원되는 타입
structuredClone({
  primitives: [1, "hello", true, null, undefined],
  date: new Date(),
  regexp: /pattern/gi,
  map: new Map(),
  set: new Set(),
  arrayBuffer: new ArrayBuffer(8),
  typedArray: new Uint8Array([1, 2, 3]),
  blob: new Blob(["hello"]),
  error: new Error("test"),
  // ... 대부분의 내장 객체
});

// 지원되지 않는 타입
structuredClone({
  fn: () => {},         // TypeError: 함수는 복제 불가
  dom: document.body,   // TypeError: DOM 노드 불가
  symbol: Symbol("id"), // TypeError: Symbol 불가
});

Transferable과 함께 사용

JS
// 대용량 데이터를 복사 대신 이전 (Web Worker에서 유용)
const buffer = new ArrayBuffer(1024 * 1024); // 1MB
const clone = structuredClone(buffer, { transfer: [buffer] });

console.log(buffer.byteLength);  // 0 — 원본은 비워짐
console.log(clone.byteLength);   // 1048576 — 소유권 이전됨

순환 참조 처리

JS
// JSON 방식은 순환 참조에서 에러 발생
const obj = { a: 1 };
obj.self = obj;

// JSON.stringify(obj); // TypeError: circular structure

// structuredClone은 순환 참조를 처리할 수 있음
const clone = structuredClone(obj);
console.log(clone.self === clone); // true — 순환 참조 유지

방법별 비교

기능{...obj} / Object.assignJSON.parse(stringify)structuredClone
깊이얕은 복사깊은 복사깊은 복사
Date참조 공유문자열로 변환올바르게 복사
Map/Set참조 공유빈 객체올바르게 복사
RegExp참조 공유빈 객체올바르게 복사
함수참조 공유사라짐TypeError
순환 참조XTypeError지원
성능빠름보통보통
undefined유지사라짐유지
NaN유지null유지

실전 활용

JS
// 1. 상태 관리에서 불변성 유지
function reducer(state, action) {
  const newState = structuredClone(state);
  // newState를 안전하게 수정
  return newState;
}

// 2. API 응답 캐시
const cache = new Map();
function getCachedData(key) {
  const data = cache.get(key);
  return data ? structuredClone(data) : null; // 캐시 원본 보호
}

// 3. Undo/Redo 기능
const history = [];
function saveState(state) {
  history.push(structuredClone(state));
}

**기억하기 **: structuredClone은 자바스크립트 깊은 복사의 표준입니다. JSON.parse(JSON.stringify())의 한계(Date, Map, Set, undefined 등)를 모두 해결합니다. 함수와 DOM 노드는 복제할 수 없다는 점만 기억하면 됩니다.

댓글 로딩 중...