본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 객체 복사의 2가지 메커니즘 : copy와 deepcopy의 내부 순회 방식 차이 해결

by Papa Martino V 2026. 3. 3.
728x90
copy와 deepcopy
copy와 deepcopy

 

파이썬에서 가변 객체(Mutable Objects)를 다룰 때 가장 흔하게 발생하는 실수 중 하나는 '참조 복사'와 '실제 복사'를 혼동하는 것입니다. 단순히 리스트나 딕셔너리를 복사했다고 생각했지만, 원본 데이터를 수정했을 때 복사본까지 함께 변경되는 당혹스러운 경험을 해보셨을 것입니다. 본 가이드에서는 파이썬의 copy 모듈이 내부적으로 객체를 어떻게 트리 구조로 탐색하고 복제하는지, 그 기술적 깊이를 분석하여 데이터 무결성을 지키는 방법을 제시합니다.


1. 객체 복사의 근본적 이유와 메모리 참조

파이썬의 모든 것은 객체입니다. 변수는 객체를 담는 그릇이 아니라 객체의 메모리 주소를 가리키는 '포인터'에 가깝습니다. 따라서 b = a와 같은 할당문은 객체를 복사하는 것이 아니라 주소값만 전달합니다. 이를 해결하기 위해 파이썬은 얕은 복사(Shallow Copy)깊은 복사(Deep Copy)라는 두 가지 전략을 제공합니다.


2. 내부 순회 방식의 결정적 차이 비교

copy.copy()copy.deepcopy()는 객체의 그래프를 탐색하는 깊이에서 근본적인 차이를 보입니다.

비교 항목 copy.copy() (얕은 복사) copy.deepcopy() (깊은 복사)
순회 깊이 최상위 객체(Level 1)만 순회 중첩된 모든 하위 객체를 재귀적으로 순회
내부 구현 객체의 __copy__ 메서드 호출 객체의 __deepcopy__ 메서드 재귀 호출
메모리 주소 최상위는 다르나 내부 요소는 동일 모든 가변 요소의 메모리 주소가 새로 할당됨
처리 속도 매우 빠름 재귀 탐색 및 객체 생성으로 인해 상대적 느림
순환 참조 대응 해당 사항 없음 memo 딕셔너리를 통한 무한 루프 방지

3. 얕은 복사(Shallow Copy)의 작동 원리

copy.copy()는 새로운 복합 객체를 생성한 후, 원본 객체 안에 있는 참조(Reference)를 그대로 새 객체에 삽입합니다. 즉, 겉껍데기만 새로 만들고 알맹이는 원본과 공유하는 방식입니다. 이는 리스트 내부에 또 다른 리스트가 있는 '중첩 구조'에서 예상치 못한 부작용을 야기할 수 있습니다.


4. 깊은 복사(Deep Copy)와 재귀적 탐색 해결

copy.deepcopy()는 훨씬 복잡한 과정을 거칩니다. 내부적으로 다음과 같은 로직을 수행합니다.

  1. 최상위 객체를 복사합니다.
  2. 객체 내부의 모든 요소를 하나씩 방문하며, 해당 요소가 가변 객체일 경우 다시 deepcopy를 호출합니다(Recursive Traversal).
  3. Memoization: 이미 복사한 객체의 ID를 memo라는 딕셔너리에 저장합니다. 만약 순환 참조(객체 A가 B를 참조하고 B가 다시 A를 참조)가 발견되면, 새로 복사하는 대신 저장된 복사본을 반환하여 무한 루프를 해결합니다.

5. Sample Example: 실무 코드로 보는 복사 방식의 차이

아래 예제를 통해 두 방식이 실제 데이터에 어떤 영향을 주는지 확인해 보겠습니다.


import copy

# 원본 데이터: 리스트 안에 리스트가 포함된 중첩 구조
original = [[1, 2, 3], [4, 5, 6]]

# 1. 얕은 복사 수행
shallow_copied = copy.copy(original)

# 2. 깊은 복사 수행
deep_copied = copy.deepcopy(original)

# 원본의 내부 리스트 요소 수정
original[0][0] = 'CHANGED'

print(f"Original: {original}")
print(f"Shallow:  {shallow_copied}") # 원본 수정이 반영됨 (참조 공유)
print(f"Deep:     {deep_copied}")    # 원본 수정과 무관 (완전 분리)

# 결과 확인:
# Shallow: [['CHANGED', 2, 3], [4, 5, 6]]
# Deep: [[1, 2, 3], [4, 5, 6]]

6. 성능 최적화를 위한 전문가의 조언

단순히 '안전하니까 deepcopy를 쓰자'는 생각은 위험할 수 있습니다. deepcopy는 모든 객체를 순회하고 새로 생성하므로 CPU와 메모리 사용량이 급격히 증가합니다. 특히 수만 개의 노드를 가진 대규모 그래프 객체를 복사할 때는 성능 병목의 원인이 됩니다.

  • 불변 객체(Immutable): 튜플이나 문자열은 deepcopy를 하더라도 파이썬 내부 최적화에 의해 참조만 전달됩니다.
  • 부분 복사: 전체를 복사하는 대신 필요한 슬라이스나 하위 객체만 명시적으로 복사하는 것이 효율적입니다.
  • 사용자 정의 복사: 클래스 설계 시 __deepcopy__ 메서드를 오버라이딩하여 특정 필드는 복사에서 제외하는 등 동작을 제어할 수 있습니다.

7. 결론: 올바른 복사 전략 선택하기

데이터의 독립성이 보장되어야 하는 복잡한 객체 구조에서는 deepcopy가 유일한 해결책입니다. 하지만 성능이 중요한 루프 내부나 대용량 처리 시에는 copy의 얕은 복사 메커니즘을 정확히 이해하고 사용하는 지혜가 필요합니다. 파이썬의 메모리 관리 방식을 이해하는 것은 곧 효율적인 아키텍처 설계의 밑거름이 됩니다.


글 내용의 출처 및 참고 자료

  • Python Official Documentation: copy — Shallow and deep copy operations
  • Fluent Python (Luciano Ramalho): "Object References, Mutability, and Recycling"
  • Python Software Foundation: CPython Implementation detail of copy module
  • Real Python: Shallow vs Deep Copying of Python Objects
728x90