얕은 복사와 깊은 복사
쉽게 풀면
서류 봉투 안에 여러 장의 문서가 들어 있다고 생각해 봅시다. "얕은 복사"는 새 봉투를 만들되 그 안에 원본 문서를 그대로 다시 넣는 것과 같습니다. 봉투(겉모습)는 두 개가 되었지만 안에 든 문서는 여전히 하나뿐이라서, 한쪽 봉투에서 문서를 고치면 다른 쪽 봉투를 열어봐도 똑같이 고쳐진 내용이 보입니다. 반면 "깊은 복사"는 봉투뿐 아니라 안에 든 문서까지 전부 복사기로 새로 뽑아 넣는 것입니다. 이 경우 두 봉투는 완전히 독립적이어서 한쪽 문서를 고쳐도 다른 쪽에는 아무 영향이 없습니다. 프로그래밍에서 배열이나 객체처럼 여러 값을 담는 자료구조를 복사할 때, 이 두 방식 중 무엇을 썼는지에 따라 결과가 크게 달라집니다.
왜 중요한가
얕은 복사와 깊은 복사를 혼동하면 원본 데이터가 의도치 않게 변형되는 버그가 생기는데, 이런 버그는 실행 중 오류를 내지 않고 조용히 결과만 오염시키기 때문에 재현성 문제로 이어지기 쉽습니다. 그래서 머신러닝 실험, 시뮬레이션, 병렬·분산 처리처럼 동일한 데이터 구조를 여러 번 복제해 독립적으로 다루어야 하는 연구에서는 복사 방식을 명확히 관리하는 것이 실험 신뢰성의 기본 조건으로 취급됩니다.
논문에서는 이렇게 쓰입니다
이 문장은 "데이터를 복사해서 가공하려 했는데, 겉모습만 복사되고 내부 내용은 원본과 공유되고 있어서 가공 과정에서 원본 데이터까지 함께 바뀌어 버렸다. 그래서 내부 내용까지 완전히 새로 복사하는 방식으로 고쳤다"는 뜻입니다.
여러 스레드가 동시에 같은 자료구조를 다루는 병렬 처리 상황에서, 서로 간섭이 생기지 않도록 깊은 복사로 상태를 분리했다는 뜻입니다.
이 경우는 원본 변형 위험보다 메모리 효율이 더 중요한 상황에서 얕은 복사를 의도적으로 선택한 예로, 얕은 복사가 항상 문제인 것은 아니라는 점을 보여줍니다.
조금 더 깊게 보면
실제 구현에서는 얕은 복사와 깊은 복사가 이분법적으로 딱 나뉘지 않는 경우가 많습니다. 리스트 안에 리스트가 들어 있는 것처럼 자료구조가 여러 겹으로 중첩되어 있으면, 겉으로는 깊은 복사 함수를 썼더라도 특정 언어나 라이브러리의 구현 방식에 따라 가장 안쪽 요소는 여전히 원본과 참조를 공유하는 경우가 있습니다. 또한 순환 참조(객체가 자기 자신을 다시 참조하는 구조)가 있는 자료구조는 단순한 재귀적 복사로는 무한 루프에 빠질 수 있어, 이를 다루는 라이브러리는 별도의 참조 추적 로직을 포함하는 경우가 일반적입니다.
주의할 점
얕은 복사가 항상 나쁜 것은 아닙니다. 원본을 바꿀 계획이 없거나 메모리와 처리 속도를 아껴야 할 때는 얕은 복사가 더 효율적입니다. 문제는 "복사했으니 이제 완전히 별개다"라고 착각한 채 코드를 짜는 경우입니다. 특히 여러 겹으로 중첩된 자료구조(리스트 안에 리스트가 있는 경우 등)는 겉으로 보기엔 깊은 복사를 한 것 같아도 안쪽 요소는 여전히 원본과 공유되고 있을 수 있으므로, 자료구조의 복사 방식을 사용하는 도구나 라이브러리의 문서에서 정확히 확인해야 합니다.