카테고리 없음

깊은 복사(Deep Copy) vs 얕은 복사(Shallow Copy)

머니지니87 2026. 2. 2. 13:59
반응형

소프트웨어 개발, 특히 자바스크립트(JavaScript)나 파이썬(Python)과 같은 언어를 다루다 보면 의도치 않게 원본 데이터가 변경되어 발생하는 치명적인 버그를 마주하게 됩니다. 변수에 값을 할당하거나 객체를 복사했다고 생각했지만, 실제로는 메모리 주소만 참조되어 원본과 복사본이 서로 영향을 주고받는 현상입니다. 이러한 문제는 데이터의 무결성을 해치고, 디버깅을 어렵게 만드는 주된 원인이 됩니다. 본 글에서는 프로그래밍의 메모리 관리 핵심 개념인 깊은 복사(Deep Copy)얕은 복사(Shallow Copy)의 차이점을 명확히 분석하고, 상황별 최적의 적용 방법을 기술적으로 제시합니다.

1. 메모리 구조와 데이터 타입의 이해

복사의 개념을 정확히 이해하기 위해서는 먼저 프로그래밍 언어가 데이터를 메모리에 저장하는 방식을 파악해야 합니다. 데이터 타입은 크게 원시 타입(Primitive Type)참조 타입(Reference Type)으로 나뉘며, 이는 각각 다른 메모리 영역을 사용합니다.

1-1. 원시 타입과 스택(Stack) 영역

숫자(Number), 문자열(String), 불리언(Boolean)과 같은 원시 타입 데이터는 값 자체가 메모리의 스택(Stack) 영역에 저장됩니다. 이 경우 변수에 다른 변수를 할당하면, 값 자체가 복사되어 독립적인 메모리 공간을 차지하게 됩니다. 따라서 원본을 수정해도 복사본에는 전혀 영향을 주지 않습니다.

1-2. 참조 타입과 힙(Heap) 영역

객체(Object), 배열(Array), 함수(Function) 등의 참조 타입은 데이터의 크기가 유동적이므로 힙(Heap) 영역에 실제 데이터가 저장됩니다. 그리고 변수(식별자)가 있는 스택 영역에는 힙 영역의 실제 데이터를 가리키는 메모리 주소(Reference)만 저장됩니다. 바로 이 지점에서 얕은 복사와 깊은 복사의 차이가 발생합니다.

2. 얕은 복사 (Shallow Copy)의 메커니즘

얕은 복사는 객체의 참조값(메모리 주소)을 복사하는 방식입니다. 즉, 새로운 객체를 생성하긴 하지만, 내부의 중첩된 객체(Nested Object)에 대해서는 원본 객체와 동일한 메모리 주소를 공유합니다.

2-1. 얕은 복사가 발생하는 상황

자바스크립트에서 흔히 사용하는 할당 연산자, 전개 구문(Spread Syntax), Object.assign() 등이 대표적인 얕은 복사의 예입니다. 1단계(Depth 1) 속성은 복사되는 것처럼 보이지만, 내부에 객체가 또 존재할 경우 문제가 발생합니다.


// 얕은 복사 예시 코드 (JavaScript)
const original = {
    name: "Aros",
    details: { age: 20, rank: "Expert" }
};

const shallowCopy = { ...original }; // 전개 구문 사용

shallowCopy.name = "Tistory"; // 1단계 속성 변경 (원본 영향 없음)
shallowCopy.details.age = 30; // 2단계 중첩 객체 변경 (원본도 함께 변경됨!)

console.log(original.details.age); // 출력: 30 (20이 아님)

위 코드에서 볼 수 있듯, details라는 중첩 객체는 원본과 복사본이 같은 주소를 바라보고 있기 때문에, 복사본을 수정했음에도 원본 데이터가 훼손되는 사이드 이펙트(Side Effect)가 발생합니다.

3. 깊은 복사 (Deep Copy)의 구현과 중요성

깊은 복사는 데이터의 참조값뿐만 아니라, 힙 영역에 있는 실제 데이터까지 완벽하게 새로운 메모리 공간으로 복제하는 것을 의미합니다. 원본과 복사본은 완전히 독립적인 상태가 되며, 서로에게 어떠한 영향도 미치지 않습니다.

3-1. JSON 객체 메서드 활용

가장 간단하고 널리 알려진 방법은 객체를 문자열로 변환했다가 다시 객체로 파싱하는 방법입니다.


const deepCopy = JSON.parse(JSON.stringify(original));

이 방법은 간편하지만 두 가지 치명적인 단점이 있습니다. 첫째, 문자열 파싱 과정에서 속도가 느립니다. 둘째, Date 객체나 Function, undefined 같은 데이터는 문자열로 변환 과정에서 유실되거나 변형됩니다.

3-2. 재귀 함수 (Recursive Function)

객체의 깊이만큼 재귀적으로 파고들어 복사하는 함수를 직접 구현하거나, Lodash 라이브러리의 _.cloneDeep() 메서드를 사용하는 방법입니다. 이는 완벽한 복사를 보장하지만 외부 라이브러리 의존도가 생기거나 코드가 복잡해질 수 있습니다.

4. 최신 표준 기술: structuredClone()

최근 모던 브라우저와 Node.js 환경에서는 structuredClone()이라는 전역 함수를 지원합니다. 이는 JSON 방식의 한계를 극복하고, 재귀 함수보다 효율적으로 깊은 복사를 수행하는 가장 권장되는 방법입니다.

4-1. structuredClone 사용법 및 장점

  • 완전한 독립성: 중첩된 객체까지 완벽하게 새로운 메모리에 할당합니다.
  • 데이터 보존: JSON 방식과 달리 Date, RegExp, Map, Set 등의 복잡한 타입도 정확하게 복사합니다.
  • 순환 참조 해결: 객체가 자기 자신을 참조하는 순환 구조에서도 에러 없이 작동합니다.

// 최신 표준 깊은 복사 방법
const original = {
    date: new Date(),
    data: new Set([1, 2, 3])
};

const deepCopy = structuredClone(original);

console.log(deepCopy.date === original.date); // false (다른 메모리 주소)
console.log(deepCopy.data); // Set(3) {1, 2, 3} (데이터 타입 유지)
[핵심 요약]
1. 얕은 복사(Shallow Copy)는 주소값을 공유하므로 중첩 객체 수정 시 원본이 훼손될 위험이 있습니다.
2. 깊은 복사(Deep Copy)는 실제 데이터를 새로운 메모리에 복제하여 원본과 완벽히 분리됩니다.
3. 최신 개발 환경에서는 성능과 데이터 안정성이 보장된 structuredClone() 사용을 권장합니다.
반응형