최악의 시간 낭비 버그
핵심 요약
파이썬에서 리스트나 딕셔너리를 복사할 때 ‘얕은 복사(Shallow copy)’로 인해 예상치 못한 버그가 발생할 수 있습니다. 얕은 복사는 값 자체가 아닌 메모리 주소 정보만 복사하여, 원본 데이터와 복사본이 동일한 메모리 공간을 공유하게 됩니다. 특히 함수 인자로 리스트를 넘겨주거나 중첩 리스트를 다룰 때 copy.deepcopy()를 사용하여 원본으로부터 완전히 분리된 깊은 복사를 수행해야 버그를 방지할 수 있습니다.
주요 내용
얕은 복사(Shallow copy)란 무엇인가
파이썬에서 리스트나 딕셔너리 같은 객체를 복사할 때 ‘얕은 복사’ 개념을 이해하는 것이 중요합니다.
- ‘복사한다’는 말의 일반적인 개념: 일상생활에서 복사는 원본을 본떠 새로운 별개의 존재를 만드는 것을 의미합니다. 예를 들어 컴퓨터 파일을 복사하면 내용이 같지만 별개의 메모리를 점유하는 두 개의 파일이 생깁니다.
- 파이썬 리스트, 딕셔너리의 구현: 파이썬에서 변수에 리스트를 대입하면, 변수는 리스트 데이터가 위치한 ‘메모리 주소값’을 가집니다.
append나pop같은 리스트 기능은 이 주소값을 기반으로 동작합니다.
얕은 복사는 바로 이 ‘주소 정보’만을 복사하는 개념입니다. 이는 복사본 변수가 원본 변수와 동일한 메모리 주소를 가리키게 하여, 실제 데이터는 메모리에 하나만 존재하게 합니다.
실제 동작과 우리의 의도 사이의 괴리
- 우리의 의도: 보통 복사를 생각할 때 원본과 똑같이 생겼지만 별개의 존재가 만들어질 것이라고 예상합니다. 따라서 복사본을 수정해도 원본은 변경되지 않을 것이라고 생각합니다.
- 실제 동작 (얕은 복사): 얕은 복사의 경우, 복사된 정보는 주소값이므로, 복사본에 변경을 가하면 동일한 주소에 있는 원본 데이터도 변경됩니다. 이로 인해 난해한 버그가 발생할 수 있습니다.
얕은 복사로 인한 버그 사례
- 변수 직접 대입:
p = [1, 2, 3]piui = p(piui가 p와 동일한 리스트의 주소를 참조)piui.append(4)(piui를 통해 리스트에 4를 추가)- 결과적으로
p와piui모두[1, 2, 3, 4]가 됩니다.
- 함수 인자로 리스트 전달:
- 함수가 리스트를 인자로 받아 내부에서 해당 리스트를 수정하는 코드가 있는 경우.
def func(numbers: list):if 3 not in numbers: numbers.append(3)return sum(numbers)nums = [10, 20, 30]result = func(nums)- 함수 내부에서
numbers.append(3)이 실행되면, 함수 외부의nums리스트도[10, 20, 30, 3]로 변경됩니다. 이는 함수의 부작용(side effect)이며, 의도치 않은 결과를 초래합니다.
얕은 복사와 깊은 복사를 통한 버그 방지
list.copy()(얕은 복사):- 함수 내에서 입력 리스트를 수정해야 하지만 원본은 보존하고 싶을 때
numbers = numbers.copy()를 사용할 수 있습니다. - 이 방법은 ‘내부 데이터’를 복사하여 새로운 리스트를 생성하므로, 함수 내부의 리스트 변경이 외부 리스트에 영향을 주지 않습니다.
- 단점:
list.copy()는 중첩 리스트(리스트 안의 리스트)의 경우, 바깥 리스트는 새로 생성하지만 안쪽 리스트의 ‘주소 정보’만 복사합니다. 따라서 중첩된 리스트를 수정하면 여전히 원본의 중첩 리스트가 변경됩니다.
- 함수 내에서 입력 리스트를 수정해야 하지만 원본은 보존하고 싶을 때
copy.deepcopy()(깊은 복사):copy모듈의deepcopy()함수를 사용하면 리스트 내부의 리스트까지 ‘전부 다’ 원본과는 완전히 별개인 새로운 데이터를 만들어 복사합니다.- 이는 중첩된 구조를 포함하여 모든 데이터를 재귀적으로 복사하여, 원본과 완벽하게 독립적인 복사본을 생성합니다.
핵심 데이터 / 비교표
(영상 내에 수치나 통계 자료가 없어 해당 섹션은 생략합니다.)
타임스탬프별 핵심 포인트
| 시간 | 핵심 내용 |
|—|—|
| 00:00 | 파이썬 코딩 시 리스트/딕셔너리 복사로 인한 버그 문제 제기 |
| 00:07 | 별 생각 없이 복사하면 버그 위험이 있으며, 원인 파악이 어려움 |
| 00:22 | 문제의 원인은 ‘Shallow copy’ (얕은 복사) 때문임 |
| 00:32 | 얕은 복사를 이해하기 위한 두 가지 전제 설명 |
| 00:40 | 1. ‘복사한다’는 말에서 떠오르는 개념 (원본과 별개의 존재 생성) |
| 00:47 | 2. Python list, dict의 구현은 주소값 (메모리 주소 참조) |
| 01:06 | 컴퓨터 파일 복사 예시를 통해 ‘별개의 데이터’ 개념 설명 |
| 01:34 | Python 변수의 리스트 대입 시 변수는 실제 데이터의 ‘위치’를 가짐 |
| 02:09 | 얕은 복사의 실제 동작: 변수들이 동일한 메모리 주소를 공유하여 같은 데이터를 참조함 |
| 02:27 | 실제 동작(주소 공유)과 우리의 의도(별개 존재) 간의 괴리 설명 |
| 02:57 | 코딩 시 얕은 복사 버그를 방지하기 위한 경각심 강조 |
| 03:05 | 직접 대입 (piui = p) 시 원본 리스트도 함께 변경되는 코드 예시 |
| 03:51 | 함수 인자로 리스트를 넘길 때 원본이 변경되는 예시 (numbers.append(3)) |
| 04:32 | numbers.copy()를 사용하여 얕은 복사로 원본 변경 방지 (단순 리스트의 경우) |
| 05:03 | list.copy()의 한계: 중첩 리스트의 내부 리스트는 여전히 주소를 공유하여 진정한 깊은 복사가 아님 |
| 05:48 | copy.deepcopy()를 사용하여 중첩 리스트까지 완전히 복사하는 방법 제시 |
| 06:01 | 결론: 함수 인자로 리스트를 받을 때, 변경이 필요하면 copy.deepcopy() 사용을 권장 |
결론 및 시사점
파이썬에서 리스트나 딕셔너리를 다룰 때 ‘복사’라는 용어는 실제 동작과 우리가 흔히 생각하는 개념 사이에 큰 차이가 있습니다. 단순 대입이나 list.copy()와 같은 얕은 복사는 메모리 주소 정보만을 복사하여, 원본 객체와 복사본이 내부 데이터를 공유하게 만듭니다. 이는 특히 함수 인자로 리스트를 전달하거나 중첩된 데이터 구조를 사용할 때 예상치 못한 버그로 이어질 수 있습니다. 따라서 원본 객체에 영향을 주지 않으면서 완전히 독립적인 복사본이 필요하다면, copy 모듈의 deepcopy() 함수를 사용하는 것이 가장 안전하고 확실한 방법입니다.
추가 학습 키워드
- Python Shallow Copy
- Python Deep Copy
- Mutable vs Immutable Types (Python)
- Reference vs Value (Programming)
- Python
copymodule
기본 정보
| 항목 | 내용 | |—|—| | 채널 | 임커밋 | | 카테고리 | 프로그래밍 | | 게시일 | 2026-03-02 | | 영상 길이 | 6:27 | | 처리 엔진 | gemini-2.5-flash | | 원본 영상 | YouTube에서 보기 |