← 2026-03-02 목록으로

최악의 시간 낭비 버그

핵심 요약

파이썬에서 리스트나 딕셔너리를 복사할 때 ‘얕은 복사(Shallow copy)’로 인해 예상치 못한 버그가 발생할 수 있습니다. 얕은 복사는 값 자체가 아닌 메모리 주소 정보만 복사하여, 원본 데이터와 복사본이 동일한 메모리 공간을 공유하게 됩니다. 특히 함수 인자로 리스트를 넘겨주거나 중첩 리스트를 다룰 때 copy.deepcopy()를 사용하여 원본으로부터 완전히 분리된 깊은 복사를 수행해야 버그를 방지할 수 있습니다.


주요 내용

얕은 복사(Shallow copy)란 무엇인가

파이썬에서 리스트나 딕셔너리 같은 객체를 복사할 때 ‘얕은 복사’ 개념을 이해하는 것이 중요합니다.

  1. ‘복사한다’는 말의 일반적인 개념: 일상생활에서 복사는 원본을 본떠 새로운 별개의 존재를 만드는 것을 의미합니다. 예를 들어 컴퓨터 파일을 복사하면 내용이 같지만 별개의 메모리를 점유하는 두 개의 파일이 생깁니다.
  2. 파이썬 리스트, 딕셔너리의 구현: 파이썬에서 변수에 리스트를 대입하면, 변수는 리스트 데이터가 위치한 ‘메모리 주소값’을 가집니다. appendpop 같은 리스트 기능은 이 주소값을 기반으로 동작합니다.

얕은 복사는 바로 이 ‘주소 정보’만을 복사하는 개념입니다. 이는 복사본 변수가 원본 변수와 동일한 메모리 주소를 가리키게 하여, 실제 데이터는 메모리에 하나만 존재하게 합니다.

실제 동작과 우리의 의도 사이의 괴리

얕은 복사로 인한 버그 사례

  1. 변수 직접 대입:
    • p = [1, 2, 3]
    • piui = p (piui가 p와 동일한 리스트의 주소를 참조)
    • piui.append(4) (piui를 통해 리스트에 4를 추가)
    • 결과적으로 ppiui 모두 [1, 2, 3, 4]가 됩니다.
  2. 함수 인자로 리스트 전달:
    • 함수가 리스트를 인자로 받아 내부에서 해당 리스트를 수정하는 코드가 있는 경우.
    • 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)이며, 의도치 않은 결과를 초래합니다.

얕은 복사와 깊은 복사를 통한 버그 방지

  1. list.copy() (얕은 복사):
    • 함수 내에서 입력 리스트를 수정해야 하지만 원본은 보존하고 싶을 때 numbers = numbers.copy()를 사용할 수 있습니다.
    • 이 방법은 ‘내부 데이터’를 복사하여 새로운 리스트를 생성하므로, 함수 내부의 리스트 변경이 외부 리스트에 영향을 주지 않습니다.
    • 단점: list.copy()는 중첩 리스트(리스트 안의 리스트)의 경우, 바깥 리스트는 새로 생성하지만 안쪽 리스트의 ‘주소 정보’만 복사합니다. 따라서 중첩된 리스트를 수정하면 여전히 원본의 중첩 리스트가 변경됩니다.
  2. 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() 함수를 사용하는 것이 가장 안전하고 확실한 방법입니다.


추가 학습 키워드


기본 정보

| 항목 | 내용 | |—|—| | 채널 | 임커밋 | | 카테고리 | 프로그래밍 | | 게시일 | 2026-03-02 | | 영상 길이 | 6:27 | | 처리 엔진 | gemini-2.5-flash | | 원본 영상 | YouTube에서 보기 |