메뉴 닫기

파이썬 얕은 복사 vs 깊은 복사 완벽 정리

파이썬 얕은 복사 vs 깊은 복사 완벽 정리

🐍 파이썬 리스트 복사, 제대로 알고 써야 오류를 막을 수 있어요!

파이썬을 처음 배우다 보면 리스트나 딕셔너리처럼 ‘가변 객체’를 복사할 때 예상치 못한 결과가 나와 당황스러운 순간이 많습니다.
특히, `리스트를 다른 변수에 그대로 할당했는데 원본이 바뀌는` 상황은 초보자들이 가장 많이 겪는 혼란 중 하나죠.
저도 파이썬 입문 초기에 이 문제로 몇 시간 동안 헤매며 코드를 다시 짠 경험이 있습니다.
오늘은 이처럼 많이들 헷갈리는 얕은 복사(shallow copy)와 깊은 복사(deep copy)의 차이를 아주 쉽게 설명해드릴게요.
기초 문법을 넘어서 실무에도 꼭 필요한 개념이니 끝까지 꼼꼼히 읽어보세요.

이 글에서는 파이썬에서 리스트나 딕셔너리를 복사하는 다양한 방식과 각각이 어떤 결과를 가져오는지를 예제와 함께 알려드립니다.
단순 할당이 왜 얕은 복사인지, 그리고 copy 모듈을 사용하면 어떤 일이 일어나는지를 코드로 확인하면서 직접 체감하실 수 있어요.
이론과 실습을 모두 이해하고 나면, 복잡한 데이터 구조에서도 오류 없이 복사할 수 있는 실력을 갖추게 되실 겁니다.



🔗 얕은 복사란?

파이썬에서 얕은 복사(shallow copy)는 리스트, 딕셔너리 같은 가변 객체를 새로운 변수에 복사할 때 흔히 발생합니다.
하지만 복사된 변수는 ‘데이터를 새로 만드는 것’이 아니라 ‘같은 객체를 바라보는 것’이라는 점이 핵심이에요.

쉽게 말해, 원본 리스트와 복사된 리스트가 서로 연결된 상태이기 때문에 한 쪽을 수정하면 다른 쪽도 영향을 받게 됩니다.
예를 들어 리스트를 단순히 a = b와 같이 복사하면, 둘 다 같은 메모리 주소를 공유하게 됩니다.

CODE BLOCK
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() 함수를 사용하면 중첩된 내부 객체까지 재귀적으로 복사할 수 있어요.
이때 복사된 객체는 메모리 주소까지 완전히 달라지기 때문에, 어떤 부분을 수정해도 서로 영향을 주지 않습니다.

CODE BLOCK
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()라는 두 가지 함수가 있으며, 각각 얕은 복사와 깊은 복사를 수행합니다.

두 함수의 사용법은 매우 간단하지만, 구조에 따라 결과가 완전히 달라질 수 있으므로 반드시 올바른 상황에서 사용해야 해요.

CODE BLOCK
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() 깊은 복사
내부까지 모두 새로운 객체 생성

실제 업무에서도 복사 방식을 헷갈려서 데이터를 훼손하거나 프로그램이 오작동하는 경우가 많습니다.
그러니 데이터 구조를 먼저 살펴보고, 적절한 복사 방법을 선택하는 습관을 꼭 들이세요.

🔌 얕은 복사와 깊은 복사 비교 예제

이제 얕은 복사와 깊은 복사의 차이를 한눈에 비교할 수 있는 코드 예제로 정리해볼게요.
두 방식이 실제 코드에서 어떻게 다르게 동작하는지 살펴보면, 어떤 상황에 어떤 방식을 써야 하는지 감이 딱 옵니다.

CODE BLOCK
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()는 안전한가요?
1차원 리스트나 단순한 구조에서는 안전하지만, 내부에 리스트나 딕셔너리가 포함된 경우엔 예기치 않게 원본이 변경될 수 있습니다.
deepcopy()는 성능에 영향을 주지 않나요?
구조가 복잡할수록 메모리와 시간이 더 소모되긴 하지만, 안정성과 유지보수를 위해 필요한 경우에는 사용을 권장합니다.
딕셔너리도 얕은 복사 문제가 발생하나요?
네, 딕셔너리 안에 리스트나 다른 딕셔너리가 들어있을 경우, 얕은 복사 시 내부 요소가 공유되어 의도치 않은 동작이 발생할 수 있습니다.
리스트를 복사할 때 슬라이싱을 쓰면 깊은 복사인가요?
리스트 슬라이싱([:])은 얕은 복사입니다. 내부에 중첩된 객체가 있다면 여전히 참조가 공유됩니다.
중첩 구조가 있는지 어떻게 알 수 있나요?
리스트 안에 또 다른 리스트나 딕셔너리가 있다면 중첩 구조입니다. print()나 type() 함수로 내부 요소를 직접 확인해보세요.
복사 후 객체 주소를 확인하려면 어떻게 하나요?
파이썬의 id() 함수를 사용하면 각 객체의 메모리 주소를 확인할 수 있습니다. 복사된 결과가 진짜 새로운 객체인지 확인할 때 유용합니다.

📌 얕은 복사와 깊은 복사, 이젠 확실히 구분하세요

파이썬에서 리스트나 딕셔너리처럼 가변 객체를 복사할 때 얕은 복사와 깊은 복사의 개념을 명확히 이해하지 못하면, 예상치 못한 버그에 쉽게 노출될 수 있습니다.
단순 할당이나 copy.copy()는 내부 객체를 공유하는 얕은 복사이기 때문에, 중첩 구조에서는 원본 데이터를 훼손할 수 있어요.
이 글에서는 copy 모듈을 활용한 복사 방식, 실제 코드 예제, 그리고 실전에서 적용할 팁까지 모두 정리해드렸습니다.
특히 copy.deepcopy()는 중첩된 구조에서도 안전하게 데이터를 분리할 수 있는 중요한 도구입니다.
프로그래밍 실수는 대부분 작은 이해 부족에서 시작되니, 오늘 배운 개념을 코드에 직접 적용해보시고 확실히 체득해보세요.


🏷️ 관련 태그 : 파이썬복사, 얕은복사, 깊은복사, copy모듈, deepcopy, 파이썬리스트, 파이썬딕셔너리, 메모리참조, 중첩구조, 파이썬기초