Ruby: 참조값에 의한 전달
Source: Dev.to
대부분의 Ruby 개발자는 흔히 “pass‑by‑reference‑value”라고 불리는 개념에 익숙합니다.
가변 객체(예: String)를 다른 변수에 할당하면 두 변수 모두 같은 객체를 참조합니다. 하나의 참조를 통해 객체를 변형하면 다른 변수에서도 그 변형이 반영됩니다.
Simple Assignment Example
var1 = "Hello, World"
var2 = var1
var2.gsub!("World", "Ruby")
puts var1 # => Hello, Ruby
Service Class Example
class DummyClass
attr_accessor :value
def initialize(value)
@value = value
end
end
dummy = DummyClass.new("Hello, World!")
puts "Before service action:"
puts dummy.value # => Hello, World!
class ServiceClass
def initialize(value)
@value = value
end
def perform_action
local_value = @value
local_value.gsub!("World", "Ruby")
end
end
ServiceClass.new(dummy.value).perform_action
puts "After service action:"
puts dummy.value # => Hello, Ruby!
Real‑World Pitfall
실제 운영 코드베이스에서는 같은 동작이 은밀한 버그가 될 수 있습니다. 문자열 속성을 가진 ActiveRecord 모델이 템플릿 문자열 안의 플레이스홀더(예: %%BRAND%%)를 대체하는 서비스에 전달된다고 가정해 보세요:
# Hypothetical usage
Interpolator.custom_interpolate(source, brand_name)
메서드 이름 custom_interpolate는 새로운 문자열을 반환한다는 인상을 주지만, 구현이 source 객체에 gsub! 같은 변형 메서드를 사용한다면 레코드가 저장되기 전에 모델의 속성이 메모리 상에서 수정됩니다. 이러한 부수 효과는 애플리케이션 전반에 걸쳐 예상치 못한 데이터 변화를 초래할 수 있습니다.
Best Practices
-
변형되지 않는 메서드(
gsub대신gsub!)를 사용해 변환된 값만 필요할 때는 변형 메서드를 피합니다. -
입력을 복제하여 원본 객체를 보호합니다:
def safe_interpolate(str, replacements) str = str.dup # perform non‑mutating transformations str.gsub!("%BRAND%", replacements[:brand]) str end -
메서드가 의도적으로 인자를 변형한다면 부수 효과를 명확히 문서화합니다.
-
서비스 호출 후 객체가 의도치 않게 변경되지 않았는지 테스트를 작성합니다.
변형 가능성에 대해 명시적이고 방어적인 복사를 사용하면 Ruby의 pass‑by‑reference‑value 특성으로 인한 미묘한 버그를 피할 수 있습니다.