얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)는 자바스크립트에 국한된 개념이 아니라 프로그래밍 언어 전반에 걸친 보편적인 개념입니다. 두 복사 방식의 핵심 차이는 중첩된 객체(참조 타입)까지 복사하느냐에 있습니다. 이 글에서는 예제 코드를 통해 두 방식의 차이를 명확하게 살펴보겠습니다.
얕은 복사(Shallow Copy)란?
얕은 복사는 최소한의 데이터만 복제하는 방식입니다. 컬렉션(객체나 배열)을 얕은 복사하면 컬렉션의 구조만 복사될 뿐, 내부에 담긴 개별 요소(참조 값)까지 복제되지는 않습니다. 따라서 얕은 복사 이후에는 원본과 복사본이 동일한 내부 객체를 함께 참조(공유)하게 됩니다.
예제
let innerObj = {
a: 'b',
c: 'd'
}
let obj = {
x: "test",
y: innerObj
}
// 얕은 복사 생성
let copyObj = Object.assign({}, obj);
// copyObj와 obj의 y 프로퍼티가 같은 innerObj를 참조하므로,
// 변경 사항이 양쪽 모두에 반영됩니다.
innerObj.a = "test"
console.log(obj)
console.log(copyObj)실행 결과
{ x: 'test', y: { a: 'test', c: 'd' } }
{ x: 'test', y: { a: 'test', c: 'd' } }위 결과에서 볼 수 있듯이, innerObj.a 값을 변경하자 원본 객체와 복사본 객체 모두에서 값이 변경되었습니다. 이는 두 객체가 같은 내부 객체를 공유하고 있기 때문입니다.
주의할 점은 얕은 복사는 재귀적으로 이루어지지 않는다는 것입니다. 복사는 오직 최상위 레벨(1단계 깊이)에서만 수행되며, 그 이하의 중첩 객체는 여전히 원본과 참조를 공유합니다.
깊은 복사(Deep Copy)란?
깊은 복사는 말 그대로 모든 것을 복제하는 방식입니다. 컬렉션을 깊은 복사하면 원본 컬렉션 내부의 모든 요소까지 완전히 새롭게 복제된, 완전히 독립적인 두 개의 컬렉션이 만들어집니다.
예제
let innerObj = {
a: 'b',
c: 'd'
}
let obj = {
x: "test",
y: innerObj
}
// 깊은 복사 생성
let copyObj = JSON.parse(JSON.stringify(obj))
// innerObj를 변경해도 copyObj에는 영향을 주지 않습니다.
innerObj.a = "test"
console.log(obj)
console.log(copyObj)실행 결과
{ x: 'test', y: { a: 'test', c: 'd' } }
{ x: 'test', y: { a: 'b', c: 'd' } }이번에는 innerObj.a를 변경했음에도 복사본 copyObj의 값은 그대로 'b'로 유지되었습니다. 깊은 복사를 통해 내부 객체까지 완전히 복제되었기 때문입니다.
정리: 얕은 복사 vs 깊은 복사
얕은 복사는 최상위 레벨만 복사하므로 중첩 객체는 원본과 참조를 공유하며, 대표적으로 Object.assign()이나 스프레드 연산자({...obj})를 사용해 구현할 수 있습니다. 반면 깊은 복사는 중첩된 모든 객체를 재귀적으로 복제하여 완전히 독립적인 복사본을 만듭니다.
참고로 JSON.parse(JSON.stringify()) 방식은 간편하지만 함수, undefined, Symbol, Date 객체 등은 제대로 복사하지 못한다는 한계가 있습니다. 최신 자바스크립트 환경에서는 이러한 문제를 해결한 structuredClone() 내장 함수를 사용하는 것이 권장됩니다.