ViewComponent를 Turbo Rails의 broadcast_action_later_to와 함께 사용하려면 ViewComponent 객체가 Active Job에 의해 직렬화될 수 있어야 합니다. 이는 broadcast_action_later_to가 인수를 직렬화하여 백그라운드 Job으로 실행하기 때문입니다. 그러나 ViewComponent를 직렬화하는 과정에는 몇 가지 도전 과제가 존재합니다. 특히 ViewComponent의 initialize 메서드가 컬렉션 렌더링을 위해 파라미터 내성(introspection)을 수행하므로, 일반적인 방식으로 initialize를 재정의하거나 모듈을 prepend하는 것이 복잡성을 야기합니다. 이러한 제약 사항을 고려하여, 저자는 다음의 해결책을 제시합니다.
serializable 클래스 메서드 도입
ViewComponent의 인스턴스 생성 시 new 대신 serializable 클래스 메서드를 사용합니다. 이 메서드는 ViewComponent를 인스턴스화하면서 초기화 인수를 @serializable_args 인스턴스 변수에 저장하여 직렬화에 필요한 정보를 보존합니다.
ruby
module ViewComponent
module Serializable
extend ActiveSupport::Concern
included do
attr_reader :serializable_args
end
class_methods do
def serializable(*args)
new(*args).tap do |instance|
instance.instance_variable_set(:@serializable_args, args)
end
end
ruby2_keywords(:serializable)
end
end
end
커스텀 ViewComponentSerializer 구현
Active Job이 ViewComponent 객체를 직렬화하고 역직렬화할 수 있도록 ActiveJob::Serializers::ObjectSerializer를 상속받는 ViewComponentSerializer를 정의합니다.
-
serialize?메서드는 인수가ViewComponent::Base의 인스턴스이고serializable_args에 응답하는지 확인하여 직렬화 대상을 식별합니다. -
serialize메서드는 ViewComponent의 클래스 이름과serializable_args를 직렬화하여 저장합니다. -
deserialize메서드는 저장된 클래스 이름과 인수를 사용하여 ViewComponent 객체를 재구성합니다.
ruby
class ViewComponentSerializer < ActiveJob::Serializers::ObjectSerializer
def serialize?(argument)
argument.is_a?(ViewComponent::Base) && argument.respond_to?(:serializable_args)
end
def serialize(view_component)
super(
"component" => view_component.class.name,
"arguments" => ActiveJob::Arguments.serialize(view_component.serializable_args),
)
end
def deserialize(hash)
hash["component"].safe_constantize&.new(*ActiveJob::Arguments.deserialize(hash["arguments"]))
end
ActiveJob::Serializers.add_serializers(self)
end
이러한 접근 방식은 broadcast_action_later_to와 같은 비동기 작업에서 ViewComponent를 renderable로 전달할 수 있게 하여, 백그라운드에서 Hotwire 컴포넌트 갱신을 처리하는 유연성을 제공합니다. 저자는 이 기능이 Rails, Active Job, Turbo의 현재 상태에서 일급 시민(first-class) 행동으로 간주되어야 한다고 주장하며, 라이브러리 유지보수자들이 인체공학, 복잡성, 성능 간의 최적의 균형을 찾도록 독려하고 있습니다.