rails7 Hotwire Stimulus란?

Posted by negabaro kim on Sunday, December 3, 2023 Tags: rails/hotwire   5 minute read

Stimulus란?(rails7 Hotwire Stimulus란?)

Hotwire을 구성하는 새개의 요소중 하나

DOM을 조작할때 사용함

Turbo와 상성이 좋은 js라이브러리임

DOM을 조작할때? 예를들면

Js와 dom에서 사용할 이벤트를 연결할때 사용하는데

예를들어

  1. 어떤 button을 클릭했을때 특정 dom의 색깔을 변경하고 싶다고 했을때
  2. 특정 영역에 파일을 드래그&드롭했을때 POST를 발생시키고 싶을때

등등에 사용한다.

Stimulus를 사용하면 좋은점

위와 같은 내용을 구현할때

바닐라js의 경우 getElementById(“xx”)로 특정 dom을 찾아서 해당 돔에 addEventListener등으로 이벤트 등록하고 해당 이벤트 발생시 특정 함수를 실행하는등 js코드가 길어짐

이런걸 조금 더 간단하게 하기 위해 jquery가 나왔고 그걸 더 모던하게 하기위해 vue,react가 나왔음. 현재 주류는 vue,react

vue,react를 사용하면 어떤 사양이라도 모던한 웹 개발이 가능하다는 장점이 있는 반면, 적어야 하는 코드량, 환경구축(webpack, api서버,인증 등)의 코스트가 발생함.

필자의 경험상 js로 인한 운영코스트(버그수정, 테스트코드 작성)등 어마어마함

특히 비지니스적으로 가능성이 있는지 검증되지 않는 상태에서 이 코스트를 안고 가는건 리스크

이때, stimulus를 이용하면 js코드량이 줄어들고,환경구축 비용이 들지 않기 때문에 로우 리스크로 웹 개발을 진행할 수 있음.

특히 빠르게 초기 릴리스해서 비지니스적 수요를 확인할때 유용함.

비지니스적 요건이 있는걸 확인후 클라이언트 단에서 복잡한 상태처리를 필요로 한다면 SPA를 도입하면됨.

Progressive Enhancement적인 개발이 가능해지는 이유

Stimulus를 사용하면 안되는경우

반대로 아래와 같은 경우 stimulus보다는 SPA를 검토해야한다.

비지니스적 요건 검증후, 클라이언트 단에서 복잡한 상태처리를 필요로 하는경우 높은 수준의 퍼포먼스적와 사용자경험의 요건이 있는 경우

Stimulus를 사용하면 느낀 단점

비지니스적 관점 외에도 stimulus를 사용하며 느낀 단점도 정리해본다. 필자의 주관적인 견해이다.

typescript쓰기 어려움(애초에 js코드를 없애는게 목표이므로) 어쩔수 없이 js코드양이 많아지는 경우가 있는데 그때 현타가 온다.

이벤트 attach에 대한 테스트(클릭시 뭐가 동작함)코드 작성이 빡셈

나중에 봤을때 이 dom이랑 뭐가 연결되있는지 파악이 빡셈 이 부분때문에 필자는 유니크한 문자열과 결합해서 사용중 예를들어 search-form이면 search-form-xdw35i3sg 이런식으로 만들어 놓으면 검색하기 편함.

Stimulus를 사용하면 느낀 장점

dom자체에 대한 테스트는 우수함(rails 레일 그대로 사용가능) ex) button클릭시 이러한 dom이 되어야함 같은 경우 button클릭시를 가정한 테스트코드는 만들기 쉬움

js코드량이 줄어듬, rails로만 웬만한 모던한 웹개발 가능(필자가 느낀 압도적인 장점)

예를들어 autocomplete select box구현도 js코드 없이 구현이 가능했다. 필자가 만든 rails7-turbo-autocomplete-select-box참고

사용방법

rails generate stimulus search-form

을 실행해주면 app/javascript/controllers/search_form_controller.js

import { Controller } from "@hotwired/stimulus";

// Connects to data-controller="search-form"
export default class extends Controller {
  connect()
}

파일이 만들어짐

app/javascript/controllers/index.js

import SearchFormController from "./search_form_controller";
application.register("search-form", SearchFormController);

위 파일을 register해줌(일일이 register안해도 사용가능한 방법도 있지만 생략)

이 상태에서 connect()안에 console.log를 넣어주고 페이지 리로드하면 해당 console.log가 표시될거임

을 실행해주면 app/javascript/controllers/search_form_controller.js

submit() {
  console.log("submit")
}

을 추가해서

<div data-controller="search-form">
  <button id="1" data-action="click->search-form#submit">search</button>
</div>

이런식으로 기술해주고 버튼을 클릭하면 위에 선언한 submit()이 호출된다. coffie script향기가 나는 문법인데 click->부분이 사용할 이벤트이다. hover->이런식으로도 사용가능함. search-form부분이 register시 등록한 부분 #submit이 해당 controller에서 선언된 함수명이다.

memo

application.register없이 등록하는 방법

import SearchFormController from "./search_form_controller";
application.register("search-form", SearchFormController);

매번 등록하는게 귀찮을때 아래와 같이 설정해주는 방법도 있다.

import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
eagerLoadControllersFrom("controllers", application);

eagerLoadControllersFrom을 사용하면 file명을 보고 controller명을 유추해낸다. 예를들어 app/javascript/controllers/search_form2_controller.js 이면 controller에 search-form2를 지정해주면 attach가 가능

<%= search_form_for @q, url: root_path,
    data: {
      turbo_frame: "search_results_ptRAgvtb",
      controller: "search-form2",
      action: "input->search-form2#submit"
    } do |f| %>

주의할점은 파일명 바꾸줬을때 재기동이 필요하다. 재기동 안하면 인식 안하는듯

controller와 action양쪽에 선언해줘야함

왜 둘다 써줘야되지 싶은데 어느 한쪽이라도 없으면 attach동작 안함.. controller,action양쪽에 컨트롤러명(search-form)을 선언해주자

<%= search_form_for @q, url: root_path,
    data: {
      turbo_frame: "search_results_ptRAgvtb",
      controller: "search-form",
      action: "input->search-form#submit"
    } do |f| %>

stimulus의미는?

생물에서 반응을 유발하는 자극이라는 의미 뜻을 아니까 더 なるほど라고 느낌

스티뮤라스(スティミュラス)라고 읽음

react와 같이 사용?

가능은한데 몇가지 문제점이

문제점1 rails7에는 webpack같은게 디폴트로 안들어가있음

rails7에서는 js번들러툴이 기본적으로 안들어가 있고 의존해결에 importmap을 사용함 빌드 툴이 없으면 React(JSX)코드가 js로 변환되지 않아 동작하지 않는경우가 있음

그래서 따로 esbuild,webpack등을 설치해줄 필요가 있음. 조금 귀찮

문제점2 코드가 지저분해짐

방대한 컴퍼넌트를 가질경우 react의 장점을 죽일 수 있음.

実際のコードを見る前に結論を話すと、SmartHR UI のようなある程度完成されたコンポーネント集を Stimulus 経由で呼び出すのはあまり良い選択肢ではないなと思いました。SmartHR UI には膨大な数のコンポーネントがあるので、それらに対応するコントローラをいちいち作るわけにもいかず、かといって一つのコントローラ内でなんのコンポーネントをレンダリングするかを分岐させるのもやや冗長で、せっかくの React の強みを殺しているような状態になってしまったからです。

data 属性でコンポーネント名を渡し、それを switch で切り替えてレンダリングしています。お世辞にも良いコードとは思いません。コンポーネントが増えれば増えるほど switch と import が伸び、結局 React 内部の制御は何もしていない状態です。

문제점3 라이프사이클 상성이 별로

react의 라이프 사이클과 stimulus의 상성이 안좋다고 함.

React のライフサイクルと Stimulus のライフサイクルの相性があまり良くないというか、どう折り合いをつければ良いのかよくわからなかった点があります。React 自身も、Stimulus 自身もそれぞれイベントハンドリングや状態の保持を行うため、やるべきことが重複してしまい、どこで制御すべきなのかが宙ぶらりんになってしまいました。また、FC などの状態を持たないコンポーネントで、例えば select タグなどを扱う場合、selected 属性がついているかどうかを仮想 DOM 側で React が制御する一方で、Stimulus 側は実際の DOM 構造を見てしまうので、どうにもいかなくなります。自分で作ったコンポーネントであればよいのかなとも思いますが、SmartHR UI のように既にロジックがついた UI パーツを使ってしまうと、このような状態の整合性の不一致により身動きが取れなくなってしまいます。

이 부분은 잘 이해가 안되지만 뭐 그렇다함..

vue도 같이 사용가능?

이것도 가능

rails hotwire stimulus 使うとき、vue も使える?
はい、Rails の Hotwire と Stimulus を使用する場合、Vue.js も併用することができます。Hotwire と Stimulus は Rails のネイティブな JavaScript ライブラリであり、Vue.js は別の JavaScript フレームワークですが、共存させることは可能です。

通常、Hotwire と Stimulus はサーバーサイドレンダリングや Turbo Streams を使用して、リアルタイムでの更新を実現するために使用されます。一方、Vue.js はクライアントサイドでの動的な UI の構築やデータのバインディングなどに特化しています。

Hotwire と Stimulus と Vue.js を組み合わせる場合、以下のようなアプローチを取ることができます。

ページ全体では Hotwire と Stimulus を使用し、サーバーサイドのリアルタイムな更新を処理します。
特定のセクションやコンポーネントでは Vue.js を使用し、クライアントサイドの動的な UI を実装します。
例えば、Hotwire と Stimulus を使用してページ全体のサーバーサイドの更新を処理し、Vue.js を使用して特定のセクション内でデータのバインディングやコンポーネントの再利用を行うことができます。Hotwire と Vue.js を組み合わせる場合は、特に Turbo Streams と Vue.js の相互運用性についていくつかの注意点がありますが、一般的にはうまく組み合わせることができます。

ただし、Hotwire と Stimulus の組み合わせだけでアプリケーションを構築する場合でも、Vue.js を併用する必要はありません。Hotwire と Stimulus は単独でも強力なツールですので、プロジェクトの要件や好みに応じて適切な選択を行ってください。