Ruby에서 객체를 복사할 수 있다는 사실을 알고 계셨나요? 게다가 복사 방법도 한 가지가 아니라 두 가지나 됩니다!
바로 이 두 메서드입니다:
dupclone
곧 두 메서드의 차이점을 자세히 살펴보겠습니다. 그 전에 먼저 이런 질문부터 드려볼까요?
객체를 복제해야 하는 이유는?
Ruby의 많은 객체는 변경 가능(mutable)합니다. 즉, 언제든 내용을 수정할 수 있죠.
그래서 객체를 변경하면서도 원본은 그대로 보존하고 싶을 때 객체를 복제하면 유용합니다.
예시를 들어볼게요. 배열의 첫 번째 요소만 제외한 나머지 요소들로 구성된 새로운 배열이 필요하다고 가정해봅시다.
첫 번째 방법은 범위 연산자를 활용하는 것입니다:
a = [1,2,3,4,5] a[1..-1] # [2,3,4,5]
두 번째 방법은 배열을 복제한 후 요소를 제거하는 것입니다:
b = a.clone b.shift # [1] b # [2,3,4,5]
두 예제 모두 원본 배열 a는 그대로 유지된다는 점이 공통점입니다.
Frozen(동결) 객체와의 관계
dup과 clone은 Ruby의 다른 메서드 쌍(map/collect처럼 서로 별칭 관계인 메서드)과 달리, 서로 미묘하지만 중요한 차이점을 가지고 있습니다.
두 가지 대상의 공통점과 차이점을 비교해 보는 것은 이해도를 높이는 아주 좋은 학습 방법입니다.
두 메서드 모두 객체를 복사한다는 점은 같지만, dup은 객체의 메타 정보(속성)까지는 복사하지 않는다는 점이 다릅니다.
여기서 말하는 객체 속성이란 다음과 같습니다:
- frozen 상태 (동결 여부)
- tainted 상태 (오염 여부)
- 싱글턴 클래스(singleton class)
실제 예제를 통해 확인해 보겠습니다:
a = Object.new.freeze b = a.dup b.frozen? # false b = a.clone b.frozen? # true
동결된 객체를 dup으로 복사하면 동결 상태가 해제되지만, clone으로 복사하면 동결 상태가 그대로 유지됩니다.
참고로 Ruby 2.4부터는 clone에 옵션을 지정하여 원본의 동결 상태를 무시하고 복사할 수 있습니다.
예시:
a.clone(freeze: true) # 강제로 동결 상태로 복사 a.clone(freeze: false) # 동결 상태를 해제하며 복사
얕은 복사 vs 깊은 복사 (Shallow Copy vs Deep Copy)
객체 복사에는 겉으로 드러나지 않는 중요한 개념이 하나 더 숨어 있습니다.
dup이든 clone이든 어떤 메서드로 복사하든, 기본적으로 수행되는 것은 얕은 복사(shallow copy)라는 점입니다.
즉, 객체 내부에 포함된 다른 객체들은 함께 복사되지 않습니다.
다르게 표현하면:
문자열로 이루어진 배열을 복사할 때, 복사되는 것은 배열 자체뿐이며 배열 안의 문자열들은 복사되지 않습니다.
직접 확인해 보겠습니다:
original = %w(apple orange banana) copy = original.clone original.map(&:object_id) # [23506500, 23506488, 23506476] copy.map(&:object_id) # [23506500, 23506488, 23506476]
배열을 복제했음에도 object_id가 동일하게 출력됩니다. 즉, 배열 안의 문자열들은 여전히 같은 객체라는 뜻입니다.
내부 객체까지 복사하려면 다음과 같이 작성할 수 있습니다:
strings.clone.map(&:clone)
이렇게 하면 배열과 문자열 모두 복제됩니다. 다만 주의할 점은 이 방법 역시 한 단계(1 depth)만 복사한다는 것입니다. 더 깊은 계층까지 복사해야 한다면 ActiveSupport에서 제공하는 deep_dup 메서드를 활용해 보세요.
정리
지금까지 Ruby에서 객체를 복제하는 방법을 배웠습니다. dup과 clone 메서드의 차이점, 그리고 얕은 복사와 깊은 복사의 개념까지 살펴보았습니다.
dup: frozen 상태, 싱글턴 클래스 등 메타 정보를 복사하지 않음clone: frozen 상태와 싱글턴 클래스까지 그대로 복사함- 두 메서드 모두 기본적으로 얕은 복사를 수행하므로, 중첩된 객체까지 복사하려면 추가 처리 필요
읽어주셔서 감사합니다!