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

[PYTHON] 리스트 복사할 때 b = a라고 하면 왜 같이 변하나요? (깊은 복사 vs 얕은 복사)

by Papa Martino V 2026. 2. 5.
728x90

깊은 복사 vs 얕은 복사
깊은 복사 vs 얕은 복사

 

파이썬을 처음 접하는 개발자들이 가장 당혹스러워하는 순간 중 하나는 분명히 리스트를 복사했다고 생각했는데, 복사본(b)을 수정하니 원본(a)까지 함께 변하는 현상을 목격할 때입니다. "나는 분명 b = a라고 썼을 뿐인데, 왜 파이썬은 내 의도와 다르게 동작할까?"라는 의문이 생기기 마련입니다. 이 현상은 단순히 파이썬의 오류가 아니라, 파이썬이 객체를 메모리에 저장하고 참조(Reference)하는 방식에서 기인합니다. 오늘 우리는 단순 할당, 얕은 복사(Shallow Copy), 그리고 깊은 복사(Deep Copy)의 메커니즘을 심층적으로 분석하여, 데이터 무결성을 지키는 프로그래밍 기법을 완벽히 마스터해 보겠습니다.


1. 단순 할당(Assignment): 복사가 아닌 '별명' 짓기

파이썬에서 b = a라는 코드는 데이터를 물리적으로 복제하는 명령이 아닙니다. 엄밀히 말하면 a가 가리키고 있는 메모리 주소(객체)에 b라는 또 다른 이름을 붙이는 행위, 즉 참조 공유입니다.

왜 같이 변할까?

파이썬의 모든 변수는 객체의 위치를 가리키는 포인터 역할을 합니다. b = a를 실행하면 두 변수는 동일한 메모리 주소를 가리키게 됩니다. 따라서 b를 통해 리스트의 요소를 수정하면, 같은 리스트를 보고 있는 a의 내용도 당연히 바뀐 것처럼 보이게 됩니다.

구분 단순 할당 (b = a) 얕은 복사 (Shallow Copy) 깊은 복사 (Deep Copy)
메모리 주소 원본과 동일 (id(a) == id(b)) 새로운 주소 (id(a) != id(b)) 새로운 주소 (내부 객체까지 모두 새 주소)
내부 리스트 수정 시 원본에 즉시 반영 원본에 반영 (가변 객체일 경우) 원본과 완전히 독립
주요 사용 모듈 기본 연산자 list(), [:] 슬라이싱, copy.copy() copy.deepcopy()

2. 얕은 복사(Shallow Copy)의 한계

b = a[:] 또는 b = list(a)를 사용하면 새로운 리스트 객체가 생성됩니다. 하지만 여기서 '얕은'이라는 표현이 붙은 이유가 있습니다. 리스트 내부에 또 다른 가변 객체(리스트, 딕셔너리 등)가 포함되어 있다면, 그 내부 객체까지는 복사하지 못하고 기존의 참조값을 그대로 가져오기 때문입니다.

Sample Example: 얕은 복사의 배신

import copy

# 중첩된 리스트 생성
original = [[1, 2], [3, 4]]
shallow_copied = copy.copy(original)

# 1단계 리스트 수정 (영향 없음)
shallow_copied.append([5, 6]) 

# 내부 리스트 수정 (원본도 함께 변함!)
shallow_copied[0][0] = 99

print(f"Original: {original}")
# 결과: [[99, 2], [3, 4]] -> 원본이 오염됨!

3. 깊은 복사(Deep Copy): 완전한 독립 선언

중첩된 리스트나 복잡한 객체 구조에서 원본을 완벽하게 보존하고 싶다면 copy.deepcopy()를 사용해야 합니다. 깊은 복사는 재귀적으로 모든 내부 객체를 복제하여 원본과의 연결 고리를 완전히 끊어냅니다.

언제 깊은 복사를 써야 할까?

  • 설정값(Configuration) 데이터가 여러 모듈에서 공유되지만, 특정 모듈에서만 임시로 값을 변경해야 할 때.
  • 다차원 배열(Matrix) 연산을 수행할 때 원본 데이터를 보존해야 하는 경우.
  • Undo/Redo 기능을 구현하기 위해 이전 상태를 백업해둘 때.

4. 전문가를 위한 조언: 성능과 안정성 사이의 선택

무조건 깊은 복사를 쓰는 것이 정답은 아닙니다. deepcopy는 객체의 구조가 복잡할수록 메모리 사용량이 급증하고 연산 속도가 느려집니다. 따라서 데이터의 구조를 정확히 파악하고 적절한 복사 방식을 선택하는 것이 전문 개발자의 역량입니다.

  1. 단일 리스트(1차원): 슬라이싱 [:]이나 copy()로 충분합니다.
  2. 고정 데이터: 튜플(Tuple)과 같은 Immutable 객체를 사용하여 복사 문제 자체를 원천 차단하십시오.
  3. 대용량 수치 데이터: 파이썬 기본 리스트 대신 NumPy.copy()를 활용하는 것이 훨씬 효율적입니다.

5. 결론

파이썬에서 b = a가 같이 변하는 이유는 그것이 '값의 복사'가 아닌 '메모리 주소의 공유'이기 때문입니다. 이를 해결하기 위해 우리는 객체의 가변성(Mutability)을 이해하고, 상황에 맞는 복사 전략을 세워야 합니다. 얕은 복사는 껍데기만 새로 만들고, 깊은 복사는 알맹이까지 새로 만든다는 점을 명심하십시오.


출처 및 참고 문헌

  • Python Software Foundation. "Standard Library - copy module." Official Documentation.
  • Lutz, Mark. "Learning Python: Powerful Object-Oriented Programming." O'Reilly Media.
  • Real Python. "Shallow vs Deep Copying in Python."
728x90