JavaScript 얕은 복사와 깊은 복사 차이
JavaScript의 객체를 다른 변수에 대입한다고 복사본이 생기지는 않는다.
얕은 복사는 최상위 구조만 새로 만들고 중첩 객체의 참조는 공유한다.
깊은 복사는 중첩된 값까지 독립적인 구조로 만든다.
어떤 방식이 필요한지는 중첩 객체를 공유해도 되는지에 따라 달라진다.
대입과 복사의 차이
객체를 대입하면 새 객체가 아니라 기존 객체를 가리키는 별칭이 생긴다.
두 변수는 같은 객체를 참조하므로 어느 쪽에서 속성을 바꾸든 동일한 결과를 본다.
const original = {
profile: { name: 'Kim' },
}
const alias = original
alias.profile.name = 'Lee'
console.log(original.profile.name) // 'Lee'
console.log(alias === original) // true복사 여부를 확인할 때는 최상위 객체와 중첩 객체의 일치 비교를 따로 보는 편이 좋다.
얕은 복사의 참조 구조
객체 spread와 Object.assign()은 새 최상위 객체를 만든다.
배열 spread, slice(), concat(), Array.from()도 새 배열을 만들지만 중첩 객체는 공유한다.
const original = {
active: true,
profile: { name: 'Kim' },
}
const copy = { ...original }
copy.active = false
copy.profile.name = 'Lee'
console.log(original.active) // true
console.log(original.profile.name) // 'Lee'
console.log(copy !== original) // true
console.log(copy.profile === original.profile) // true최상위 속성을 재할당하는 작업은 원본에 영향을 주지 않는다.
공유된 중첩 객체의 속성을 바꾸면 원본에도 변경이 보인다.
깊은 복사와 structuredClone
structuredClone()은 구조화 복제 알고리즘으로 값의 깊은 복사본을 만든다.
일반 객체와 배열 외에도 Date, Map, Set, 순환 참조 같은 여러 값을 처리할 수 있다.
const original = {
settings: { theme: 'dark' },
visitedAt: new Date('2026-07-23T00:00:00Z'),
tags: new Set(['javascript']),
}
original.self = original
const copy = structuredClone(original)
console.log(copy.settings === original.settings) // false
console.log(copy.visitedAt instanceof Date) // true
console.log(copy.tags instanceof Set) // true
console.log(copy.self === copy) // true복사본의 중첩 객체를 변경해도 원본의 동일한 중첩 객체에는 영향을 주지 않는다.
그러나 structuredClone()이 모든 JavaScript 값의 의미를 그대로 재현하는 범용 복제 함수는 아니다.
JSON 복사 방식의 한계
JSON.stringify() 결과를 JSON.parse()하는 패턴도 중첩된 일반 객체의 참조를 분리할 수 있다.
하지만 이 방식은 JavaScript 객체 복사가 아니라 JSON으로 직렬화했다가 다시 읽는 과정이다.
const original = {
createdAt: new Date('2026-07-23T00:00:00Z'),
tags: new Set(['javascript']),
note: undefined,
}
const copy = JSON.parse(JSON.stringify(original))
console.log(copy)
// { createdAt: '2026-07-23T00:00:00.000Z', tags: {} }Date는 문자열로 바뀌고 Set과 Map의 항목은 기본 설정에서 보존되지 않는다.
객체의 undefined, 함수, Symbol 값은 생략되며 BigInt와 순환 참조는 오류를 발생시킨다.
JSON으로 표현할 수 있는 데이터라는 제약이 확실할 때만 사용해야 한다.
structuredClone의 제약
함수와 DOM 노드는 구조화 복제 대상이 아니며 복제를 시도하면 DataCloneError가 발생한다.
사용자 정의 클래스의 프로토타입 체인도 복제되지 않는다.
속성 설명자와 getter, setter, 클래스 private 요소 같은 메타데이터도 유지되지 않는다.RegExp는 복제할 수 있지만 lastIndex 속성은 보존되지 않는다.
복제할 값에 이런 요소가 있다면 실행 전에 지원 범위를 확인해야 한다.
복사 방식 선택 기준
최상위 속성만 다르게 만들거나 중첩 객체를 의도적으로 공유할 때는 spread와 Object.assign()으로 충분하다.
지원되는 값의 전체 구조를 독립시켜야 할 때는 structuredClone()을 우선 검토한다.
JSON 왕복은 입력이 JSON 호환 데이터이고 직렬화에 따른 변환을 의도했을 때만 사용한다.
깊은 복사가 필요한 범위를 먼저 정하고 가능하면 변경할 가지만 새로 만드는 편이 의도를 읽기 쉽다.
map()이 새 배열을 반환해도 요소 객체는 공유될 수 있다.
배열 메서드의 원본 변경 기준은 JavaScript map과 forEach 차이에서 함께 확인할 수 있다.
참고 자료


















