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

[PYTHON] 객체 복사 시 데이터 유실을 막는 Shallow Copy vs Deepcopy 차이점과 7가지 해결 방법

by Papa Martino V 2026. 3. 30.
728x90

Shallow Copy vs Deepcopy 차이점
Shallow Copy vs Deepcopy 차이점

 

 

파이썬 프로그래밍을 하다 보면 리스트나 딕셔너리 같은 자료구조를 복사해서 사용해야 할 때가 많습니다. 단순히 new_list = old_list라고 작성하는 것이 복사라고 생각하기 쉽지만, 이는 객체의 주소값만 전달하는 '참조'에 불과합니다. 특히 다차원 배열이나 중첩된 객체를 다룰 때 Shallow Copy(얕은 복사)Deepcopy(깊은 복사)의 메커니즘을 정확히 이해하지 못하면, 원본 데이터가 의도치 않게 변동되어 시스템 전체에 치명적인 버그를 초래할 수 있습니다.


1. 왜 복사 방식의 차이를 알아야 하는가?

파이썬의 모든 것은 객체(Object)입니다. 객체는 가변 객체(Mutable: list, dict, set)와 불변 객체(Immutable: int, str, tuple)로 나뉩니다. 가변 객체가 중첩되어 있을 때, 얕은 복사는 '가장 바깥쪽 껍데기'만 새로 만들고 내부의 요소들은 원본의 주소를 그대로 가리킵니다. 반면 깊은 복사는 내부의 모든 중첩 객체까지 새로운 메모리 공간에 재할당합니다. 이 미세한 차이가 데이터 무결성을 결정짓습니다.

비교 항목 단순 대입 (Assignment) 얕은 복사 (Shallow Copy) 깊은 복사 (Deep Copy)
메모리 주소 원본과 동일함 최상위 객체만 새로 생성 모든 계층의 객체를 새로 생성
중첩 리스트 수정 시 원본과 복사본 모두 변경 원본과 복사본 모두 변경 복사본만 변경 (원본 보존)
속도 및 성능 매우 빠름 빠름 느림 (재귀적 복사 수행)
주요 모듈/메서드 = 연산자 copy.copy(), 슬라이싱 copy.deepcopy()

2. 실무에서 즉시 활용 가능한 복사 해결 예제 (7가지)

현업 개발자가 실무 로직 설계 시 발생할 수 있는 데이터 오염 상황을 가정하여, 각 상황에 맞는 최적의 복사 해결 방법을 제시합니다.

Example 1: 슬라이싱을 이용한 1차원 리스트의 빠른 얕은 복사

중첩되지 않은 단순 리스트라면 슬라이싱이 가장 효율적입니다.


original = [1, 2, 3, 4, 5]
# 전체 범위를 지정하여 새로운 객체 생성
shallowed = original[:]

shallowed[0] = 99
print(f"Original: {original}")  # [1, 2, 3, 4, 5] 보존
print(f"Shallowed: {shallowed}")

Example 2: 클래스 내부 가변 인자(Default Argument) 문제 해결

함수 정의 시 가변 객체를 기본값으로 주면 모든 인스턴스가 공유하게 됩니다. 이를 복사를 통해 해결합니다.


import copy

class TaskManager:
    def __init__(self, tasks=None):
        # 얕은 복사를 통해 외부 리스트와의 연결 고리를 끊음
        self.tasks = list(tasks) if tasks else []

user_tasks = ["Email", "Coding"]
manager = TaskManager(user_tasks)
manager.tasks.append("Meeting")

print(f"User Original Tasks: {user_tasks}") # "Meeting"이 추가되지 않음

Example 3: 중첩된 딕셔너리 설정값의 안전한 업데이트 (Deepcopy)

환경 설정(Configuration) 파일처럼 계층 구조가 복잡한 데이터를 다룰 때 필수적입니다.


import copy

default_config = {"db": {"host": "localhost", "port": 3306}, "debug": True}
# 깊은 복사로 별도의 설정 인스턴스 생성
user_config = copy.deepcopy(default_config)

user_config["db"]["host"] = "192.168.0.1"
print(f"Default Host: {default_config['db']['host']}") # localhost 유지

Example 4: 2차원 행렬(Matrix) 데이터 처리 시의 오류 방지

데이터 분석이나 게임 로직에서 2차원 배열을 복사할 때 얕은 복사를 쓰면 발생하는 참사를 방지합니다.


import copy

matrix = [[1, 2], [3, 4]]
# matrix.copy()는 얕은 복사이므로 내부 리스트 주소는 공유됨
safe_matrix = copy.deepcopy(matrix)

safe_matrix[0][0] = 0
print(f"Original Matrix[0][0]: {matrix[0][0]}") # 1 출력 (안전)

Example 5: 대규모 데이터 객체의 부분적 Deepcopy 최적화

전체 객체를 deepcopy하는 것이 너무 느리다면, 필요한 부분만 선택적으로 새로 할당합니다.


# 수동 깊은 복사 (Manual Deep Copy)
old_data = {'id': 1, 'logs': ['start', 'process']}
new_data = old_data.copy() # 껍데기 복사
new_data['logs'] = list(old_data['logs']) # 내부 가변 객체만 새로 생성

new_data['logs'].append('end')
print(f"Old Logs: {old_data['logs']}") # 'end'가 포함되지 않음

Example 6: 객체 내 순환 참조가 있는 경우의 Deepcopy 처리

파이썬의 copy.deepcopy는 내부적으로 메모이제이션을 사용하여 순환 참조가 있어도 무한 루프에 빠지지 않습니다.


import copy

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

node1 = Node(10)
node1.next = node1 # 순환 참조 발생

# deepcopy는 똑똑하게 순환 참조를 감지하고 복사본을 생성함
node2 = copy.deepcopy(node1)
print(f"Node2 Value: {node2.value}, Is circular: {node2.next is node2}")

Example 7: JSON 직렬화를 이용한 트릭 (Deepcopy 대안)

순수 데이터(Dict, List, Str, Int)로만 이루어진 경우 JSON 변환을 통해 깊은 복사 효과를 낼 수 있습니다.


import json

data = {"items": [{"name": "A"}, {"name": "B"}]}
# 직렬화 후 역직렬화를 통해 완전히 새로운 메모리에 할당
deep_copied_data = json.loads(json.dumps(data))

deep_copied_data["items"][0]["name"] = "C"
print(f"Original: {data['items'][0]['name']}") # "A" 유지

3. 어떤 상황에서 무엇을 선택해야 할까?

성능과 안전성 사이의 트레이드오프를 고려해야 합니다.

상황 권장 방법 이유
1차원 리스트/딕셔너리 복사 .copy() 또는 슬라이싱 가장 빠르며 내부 공유 객체가 없음
중첩된 JSON/API 응답 데이터 copy.deepcopy() 내부 데이터 오염 방지가 최우선
수백만 개의 데이터 처리 수동 부분 복사 deepcopy의 재귀 호출 오버헤드 방지
불변 객체(Tuple 등)만 포함 단순 대입 또는 copy() 불변 객체는 수정이 불가능하므로 안전함

4. 결론: 데이터 무결성을 위한 개발자의 자세

파이썬에서 "복사했다"는 말은 상황에 따라 의미가 달라집니다. 특히 가변 객체가 중첩된 경우, 무심코 사용한 얕은 복사는 디버깅하기 매우 어려운 논리적 오류를 발생시킵니다. 데이터의 구조가 깊다면 copy.deepcopy()를 기본으로 고려하되, 성능 최적화가 필요한 임계점에서는 부분 복사나 직렬화 기법을 도입하는 전략이 필요합니다.


내용 출처 및 참고 자료

  • Python Standard Library Documentation - copy module
  • Fluent Python (Luciano Ramalho 저) - 객체 참조 및 가변성 챕터
  • Stack Overflow - "Understanding deepcopy vs shallow copy in Python"
728x90