
structuredClone()은 네이티브 깊은 복사 API다. Date, Map, Set, 순환 참조까지 처리하고, 라이브러리도 필요 없다.
흔히 겪는 시나리오가 있다. API에서 받아온 객체를 깊은 복사해서 폼 초기값으로 세팅했다. 나중에 "변경사항 비교"를 하려고 원본과 현재 값을 비교하는데, 원본의 createdAt이 Date가 아니라 문자열이 되어있다. .getMonth()를 호출하면 TypeError. 원인은 이 한 줄이다.
const copy = JSON.parse(JSON.stringify(original));
직관적이라 관성적으로 쓰게 되지만, 이 방식은 JSON으로 변환할 수 없는 값을 전부 날려버린다.
const original = {
date: new Date('2026-04-17'),
tags: new Set(['TIL', 'JS']),
metadata: new Map([['key', 'value']]),
score: Infinity,
optional: undefined,
};
const copy = JSON.parse(JSON.stringify(original));
console.log(copy);
// {
// date: "2026-04-17T00:00:00.000Z", ← 문자열로 변환됨
// tags: {}, ← Set이 빈 객체로
// metadata: {}, ← Map이 빈 객체로
// score: null, ← Infinity가 null로
// ← undefined는 아예 사라짐
// }
Date가 문자열이 되고, Set/Map은 빈 객체가 되고, undefined는 증발한다. 여기에 순환 참조가 있으면 TypeError로 아예 터진다.
2022년부터 모든 주요 브라우저와 Node.js 17+에서 사용 가능한 네이티브 API다.
const copy = structuredClone(original);
const obj = { created: new Date('2026-04-17') };
const copy = structuredClone(obj);
console.log(copy.created instanceof Date); // true
console.log(copy.created.getFullYear()); // 2026
JSON.stringify를 거치면 문자열이 되어 .getFullYear() 같은 메서드를 호출할 수 없지만, structuredClone은 Date 객체를 그대로 유지한다.
const obj = {
tags: new Set(['TIL', 'JS']),
config: new Map([['theme', 'dark']]),
pattern: /^hello/gi,
};
const copy = structuredClone(obj);
console.log(copy.tags.has('TIL')); // true
console.log(copy.config.get('theme')); // 'dark'
console.log(copy.pattern.test('hello!')); // true
const obj = { name: 'root' };
obj.self = obj; // 순환 참조
// JSON.parse(JSON.stringify(obj)); ← TypeError: Converting circular structure to JSON
const copy = structuredClone(obj); // 정상 동작
console.log(copy.self === copy); // true (순환 구조 유지)
만능은 아니다. structuredClone이 복사할 수 없는 타입이 있다.
| 타입 | 결과 |
|---|---|
| 함수 | DataCloneError 발생 |
| DOM 노드 | DataCloneError 발생 |
| Symbol | DataCloneError 발생 |
| 클래스 인스턴스 | plain object로 변환 (프로토타입 체인 소실) |
class User {
constructor(name) { this.name = name; }
greet() { return `Hi, ${this.name}`; }
}
const user = new User('arin');
const copy = structuredClone(user);
console.log(copy.name); // 'arin' (데이터는 복사됨)
console.log(copy instanceof User); // false (프로토타입 소실)
console.log(copy.greet); // undefined (메서드 없음)
함수가 포함된 객체를 복사해야 한다면 structuredClone은 답이 아니다. 그 경우엔 수동으로 복사 로직을 작성하거나 lodash의 _.cloneDeep을 쓰는 게 맞다.
structuredClone 후 수정_.cloneDeep 하나 때문에 lodash를 쓰고 있었다면, 네이티브로 대체 가능