Structured Clone — 깊은 복사의 표준, JSON.parse 대체
"자바스크립트에서 깊은 복사를 어떻게 하나요?"는 면접 단골 질문입니다. 과거에는
JSON.parse(JSON.stringify())가 대표적이었지만, 이제는structuredClone이라는 표준 API가 있습니다.
얕은 복사 vs 깊은 복사
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 기본
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()) 방식의 한계
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이 처리할 수 있는 타입
// 지원되는 타입
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과 함께 사용
// 대용량 데이터를 복사 대신 이전 (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 — 소유권 이전됨
순환 참조 처리
// 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.assign | JSON.parse(stringify) | structuredClone |
|---|---|---|---|
| 깊이 | 얕은 복사 | 깊은 복사 | 깊은 복사 |
| Date | 참조 공유 | 문자열로 변환 | 올바르게 복사 |
| Map/Set | 참조 공유 | 빈 객체 | 올바르게 복사 |
| RegExp | 참조 공유 | 빈 객체 | 올바르게 복사 |
| 함수 | 참조 공유 | 사라짐 | TypeError |
| 순환 참조 | X | TypeError | 지원 |
| 성능 | 빠름 | 보통 | 보통 |
| undefined | 유지 | 사라짐 | 유지 |
| NaN | 유지 | null | 유지 |
실전 활용
// 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 노드는 복제할 수 없다는 점만 기억하면 됩니다.
댓글 로딩 중...