작성자는 Rails 애플리케이션에서 Singleton 패턴을 사용하는 클라이언트 라이브러리 마이그레이션 중 테스트 환경에서 Singleton 인스턴스 상태 관리 문제에 직면했습니다. 전체 테스트 스위트 실행 시, 첫 번째 테스트에서 설정된 Singleton 인스턴스의 상태가 이후 모든 테스트에 전파되어 예측 불가능한 결과를 초래했습니다. 이 문제를 해결하기 위해 Ruby Singleton 모듈의 소스 코드를 분석했고, 비공개 메서드인 __init__를 발견했습니다.
Singleton.__init__를 이용한 인스턴스 재설정
Singleton.__init__(클래스명) 메서드는 해당 Singleton 클래스의 인스턴스를 nil로 설정하고 새로운 뮤텍스를 생성하여 스레드 안전성을 유지하며 클래스 상태를 재설정합니다. 이 메서드를 호출하면 다음 instance 호출 시 완전히 새로운 Singleton 인스턴스가 생성됩니다.
작성자는 이 기법을 활용하여 Rails.env를 스텁하고 테스트 전후로 Singleton 인스턴스를 재설정함으로써, 각 테스트가 독립적인 환경에서 실행되도록 했습니다. RSpec 테스트 코드 예시는 다음과 같습니다.
ruby
RSpec.describe "Clients migration" do
before { setup_env('production') }
after { setup_env('test') }
it 'generates the correct URL for client instance' do
# ...
end
def setup_env(env)
Rails.env = env
Singleton.__init__(MyClient)
Singleton.__init__(AnotherClient)
# ...
end
end
clone 메서드의 다른 동작
Singleton 모듈의 클래스 레벨 clone 메서드는 내부적으로 __init__를 호출하지만, 기존 Singleton 클래스를 재설정하는 대신 새로운 익명 Singleton 클래스를 반환합니다. 이는 기존 인스턴스에 영향을 주지 않고 독립적인 상태를 가진 새로운 Singleton 인스턴스를 얻고자 할 때 유용합니다. Timer 클래스 예시는 다음과 같습니다.
ruby
class Timer
include Singleton
attr_reader :timestamp
def initialize
@timestamp = Time.now
end
end
Timer.instance.timestamp
Timer.clone.instance.timestamp
Singleton.__init__(Timer).instance.timestamp
이러한 방법들을 통해 Singleton 인스턴스의 상태를 효과적으로 관리하고 테스트의 독립성을 확보할 수 있습니다.