기존의 turbo_streams
방식은 백그라운드 작업이나 모델에서 Turbo::StreamsChannel.broadcast_replace_to
와 같은 메서드를 사용하여 특정 target
ID를 가진 HTML 요소를 파셜로 교체하는 방식으로 작동합니다. 이때 target
ID는 파셜 내의 HTML 요소 ID와 일치해야 하며, 이는 ActionView::RecordIdentifier.dom_id
와 같은 헬퍼를 사용하더라도 여전히 여러 곳에 동일한 식별자가 분산되는 문제를 발생시킵니다. 이는 컴포넌트의 ID가 변경될 때마다 참조하는 모든 코드를 수정해야 하는 번거로움으로 이어집니다.
이러한 문제를 해결하기 위해 저자는 ViewComponent(또는 Phlex와 같은 유사 라이브러리)의 활용을 제안합니다. ViewComponent를 사용하면 뷰 로직뿐만 아니라 관련된 헬퍼, 심지어는 UI 갱신 로직까지 하나의 Ruby 클래스 내에 캡슐화할 수 있습니다. 예를 들어, UI::UserCard
와 같은 컴포넌트 클래스 내에 id
메서드를 정의하여 dom_id
를 중앙 집중화하고, broadcast_channel
메서드를 통해 ActionCable 채널 정보를 관리할 수 있습니다. 더 나아가, broadcast_refresh!
와 같은 메서드를 컴포넌트 내부에 구현하여 해당 컴포넌트가 언제든지 자신을 갱신하도록 할 수 있습니다. 이 broadcast_refresh!
메서드는 Turbo::StreamsChannel.broadcast_replace_to
를 호출할 때 renderable: self
옵션을 사용하여 컴포넌트 객체 자체를 렌더링하도록 지시함으로써, 파셜과 locals
를 명시적으로 전달할 필요 없이 컴포넌트가 스스로를 갱신할 수 있게 합니다.
이러한 접근 방식은 컨트롤러에서도 유용하게 활용됩니다. 예를 들어, 사용자가 버튼을 클릭하여 이메일 전송과 같은 비동기 작업을 시작할 때, 컨트롤러는 즉시 turbo_stream.replace
를 통해 컴포넌트의 ‘전송 중’ 상태를 반영하는 새 버전을 렌더링할 수 있습니다. 백그라운드 작업이 완료되면, 해당 작업 내에서 다시 broadcast_refresh!
를 호출하여 컴포넌트의 최종 상태(예: 이메일 전송 완료)를 반영하도록 합니다.
또한, 컴포넌트의 상태(예: sending_email?
)를 활용하여 필요한 경우에만 ActionCable 스트림을 열고 닫는 조건부 스트리밍을 구현할 수 있습니다. 이는 불필요하게 스트림을 열어두는 것을 방지하여 리소스 효율성을 높입니다. 예를 들어, 이메일 전송 중일 때만 스트림을 구독하고, 전송이 완료되면 구독을 해제하도록 하여 UI 업데이트의 라이프사이클을 더욱 정교하게 제어할 수 있습니다.