깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)는 객체 복사 시 원본 데이터와 복사본이 어떻게 연결되고 동작하는지를 결정짓는 중요한 개념이다.
C++는 메모리를 개발자가 명시적으로 관리해야 하며, Python은 내부적으로 가비지 컬렉션을 사용하여 메모리를 관리하기 때문에 두 언어의 복사 동작에는 차이가 있다.
이번 글에서는 C++에서의 얕은 복사와 깊은 복사를 먼저 다루고, 이후 Python에서의 얕은 복사와 깊은 복사를 비교하며 실무에서 어떻게 활용할 수 있을지 살펴보겠다.
얕은 복사는 객체의 최상위 레벨만 복사하며, 객체가 참조하는 내부 데이터(메모리 주소)는 복사하지 않는다.
즉, 복사본은 원본과 같은 데이터를 참조하기 때문에, 복사본에서 데이터를 변경하면 원본에도 영향을 미친다.
C++에서 얕은 복사는 기본 동작이다. 복사 생성자나 대입 연산자를 사용할 때 별도로 구현하지 않는 한, 객체는 얕은 복사를 수행한다.
#include <iostream>
using namespace std;
class Shallow {
public:
int* data;
Shallow(int value) {
data = new int(value); // 동적 메모리 할당
}
~Shallow() {
delete data; // 동적 메모리 해제
}
};
int main() {
Shallow obj1(10);
Shallow obj2 = obj1; // 얕은 복사 발생
cout << "obj1 data: " << *obj1.data << endl; // 출력: 10
cout << "obj2 data: " << *obj2.data << endl; // 출력: 10
*obj2.data = 20; // obj2를 통해 데이터 변경
cout << "obj1 data: " << *obj1.data << endl; // 출력: 20 (obj1도 영향을 받음)
return 0;
}
obj1과 obj2는 같은 메모리 공간을 공유한다.delete를 두 번 호출하여 런타임 에러가 발생할 수 있다.C++에서 얕은 복사의 문제를 해결하려면 깊은 복사를 명시적으로 구현해야 한다.

깊은 복사는 객체와 객체가 참조하는 모든 데이터를 새로운 메모리 공간에 복사하는 방식이다.
깊은 복사를 사용하면 원본과 복사본이 완전히 독립적으로 동작하며, 서로 영향을 미치지 않는다.
C++에서는 깊은 복사를 구현하려면 복사 생성자와 대입 연산자를 오버로드해야 한다
#include <iostream>
using namespace std;
class Deep {
public:
int* data;
Deep(int value) {
data = new int(value); // 동적 메모리 할당
}
Deep(const Deep& other) { // 깊은 복사 생성자
data = new int(*other.data); // 데이터 복사
}
~Deep() {
delete data; // 동적 메모리 해제
}
};
int main() {
Deep obj1(10);
Deep obj2 = obj1; // 깊은 복사 발생
cout << "obj1 data: " << *obj1.data << endl; // 출력: 10
cout << "obj2 data: " << *obj2.data << endl; // 출력: 10
*obj2.data = 20; // obj2를 통해 데이터 변경
cout << "obj1 data: " << *obj1.data << endl; // 출력: 10 (독립적임)
return 0;
}

obj1과 obj2는 각각 독립적인 메모리 공간을 가진다.obj2)이 데이터를 변경해도, 원본(obj1)에는 영향을 미치지 않는다.Python에서는 copy.copy()를 사용하면 얕은 복사가 수행된다.
리스트, 딕셔너리와 같은 가변 객체의 내부 참조 데이터는 공유되기 때문에, 원본과 복사본이 서로 영향을 미친다.
import copy
original = [1, [2, 3], 4]
shallow_copy = copy.copy(original) # 얕은 복사
print("원본:", original) # 출력: [1, [2, 3], 4]
shallow_copy[1][0] = 99 # 내부 리스트 수정
print("얕은 복사 후:", shallow_copy) # 출력: [1, [99, 3], 4]
print("원본:", original) # 출력: [1, [99, 3], 4]
[2, 3]는 원본과 복사본이 같은 메모리 주소를 공유한다.
[2, 3]는 원본과 복사본이 같은 메모리 주소를 공유한다.Python에서는 copy.deepcopy()를 사용하여 깊은 복사를 수행할 수 있다.
이 함수는 객체와 모든 참조 데이터를 재귀적으로 복사하여, 원본과 복사본이 완전히 독립적으로 동작하게 만든다.
import copy
original = [1, [2, 3], 4]
deep_copy = copy.deepcopy(original) # 깊은 복사
print("원본:", original) # 출력: [1, [2, 3], 4]
deep_copy[1][0] = 99 # 내부 리스트 수정
print("깊은 복사 후:", deep_copy) # 출력: [1, [99, 3], 4]
print("원본:", original) # 출력: [1, [2, 3], 4]

| 특징 | C++ 얕은 복사 | Python 얕은 복사 | C++ 깊은 복사 | Python 깊은 복사 |
|---|---|---|---|---|
| 구현 | 기본 복사 생성자와 대입 연산자 사용 | copy.copy() 사용 | 복사 생성자와 대입 연산자 오버로드 필요 | copy.deepcopy() 사용 |
| 내부 데이터 공유 | 내부 포인터 공유 | 내부 참조 객체 공유 | 내부 데이터까지 완전 복사 | 내부 데이터까지 완전 복사 |
| 메모리 관리 | 수동으로 delete 필요 | 가비지 컬렉터가 자동 관리 | 수동으로 메모리 해제 필요 | 가비지 컬렉터가 자동 관리 |
C++는 메모리 관리가 필요하며, Python은 자동으로 처리된다는 점에서 복사의 구현과 동작에 차이가 있다.
Python에서는 실무에서 copy 모듈을 적극적으로 활용하여 객체 복사를 처리할 수 있다.