📚 CS/JavaScript

[JavaScript] 얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy)

dev.daisy 2025. 12. 13. 22:34
JavaScript에서 얕은 복사와 깊은 복사를 이해하는 핵심은 결국 참조와 메모리 주소라는 근본적인 개념에 달려있다는 것을 깨달았습니다. 단순히 문법을 외우는 것이 아니라, 객체와 배열이 메모리에 어떻게 저장되는지를 먼저 이해해야만 얕은 복사 시 발생하는 부수 효과의 원인을 명확히 파악할 수 있었습니다. 무엇보다 다루는 객체의 크기와 구조를 고려하여 성능과 안정성 사이에서 현명하게 선택해야 한다는 것을 알게 되었습니다.

JavaScript에서 객체와 배열 같은 참조 타입(Reference Type)의 데이터를 다룰 때, 단순히 = 연산자를 사용하거나 복사 기능을 사용할 때 우리가 예상하는 것과 다른 동작을 보일 때가 있습니다. 이 모든 것은 데이터가 저장되는 메모리 주소인 참조(Reference) 때문에 발생합니다.

  • 기본 타입 (Primitive Types: String, Number, Boolean 등): 값이 직접 변수에 저장됩니다. 복사하면 완전히 새로운 값이 메모리에 생성됩니다.
  • 참조 타입 (Reference Types: Object, Array, Function 등): 실제 데이터는 메모리의 한 공간에 저장되고, 변수에는 이 데이터가 위치한 메모리 주소(참조 값)만 저장됩니다. 복사한다는 것은 이 주소(참조)를 복사한다는 뜻입니다.

이러한 참조의 작동 방식에 따라 복사는 얕은 복사깊은 복사로 나뉩니다.


1. 얕은 복사 (Shallow Copy)

얕은 복사는 객체의 1단계 깊이까지만 값을 복사하고, 그 안에 중첩된 객체(참조 타입)에 대해서는 원본 객체와 같은 참조(메모리 주소)를 공유하는 복사 방식입니다.

 

이 복사본은 껍데기만 새로 만들었을 뿐, 중첩된 객체에 접근하는 통로원본과 동일하게 공유합니다.

윈도우 바탕화면 바로가기 아이콘으로 이해하자!

얕은 복사는 원본 파일의 바로가기 아이콘을 복사해서 만드는 것과 같습니다.

  • 복사본 객체가 생성되면 아이콘은 두 개로 보입니다.
  • 하지만 두 아이콘 중 어느 쪽을 클릭해도 똑같은 하나의 원본 파일이 열립니다.
  • 따라서 복사본 바로가기로 들어가서 원본 파일의 내용을 수정하면, 당연히 다른 바로가기를 클릭했을 때도 수정된 내용이 보입니다.

💾 얕은 복사의 실사용 예시와 문제점

얕은 복사는 ... (스프레드 연산자), Object.assign(), 또는 배열의 slice(), concat()과 같은 내장 기능을 사용할 때 발생하며, 우리가 복사라고 믿고 쓰는 기능들이 바로 이 얕은 복사입니다.

const original = { 
  id: 1, 
  user: { name: 'Alice', age: 30 } 
};

// 얕은 복사: 최상위 레벨을 새로 복사
const shallowCopy = { ...original }; 

// 1. 최상위 레벨 변경: 독립적
shallowCopy.id = 2; // 원본의 id는 변경되지 않음 (값 복사)

// 2. 중첩된 객체 변경: 참조 공유
shallowCopy.user.age = 31; // 복사본을 수정했지만, 원본의 'user' 객체도 함께 변경됨!

console.log(original.id);      // 1 
console.log(original.user.age); // 31 (의도치 않은 부수 효과 발생!)

React에서의 문제는?

리액트는 상태가 변했는지 감지하여 화면을 리렌더링(Re-rendering)할지 결정하는데, 이 때 변했는지 파악하는 주된 기준은 객체의 메모리 주소가 바뀌었는가입니다.

  1. 얕은 복사로 복사본을 만들고,
  2. 그 복사본의 중첩된 객체의 값만 바꾸면,
  3. 리액트 입장에서는 객체의 주소 값은 같으므로 상태가 변하지 않았다고 판단하여 리렌더링하지 않는 문제가 발생합니다.

얕은 복사의 장단점

구분 장점 (Pros) 단점 (Cons)
속도 매우 빠르다. 복사할 때 메모리 주소만 복사하기 때문에 성능상 이점이 있다. -
안정성 - 안정성이 낮다. 복사본을 수정했는데 원본 객체까지 변경되는 의도치 않은 부수 효과가 발생할 수 있다.

2. 깊은 복사 (Deep Copy)

깊은 복사는 원본 객체의 모든 레벨(중첩된 객체 포함)에 걸쳐 존재하는 모든 값을 새로 복사하여 새로운 메모리 공간에 독립적인 객체를 만드는 방식입니다.

깊은 복사본은 원본 객체와 같은 속성 값을 가지지만, 완전히 새로운 메모리 주소를 가집니다.

파일 복사 붙여넣기로 이해하기

깊은 복사는 문서 자체를 새로 만들어서 두 개의 파일이 생기는 것과 같습니다.

  • 복사본 파일을 아무리 수정해도 원본 파일에는 아무 영향을 주지 않습니다.
  • 이것이 바로 현대 프로그래밍에서 강조되는 불변성(Immutability)을 지키는 핵심입니다.

💾 깊은 복사의 구현 방법과 제약 사항

원본은 안전하게 지키고 복사본만 완전히 독립적으로 만들고 싶을 때 깊은 복사를 사용해야 합니다.

1. JSON 직렬화/역직렬화 (JSON Trick)

가장 흔하게 사용되는 방법입니다.

const original = { /* ... */ };
const deepCopy = JSON.parse(JSON.stringify(original));
  • 동작 원리:
    1. JSON.stringify()가 JS 객체를 받아 함수, 심볼 같은 복잡한 메모리 참조 정보를 싹 제거하고 순수한 데이터의 알맹이만 남겨서 JSON 문자열로 바꿔버립니다. (원본과의 연결이 끊김)
    2. JSON.parse()가 이 텍스트를 읽어서 메모리에 새로운 공간에 새로운 객체와 배열을 재구성합니다.
  • 제약 사항: JSON은 직렬화 가능한 데이터에만 작동합니다. 함수(Function), undefined, Symbol 같은 타입은 stringify 과정에서 사라지며, Date 객체는 문자열로 변환됩니다. 또한, 객체가 순환 참조 구조를 가지고 있으면 무한 루프에 빠집니다. 단순한 데이터 객체에만 제한적으로 사용하는 것이 안전합니다.

2. structuredClone() 메서드 (Web API)

최신 JavaScript 환경(브라우저 및 Node.js)에서 제공하는 표준적인 깊은 복사 방법입니다.

const deepCopy = structuredClone(original); 
  • 장점: JSON Trick의 단점을 보완하여, Date, RegExp, Map, Set, 순환 참조하는 복잡한 객체도 안전하게 잘 복사합니다.
  • 제약 사항: 이것도 완벽한 해결책은 아닙니다. 함수(Function)나 DOM Node처럼 직렬화(Serialization) 할 수 없는 객체들은 여전히 오류를 발생시킵니다. 하지만 JSON Trick보다 훨씬 안전하고 넓은 범위의 참조를 처리할 수 있는 구원투수입니다.

3. 외부 라이브러리 (_.cloneDeep())

가장 안전하고 완벽한 깊은 복사를 위해 Lodash와 같은 라이브러리의 _.cloneDeep() 함수에 의존해왔습니다.

깊은 복사의 장단점

구분 장점 (Pros) 단점 (Cons)
안정성 매우 안정적이다. 복사본을 수정해도 원본에 부수 효과가 전혀 없다. (데이터 불변성 확보) -
속도 - 느릴 수 있다. 객체의 크기가 매우 크다면 모든 레벨을 하나하나 복사하는 것은 상당한 메모리와 시간을 소요하는 비싼 작업이 됩니다.

객체 복사는 기본적인 작업이지만, JavaScript의 참조 구조 때문에 까다로운 문제입니다.

특징 얕은 복사 (Shallow Copy) 깊은 복사 (Deep Copy)
복사 대상 최상위 레벨의 값만 복사. 중첩 객체는 참조(주소) 공유. 모든 레벨의 값을 복사. 원본과 완전히 독립.
목적 성능 최적화, 객체의 불변성을 강제하지 않을 때. 데이터 불변성 보장, 원본 보호가 필수일 때.
대표적인 방법 ... (스프레드), Object.assign() JSON.parse(JSON.stringify(obj)), structuredClone(), 라이브러리

 

핵심적인 내용은 내가 지금 다루고 있는 데이터가 어떤 종류의 값을 담고 있는지 명확히 알고, 그 장단점을 고려하여 적절한 복사 방법을 선택하는 것입니다. 성능에 민감한 상황이라면 깊은 복사를 남발하지 않도록 주의해야 합니다.