파이썬 얕은 복사 vs 깊은 복사 완벽 정리
🐍 파이썬 리스트 복사, 제대로 알고 써야 오류를 막을 수 있어요!
파이썬을 처음 배우다 보면 리스트나 딕셔너리처럼 ‘가변 객체’를 복사할 때 예상치 못한 결과가 나와 당황스러운 순간이 많습니다.
특히, `리스트를 다른 변수에 그대로 할당했는데 원본이 바뀌는` 상황은 초보자들이 가장 많이 겪는 혼란 중 하나죠.
저도 파이썬 입문 초기에 이 문제로 몇 시간 동안 헤매며 코드를 다시 짠 경험이 있습니다.
오늘은 이처럼 많이들 헷갈리는 얕은 복사(shallow copy)와 깊은 복사(deep copy)의 차이를 아주 쉽게 설명해드릴게요.
기초 문법을 넘어서 실무에도 꼭 필요한 개념이니 끝까지 꼼꼼히 읽어보세요.
이 글에서는 파이썬에서 리스트나 딕셔너리를 복사하는 다양한 방식과 각각이 어떤 결과를 가져오는지를 예제와 함께 알려드립니다.
단순 할당이 왜 얕은 복사인지, 그리고 copy 모듈을 사용하면 어떤 일이 일어나는지를 코드로 확인하면서 직접 체감하실 수 있어요.
이론과 실습을 모두 이해하고 나면, 복잡한 데이터 구조에서도 오류 없이 복사할 수 있는 실력을 갖추게 되실 겁니다.
🔗 얕은 복사란?
파이썬에서 얕은 복사(shallow copy)는 리스트, 딕셔너리 같은 가변 객체를 새로운 변수에 복사할 때 흔히 발생합니다.
하지만 복사된 변수는 ‘데이터를 새로 만드는 것’이 아니라 ‘같은 객체를 바라보는 것’이라는 점이 핵심이에요.
쉽게 말해, 원본 리스트와 복사된 리스트가 서로 연결된 상태이기 때문에 한 쪽을 수정하면 다른 쪽도 영향을 받게 됩니다.
예를 들어 리스트를 단순히 a = b와 같이 복사하면, 둘 다 같은 메모리 주소를 공유하게 됩니다.
original = [1, 2, 3]
shallow = original
shallow[0] = 100
print("original:", original)
print("shallow:", shallow)
위 코드를 실행하면 original 리스트 역시 [100, 2, 3]으로 바뀝니다.
왜냐하면 `shallow`가 `original`을 참조하고 있기 때문이에요.
이것이 바로 얕은 복사의 대표적인 특징입니다.
💎 핵심 포인트:
얕은 복사는 객체를 새로 만드는 것이 아니라 참조(레퍼런스)를 공유합니다.
리스트나 딕셔너리를 복사할 때 단순 할당(=)만 하면 두 변수는 같은 데이터를 바라보게 됩니다.
⚠️ 주의: 중첩 리스트 구조에서 얕은 복사를 사용할 경우, 내부 리스트까지 공유되므로 예기치 않은 변경이 발생할 수 있습니다.
얕은 복사는 리스트가 간단할 때는 문제 없이 작동하지만, 내부에 또 다른 리스트나 딕셔너리가 들어있는 중첩 구조에서는 심각한 오류로 이어질 수 있습니다.
이럴 땐 copy.deepcopy()를 사용한 깊은 복사가 필요하죠.
다음 STEP에서 이 부분을 자세히 살펴보겠습니다.
🛠️ 깊은 복사의 원리
얕은 복사와 달리 깊은 복사(deep copy)는 객체 전체를 완전히 복제합니다.
특히 리스트 안에 또 다른 리스트가 있는 중첩 구조(nested structure)에서는 깊은 복사가 꼭 필요합니다.
파이썬에서는 copy 모듈의 deepcopy() 함수를 사용하면 중첩된 내부 객체까지 재귀적으로 복사할 수 있어요.
이때 복사된 객체는 메모리 주소까지 완전히 달라지기 때문에, 어떤 부분을 수정해도 서로 영향을 주지 않습니다.
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 999
print("original:", original)
print("deep:", deep)
위 예제에서 deep[0][0]을 변경해도 original에는 아무 영향이 없습니다.
그 이유는 original과 deep이 완전히 다른 메모리 구조를 가지고 있기 때문이죠.
💎 핵심 포인트:
깊은 복사는 리스트 안에 또 다른 리스트가 포함된 구조에서 안전하게 데이터를 복사할 수 있는 방법입니다.
이 때 copy.deepcopy()를 사용해야 내부 요소까지 전부 복제됩니다.
- 📦copy.deepcopy()는 외부뿐만 아니라 내부 요소까지 모두 복제
- 🧠얕은 복사로는 중첩 구조를 완전히 분리할 수 없음
- ⚠️깊은 복사를 잊으면 예기치 못한 버그 발생 가능
특히 데이터 분석이나 웹 개발처럼 많은 데이터를 다루는 프로젝트에서는 깊은 복사의 필요성이 더 커집니다.
이제 copy 모듈을 어떻게 활용하면 되는지 실전 예제로 이어가 볼게요.
⚙️ copy 모듈 활용법
파이썬에서 복사를 명확히 제어하고 싶을 때는 copy 모듈을 사용합니다.
이 모듈에는 copy()와 deepcopy()라는 두 가지 함수가 있으며, 각각 얕은 복사와 깊은 복사를 수행합니다.
두 함수의 사용법은 매우 간단하지만, 구조에 따라 결과가 완전히 달라질 수 있으므로 반드시 올바른 상황에서 사용해야 해요.
import copy
# 얕은 복사
a = [[1, 2], [3, 4]]
b = copy.copy(a)
b[0][0] = 999
print("얕은 복사 결과:", a) # 내부 리스트까지 영향을 받음
# 깊은 복사
c = copy.deepcopy(a)
c[0][0] = 123
print("깊은 복사 결과:", a) # 원본은 영향 없음
위 코드를 보면 copy.copy()는 1차원 객체만 복사하고, 내부 리스트는 여전히 원본과 연결되어 있기 때문에 값이 바뀝니다.
반면 copy.deepcopy()는 중첩 구조까지 새롭게 생성해 완전히 분리된 복사본을 만들어줍니다.
💡 TIP: 리스트가 단순한 1차원 구조라면 copy.copy()로 충분하지만, 내부에 리스트나 딕셔너리가 들어간 경우엔 반드시 copy.deepcopy()를 사용해야 오류를 방지할 수 있어요.
| 복사 방식 | 특징 |
|---|---|
| copy.copy() | 얕은 복사 내부 객체는 원본과 공유 |
| copy.deepcopy() | 깊은 복사 내부까지 모두 새로운 객체 생성 |
실제 업무에서도 복사 방식을 헷갈려서 데이터를 훼손하거나 프로그램이 오작동하는 경우가 많습니다.
그러니 데이터 구조를 먼저 살펴보고, 적절한 복사 방법을 선택하는 습관을 꼭 들이세요.
🔌 얕은 복사와 깊은 복사 비교 예제
이제 얕은 복사와 깊은 복사의 차이를 한눈에 비교할 수 있는 코드 예제로 정리해볼게요.
두 방식이 실제 코드에서 어떻게 다르게 동작하는지 살펴보면, 어떤 상황에 어떤 방식을 써야 하는지 감이 딱 옵니다.
import copy
original = [[1, 2], [3, 4]]
# 얕은 복사
shallow = copy.copy(original)
shallow[0][0] = 999
# 깊은 복사
deep = copy.deepcopy(original)
deep[1][1] = 888
print("original:", original)
print("shallow:", shallow)
print("deep:", deep)
| 변수 | 리스트 결과 | 특징 |
|---|---|---|
| original | [[999, 2], [3, 4]] | shallow 수정의 영향 받음 |
| shallow | [[999, 2], [3, 4]] | 원본과 연결된 얕은 복사 |
| deep | [[1, 2], [3, 888]] | 독립된 깊은 복사 |
💎 핵심 포인트:
얕은 복사는 내부 객체가 원본과 연결되어 있기 때문에 원치 않는 결과를 낳을 수 있습니다.
중첩된 데이터 구조에서는 항상 copy.deepcopy()를 사용하세요.
코드를 직접 실행해 보면 복사 방식에 따라 데이터가 어떻게 달라지는지를 명확히 확인할 수 있어요.
단순히 외워서가 아니라, 직접 실습하고 비교해보는 경험이 가장 큰 학습이 됩니다.
💡 복사 오류를 피하는 팁
리스트나 딕셔너리를 복사할 때 단순 할당(=)을 쓰면 얕은 복사가 되기 때문에, 구조가 복잡할수록 예기치 못한 오류가 발생할 가능성이 높습니다.
특히 데이터 구조가 중첩되어 있다면, 어느 한 부분만 수정해도 전체 데이터에 영향을 줄 수 있어요.
이러한 문제를 방지하기 위해서는 다음과 같은 실전 팁들을 기억해두면 좋습니다.
개발 속도를 올리면서도, 디버깅 시간을 줄일 수 있는 중요한 요령들입니다.
- 🚫단순 할당(
=)은 복사가 아님. 참조만 전달됩니다. - 🧱copy.copy()는 얕은 복사로, 1차원 구조에만 적합합니다.
- 🏗️딕셔너리 안에 리스트, 리스트 안에 딕셔너리처럼 중첩된 자료구조에는 반드시 deepcopy 사용
- 👀
id()함수를 이용해 객체의 메모리 주소를 비교해보세요. - 🧪복사 후 반드시 테스트 코드로 변경 영향 여부를 확인하세요.
💡 TIP: 복잡한 데이터 구조를 다룰 때는 무조건 deepcopy부터 사용하는 습관을 들이면, 나중에 발생할 수 있는 디버깅 지옥을 피할 수 있습니다.
이제 얕은 복사와 깊은 복사의 개념은 물론, 실제 코드에서 어떻게 구분하고 활용해야 하는지까지 감이 오셨을 거예요.
다음은 자주 묻는 질문(FAQ)들을 정리해서, 헷갈리는 부분을 한 번 더 정리해 드릴게요.
❓ 자주 묻는 질문 (FAQ)
얕은 복사와 깊은 복사의 가장 큰 차이는 무엇인가요?
단순 할당과 얕은 복사는 같은 건가요?
copy.copy()는 안전한가요?
deepcopy()는 성능에 영향을 주지 않나요?
딕셔너리도 얕은 복사 문제가 발생하나요?
리스트를 복사할 때 슬라이싱을 쓰면 깊은 복사인가요?
중첩 구조가 있는지 어떻게 알 수 있나요?
복사 후 객체 주소를 확인하려면 어떻게 하나요?
id() 함수를 사용하면 각 객체의 메모리 주소를 확인할 수 있습니다. 복사된 결과가 진짜 새로운 객체인지 확인할 때 유용합니다.
📌 얕은 복사와 깊은 복사, 이젠 확실히 구분하세요
파이썬에서 리스트나 딕셔너리처럼 가변 객체를 복사할 때 얕은 복사와 깊은 복사의 개념을 명확히 이해하지 못하면, 예상치 못한 버그에 쉽게 노출될 수 있습니다.
단순 할당이나 copy.copy()는 내부 객체를 공유하는 얕은 복사이기 때문에, 중첩 구조에서는 원본 데이터를 훼손할 수 있어요.
이 글에서는 copy 모듈을 활용한 복사 방식, 실제 코드 예제, 그리고 실전에서 적용할 팁까지 모두 정리해드렸습니다.
특히 copy.deepcopy()는 중첩된 구조에서도 안전하게 데이터를 분리할 수 있는 중요한 도구입니다.
프로그래밍 실수는 대부분 작은 이해 부족에서 시작되니, 오늘 배운 개념을 코드에 직접 적용해보시고 확실히 체득해보세요.
🏷️ 관련 태그 : 파이썬복사, 얕은복사, 깊은복사, copy모듈, deepcopy, 파이썬리스트, 파이썬딕셔너리, 메모리참조, 중첩구조, 파이썬기초