Rails 초학자가 SetInterval과 Turbolinks 사이에 어려움을 겪었습니다.

줄거리



간단한 채팅 앱을 만드는 중.
특정 페이지에 setInterval과 Ajax를 사용한 메시지의 자동 업데이트를 구현하려고했는데,
어플의 모든 페이지에 setInterval이 작동해 버려, 특정 페이지 이외로 무한의 에러 메세지가 발생해 괴로워하는 것에....



버전



Programming 경력: 65일째
Ruby: 2.3.1
Rails: 5.0.1

원인



조사해 보면,
Rails에 디폴트로 탑재되어 있는 Turbolinks라는 기능 때문에,
한 번 로드된 자바스크립트 프로세스가 다른 페이지에서도 계속 살아 버리는 것이 원인이었습니다.

어떤 페이지에서 setInterval하면 어느 페이지에 가도 멈추지 않는다.
이것은 POST나 PUT을 하고 js의 재로드가 행해질 때까지 페이지 천이해도 js의 프로세스가 계속 살기 때문입니다. (인용: "Turbolinks와 잘 어울리는 10가지 방법" )

문제를 해결하려면 Turbolinks를 어떻게해야 할 것 같습니다.

Turbokinks란?



어쩌면 웹 앱에서 페이지를 이동할 때 신규 페이지 전체를 다시 로드하는 것이 아니라 필요한 부분만 ajax를 사용하여 다시 작성해 주는 것. 이것에 의해 페이지 이동을 고속화하고 있다.
현재의 Rails에 디폴트로 붙어 있는 기능인 것 같네요.

상세한 해설은, 이하의 기사・사이트를 알기 쉽게 추천입니다.
· KRAY 「Rails 4의 turbolinks에 대해 최소한도 알아두고 싶은 일」
→그림을 알기 쉽고, ajax로의 재기록의 이미지를 잡을 수 있습니다.
· TECHSCORE 「6. Turbolinks」
→해설이 정중하고 알기 쉽습니다.
· Qiita 「Turbolinks씨와 잘 사귀는 10가지 방법」
→ Turbolinks에서의 기본적인 주의점이 정리되어 있습니다.
· seri::diary "지금 더 Turbolinks를 처음으로 일로 사용해 보았기 때문에 여러가지 조사해 보았다"
→ 상기 기사의 필자 seri_k 님의 블로그. Turbolinks 이외의 기사도 매우 도움이됩니다.

해결책



① Turbolinks를 OFF로 하고, 특정 페이지에 개별적으로 JS를 맞춘다



최선인지 확실하지 않지만 이것이 가장 빠릅니다.
우선 Turbolinks를 OFF로 해, 그 위에 특정의 페이지에만 적용시키고 싶은 JS를 개별적으로 맞춥니다.

절차 1. Turbolinks gem 제거



Gemfile
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks' #削除する

삭제 후 bundle install 를 잊지 마세요.

terminal
$ bundle install

2단계. application.js 편집(Turbolinks 삭제)



application.js
//= require jquery
//= require jquery_ujs
//= require turbolinks #削除する
//= require_tree .

3단계. application.html.erb에서 turbolinks 제거



Before

application.html.haml
= javascript_include_tag 'application', { turbolinks: { track: true } }

After

application.html.haml
= javascript_include_tag 'application'

덧붙여서 haml로 기술하고 있습니다.

여기까지 1~3의 순서로 Turbolinks를 OFF로 할 수 있었습니다.
참조 : Rails 4에서 turbolinks를 끄는 방법

4단계. application.js 편집(stub로 작성)



application.js
//= require jquery
//= require jquery_ujs
//= require_tree .
//= stub reload #reload.jsは特定のページのみに適用させたい

"require_tree."는 application.js 이하의 디렉토리에 존재하는 모든 js 파일을 읽습니다.
특정 페이지에만 적용하려는 js 파일은 스텁을 사용하여 제외합니다.

절차 5. JS를 적용할 view 파일에 javascript_include_tag를 작성한다.



messages/index.html.haml
= javascript_include_tag 'reload'
// JSを適用させたいviewファイルにjavascript_include_tagを記述する。
.chat
  .chat-side
    = render "partials/user-info"
    = render "partials/chat-groups"

  .chat-main
    = render "chat-main"

    .chat-body
      %ul.chat-messages
        = render @messages

    .chat-footer
      = render "chat-footer"

6단계. config/assets.rb에 추가



config/assets.rb
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
Rails.application.config.assets.precompile += %w( reload.js )  #追記する

이제 특정 페이지(messages/index)에만 특정 JS(reload.js)가 닿게 되었습니다.

②「Turbolinks씨와 잘 사귀는 10가지 방법」의「3. setInterval은 신중하게 사용」을 실행


$(document).on 'page:change' ->
  if repeatable_condition # setIntarvalをセットする条件。特定のDOMがあるとか
    if window.set_timer_on == null 
      window.timer = setInterval('something_method()',3000)
  else if window.set_timer_on? # setIntarvalを動かしたくないページに来てtimerが動いる場合    
    clearInterval(window.timer)
    window.timer = null

기사에서는, 상기와 같은 코드로 Turbolinks와 Setinterval의 병용을 할 수 있다는 것이었습니다만,
시행 착오로 보았지만 잘 작동하지 않습니다. Setinterval이 모든 페이지에서 작동했습니다.
clearInterval의 거동이나 JS에 있어서의 글로벌 변수 등 공부할 필요가 있을 것 같습니다.
다음은 시행 착오 후 시도한 코드입니다 (잘 작동하지 않습니다.)

reload.js
function auto_reload(){
(中略)
}

(中略)
$(document).on("turbolinks:load",function() {
  if (document.getElementsByClassName("chat").length == 1) {
  // chatクラスが存在する場合、以下を行う。
    var id = setInterval(auto_reload, 1000);
  } else if (document.getElementsByClassName("chat").length == 0) {
  // chatクラスが存在しない場合、以下を行う。  
  clearInterval(id);
  }
});

참고 링크



· KRAY 「Rails 4의 turbolinks에 대해 최소한도 알아두고 싶은 것」
· TECHSCORE 「6. Turbolinks」
· Qiita 「Turbolinks씨와 잘 사귀는 10가지 방법」
· seri::diary "지금 떠나면서 Turbolinks를 처음으로 일로 사용해 보았기 때문에 여러가지 조사해 보았다"

좋은 웹페이지 즐겨찾기