본문 바로가기
Programming/Ruby On Rails

Ruby의 객체 복사

by 가론노미 2024. 3. 27.

Ruby에서는 배열이나 해시 같은 컬렉션 객체는 메모리에 참조(reference)로 저장되며, 이 참조를 통해 컬렉션의 데이터를 조작할 수 있다.

 

이 말은 아래와 같이 코드를 짜는 경우

default_manager_ids = [1,2]

requests.each do |request|
	notify_user_ids = default_manager_ids
    notify_user_ids.push(request.manager_id)
end

 

default_manager_ids와 notify_user_ids는 메모리를 함께 공유하게 된다.

notify_user_ids에 값을 추가하면 같은 메모리를 참조하고 있는 default_manager_ids의 값에도 동일하게 값이 추가되는 것이다.

 

이것이 의도한 것이라면 문제가 없지만, 값만 복사하여 다른 데이터로 취급하려고 했던 것이었다면 문제가 생길 수 있다.

값만 복사를 하려면?

  • dup (freeze 상태나 일부 메타데이터는 복사하지 않음)
notify_user_ids = default_manager_ids.dup
  • clone (freeze 상태와 객체의 싱글턴 메소드도 복사) 
notify_user_ids = default_manager_ids.clone
  • 슬라이싱
notify_user_ids = default_manager_ids.slice(0..-1)

 

추가 주의할 점

 

Ruby에서 clone과 dup 메소드는 모두 배열이나 해시 같은 컬렉션 객체의 얕은 복사를 만든다.

 

즉, 해당 메소드들이 새로운 컬렉션 객체를 생성하지만 컬렉션 내부의 요소들에 대한 참조는 원본 객체와 동일하게 유지한다는 의미이다.

예를 들어, 원본 배열에 중첩된 배열이나 해시가 포함되어 있다면, clone이나 dup을 사용해도 중첩된 내부 객체들은 복제되지 않는다.

 

이때, clone과 dup 사이에는 아주 약간의 차이가 있다.

  • clone은 원본 객체의 메타데이터까지 복사 (ex. 객체가 freeze 상태인 경우, clone을 사용하면 생성된 복사본도 freeze 상태가 됨)
  • dup은 freeze 상태를 복사하지 않으며, 일부 메타데이터도 복사 X

 

깊은 복사를 하고 싶다면?

Marshal 라이브러리로 직렬화 후 다시 역직렬화하는 방법을 사용하거나, 각 내부 요소에 대해 개별적으로 복사 로직을 구현해야 한다.