프로그래밍Python 개발자 (미들웨어, 백엔드, 테스트)

Python에서 컬렉션을 올바르게 복사하는 방법, copy()가 부족한 경우와 그 이유, 그리고 deep copy가 필요한 경우는 언제인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문 역사: Python에서 객체 복사(특히 컬렉션 - 목록, 사전)는 중요하다. 단순히 변수를 할당하는 것은 복사가 아닌 새로운 참조를 만든다. 불필요한 "부작용"을 방지하기 위해 copy()와 deepcopy()와 같은 특수 메소드가 도입되었다.

문제: 중첩된 컬렉션(목록 안의 목록, 사전 안의 사전) 작업 시, 단순 copy()는 컨테이너만 복사할 뿐 내부 요소는 복사하지 않는다. 이는 수정된 중첩 요소가 모든 "복사본"에 반영되는 어려운 버그를 일으킬 수 있다.

해결책: copy.copy()는 얕은 복사(shallow copy)를 생성하여 상위 수준의 새로운 컨테이너를 만들지만 중첩된 객체는 동일하게 남는다. copy.deepcopy()는 모든 중첩된 객체를 재귀적으로 복사한다.

코드 예제:

import copy lst = [[1, 2], [3, 4]] shallow = copy.copy(lst) deep = copy.deepcopy(lst) lst[0][0] = 10 print(shallow) # [[10, 2], [3, 4]] — 중첩 객체가 변경됨! print(deep) # [[1, 2], [3, 4]] — deepcopy가 원래 상태를 유지함

핵심 특징:

  • 단순 할당은 참조만 복사하며 복제 없음
  • .copy() (또는 copy.copy())는 "외부" 컨테이너를 복사하지만 중첩 객체는 복사하지 않음
  • .deepcopy()는 복사의 완전한 독립성을 위해 사용됨

함정 질문.

목록 복사를 위해 lst2 = lst[:]라고 작성하는 것이 충분한가요?

lst2 = lst[:]는 목록의 얕은 복사를 생성하지만 중첩된 객체(예: 목록 내의 목록)는 여전히 동일하다. 평면 목록의 경우 충분하지만 중첩 구조에는 그렇지 않다.

예시:

lst = [[1], [2]] lst2 = lst[:] lst[0][0] = 99 print(lst2) # [[99], [2]] — 중첩 요소가 두 "복사본" 모두에서 변경됨

모든 컬렉션에 대해 .copy() 메소드가 동일하게 작동하나요?

아니요. 예를 들어, dict.copy()는 얕은 복사를 수행하고, list.copy()는 Python 3.3부터 등장했으며, set의 경우에는 set.copy()가 있다. 사용자 정의 객체의 경우 .copy()의 지원은 구현된 메소드에 따라 다르다.


모든 곳에서 deepcopy로 대체할 수 있나요? 안전하고 효율적인가요?

아니요. deepcopy는 비싼 연산이다: 대규모 구조에서 성능 문제를 일으킬 수 있으며, 복사할 수 없는/클로저 혹은 비표준 객체에서는 작동하지 않는다. 완전히 독립적이고 재귀적인 복사가 정말로 필요할 때만 deepcopy를 사용해야 한다.


일반적인 실수 및 안티 패턴

  • 중첩 구조에 대해 deepcopy 대신 일반 할당 또는 .copy() 사용
  • 모든 객체에 대해 deepcopy 적용 (프로그램 속도를 저하시킴)
  • 복사를 지원하지 않는 객체(예: 파일, 스트림) 복사 시도

실제 사례

부정적 케이스

테스트에서 "사전의 사전"을 dict.copy()를 통해 복사했다. 이로 인해 한 사용자의 중첩 구조에서의 수정이 다른 테스트에도 갑작스럽게 영향을 미쳤다(데이터가 전역적으로 변형됨).

장점:

  • 빠름
  • 간단함

단점:

  • 명확하지 않은 버그, 환경의 격리 파괴

긍정적 케이스

copy.deepcopy()를 도입하고, 각 객체의 중첩 수준과 구조의 특징을 문서화했으며, 큰 데이터에서는 "수동"으로 부분적으로 복사하는 것을 피했다.

장점:

  • 환경의 투명성
  • 객체 독립성

단점:

  • 구조의 이해가 필요함
  • 대규모 구조에서 더 많은 메모리와 프로세서 시간을 소모함