Rails에서 JavaScript 사용하기

이 가이드는 Rails 애플리케이션에 JavaScript 기능을 통합하는 옵션, 외부 JavaScript 패키지 사용 방법, Turbo와 Rails 사용 방법을 다룹니다.

이 가이드를 읽고 나면 다음을 알 수 있습니다:

  • Node.js, Yarn, JavaScript 번들러 없이도 Rails를 사용하는 방법.
  • import maps, Bun, esbuild, Rollup 또는 Webpack을 사용하여 새로운 Rails 애플리케이션을 만드는 방법.
  • Turbo가 무엇이며 어떻게 사용하는지.
  • Rails에서 제공하는 Turbo HTML 헬퍼를 사용하는 방법.

Import Maps

Import maps를 사용하면 브라우저에서 직접 버전화된 파일에 매핑되는 논리적 이름을 사용하여 JavaScript 모듈을 가져올 수 있습니다. Import maps는 Rails 7의 기본 옵션으로, 트랜스파일링이나 번들링 없이도 대부분의 npm 패키지를 사용하여 현대적인 JavaScript 애플리케이션을 구축할 수 있습니다.

Import maps를 사용하는 애플리케이션에는 Node.js 또는 Yarn이 필요하지 않습니다. Rails와 importmap-rails를 사용하여 JavaScript 종속성을 관리하려면 Node.js나 Yarn을 설치할 필요가 없습니다.

Import maps를 사용할 때는 별도의 빌드 프로세스가 필요하지 않습니다. bin/rails server로 서버를 시작하면 바로 사용할 수 있습니다.

importmap-rails 설치하기

Importmap은 Rails 7 이상의 새로운 애플리케이션에 자동으로 포함되어 있지만, 기존 애플리케이션에도 수동으로 설치할 수 있습니다:

$ bin/bundle add importmap-rails

설치 작업을 실행합니다:

$ bin/rails importmap:install

importmap-rails로 npm 패키지 추가하기

Import map 기반 애플리케이션에 새 패키지를 추가하려면 터미널에서 bin/importmap pin 명령을 실행하세요:

$ bin/importmap pin react react-dom

그런 다음 application.js에서 패키지를 일반적으로 가져옵니다:

import React from "react"
import ReactDOM from "react-dom"

JavaScript 번들러로 npm 패키지 추가하기

Import maps는 새로운 Rails 애플리케이션의 기본 옵션이지만, 전통적인 JavaScript 번들링을 선호한다면 Bun, esbuild, Webpack, Rollup.js 중 하나를 선택하여 새 Rails 애플리케이션을 만들 수 있습니다.

Import maps 대신 번들러를 사용하려면 rails new 명령에 --javascript 또는 -j 옵션을 전달하세요:

$ rails new my_new_app --javascript=bun
또는
$ rails new my_new_app -j bun

이러한 번들링 옵션에는 jsbundling-rails 젬을 통해 간단한 구성과 애셋 파이프라인 통합이 포함되어 있습니다.

번들링 옵션을 사용할 때는 bin/dev를 사용하여 Rails 서버를 시작하고 개발용 JavaScript를 빌드합니다.

JavaScript 런타임 설치하기

esbuild, Rollup.js 또는 Webpack을 사용하여 Rails 애플리케이션의 JavaScript를 번들링하는 경우 Node.js와 Yarn을 설치해야 합니다. Bun을 사용하는 경우에는 Bun만 설치하면 됩니다.

Bun 설치하기

Bun 웹사이트에서 설치 지침을 찾고 다음 명령으로 올바르게 설치되었는지 확인하세요:

$ bun --version

Bun 런타임의 버전이 출력되어야 합니다. 버전이 1.0.0과 같이 표시되면 Bun이 올바르게 설치된 것입니다.

그렇지 않으면 현재 디렉토리에 Bun을 다시 설치하거나 터미널을 다시 시작해야 할 수 있습니다.

Node.js와 Yarn 설치하기

esbuild, Rollup.js 또는 Webpack을 사용하는 경우 Node.js와 Yarn을 설치해야 합니다.

Node.js 웹사이트에서 설치 지침을 찾고 다음 명령으로 올바르게 설치되었는지 확인하세요:

$ node --version

Node.js 런타임의 버전이 출력되어야 합니다. 버전이 8.16.0 이상인지 확인하세요.

Yarn을 설치하려면 Yarn 웹사이트의 설치 지침을 따르세요. 이 명령을 실행하면 Yarn 버전이 출력됩니다:

$ yarn --version

버전이 1.22.0과 같이 표시되면 Yarn이 올바르게 설치된 것입니다.

Import Maps와 JavaScript 번들러 중 선택하기

새 Rails 애플리케이션을 만들 때 import maps와 JavaScript 번들링 솔루션 중 하나를 선택해야 합니다. 각 애플리케이션의 요구사항이 다르므로, 복잡한 애플리케이션에서 한 옵션에서 다른 옵션으로 마이그레이션하는 것이 시간이 많이 걸릴 수 있으므로 요구사항을 신중히 고려해야 합니다.

Rails 팀은 import maps의 복잡성 감소, 개발자 경험 향상, 성능 향상 가능성을 믿기 때문에 import maps를 기본 옵션으로 선택했습니다.

Hotwire 스택을 주로 사용하는 많은 애플리케이션의 경우 장기적으로 import maps가 적합한 옵션이 될 것입니다. Rails 7에서 import maps를 기본으로 선택한 이유에 대해서는 여기에서 자세히 설명하고 있습니다.

그러나 일부 애플리케이션에는 여전히 전통적인 JavaScript 번들러가 필요할 수 있습니다. 번들러를 선택해야 하는 요구사항은 다음과 같습니다:

  • 코드에 JSX 또는 TypeScript와 같은 트랜스파일 단계가 필요한 경우.
  • CSS를 포함하거나 Webpack 로더에 의존하는 JavaScript 라이브러리를 사용해야 하는 경우.
  • 트리 셰이킹이 절대적으로 필요한 경우.
  • cssbundling-rails 젬을 통해 Bootstrap, Bulma, PostCSS 또는 Dart CSS를 설치해야 하는 경우. 이 젬이 제공하는 모든 옵션(Tailwind와 Sass 제외)은 다른 옵션을 지정하지 않으면 자동으로 esbuild를 설치합니다.

Turbo

Import maps를 선택하든 전통적인 번들러를 선택하든, Rails는 애플리케이션 속도를 높이고 작성해야 하는 JavaScript 양을 크게 줄이기 위해 Turbo를 제공합니다.

Turbo를 사용하면 서버가 직접 HTML을 전달할 수 있어, 서버 측 Rails 애플리케이션을 단순한 JSON API로 축소하는 주류 프런트엔드 프레임워크를 사용할 필요가 없습니다.

Turbo Drive

Turbo Drive는 페이지 로드 시 전체 페이지 분해와 재구축을 방지하여 페이지 로드 속도를 높입니다. Turbo Drive는 Turbolinks의 개선 및 대체 버전입니다.

Turbo Frames

Turbo Frames를 사용하면 페이지의 정의된 부분만 업데이트할 수 있어, 나머지 페이지 콘텐츠에는 영향을 미치지 않습니다.

Turbo Frames를 사용하면 사용자 정의 JavaScript 없이도 제자리 편집, 콘텐츠 지연 로드, 서버 렌더링 탭 인터페이스를 구현할 수 있습니다.

Rails는 turbo-rails 젬을 통해 Turbo Frames 사용을 간소화하는 HTML 헬퍼를 제공합니다.

이 젬을 사용하면 turbo_frame_tag 헬퍼를 사용하여 애플리케이션에 Turbo Frame을 추가할 수 있습니다:

<%= turbo_frame_tag dom_id(post) do %>
  <div>
     <%= link_to post.title, post_path(post) %>
  </div>
<% end %>

Turbo Streams

Turbo Streams는 자체 실행 <turbo-stream> 요소로 래핑된 HTML 조각을 전달하여 페이지 변경 사항을 전송합니다. Turbo Streams를 사용하면 WebSocket을 통해 다른 사용자가 만든 변경 사항을 브로드캐스트하고, 폼 제출 후 전체 페이지 로드 없이 페이지의 일부를 업데이트할 수 있습니다.

Rails는 turbo-rails 젬을 통해 Turbo Streams 사용을 간소화하는 HTML 및 서버 측 헬퍼를 제공합니다.

이 젬을 사용하면 컨트롤러 작업에서 Turbo Streams를 렌더링할 수 있습니다:

def create
  @post = Post.new(post_params)

  respond_to do |format|
    if @post.save
      format.turbo_stream
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

Rails는 .turbo_stream.erb 뷰 파일을 자동으로 찾아 해당 뷰를 렌더링합니다.

Turbo Stream 응답은 컨트롤러 작업 내에서 인라인으로 렌더링할 수도 있습니다:

def create
  @post = Post.new(post_params)

  respond_to do |format|
    if @post.save
      format.turbo_stream { render turbo_stream: turbo_stream.prepend('posts', partial: 'post') }
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

마지막으로, Turbo Streams는 내장 헬퍼를 사용하여 모델이나 백그라운드 작업에서 시작할 수 있습니다. 이러한 브로드캐스트는 WebSocket 연결을 통해 모든 사용자에게 업데이트를 전송하여 페이지 콘텐츠를 최신 상태로 유지하고 애플리케이션에 생동감을 불어넣을 수 있습니다.

모델 콜백을 사용하여 Turbo Stream을 브로드캐스트하려면 다음과 같이 하면 됩니다:

class Post < ApplicationRecord
  after_create_commit { broadcast_append_to('posts') }
end

그리고 업데이트를 받아야 하는 페이지에 다음과 같이 WebSocket 연결을 설정합니다:

<%= turbo_stream_from "posts" %>

Rails/UJS 기능의 대체 —————————————-네, 번역을 계속하겠습니다.

Rails/UJS 기능의 대체

Rails 6에는 UJS(Unobtrusive JavaScript)라는 도구가 포함되어 있었습니다. UJS를 사용하면 개발자가 <a> 태그의 HTTP 요청 메서드를 재정의하거나, 작업을 실행하기 전에 확인 대화 상자를 추가할 수 있습니다. UJS는 Rails 7 이전의 기본 도구였지만, 이제는 Turbo를 사용하는 것이 권장됩니다.

메서드

링크를 클릭하면 항상 HTTP GET 요청이 발생합니다. 애플리케이션이 RESTful한 경우, 일부 링크는 서버의 데이터를 변경하는 작업이므로 GET 이외의 요청 메서드를 사용해야 합니다. data-turbo-method 속성을 사용하면 이러한 링크에 명시적인 “post”, “put” 또는 “delete"와 같은 메서드를 표시할 수 있습니다.

Turbo는 애플리케이션의 <a> 태그에서 turbo-method 데이터 속성을 스캔하고, 존재하는 경우 지정된 메서드를 사용하여 기본 GET 작업을 재정의합니다.

예를 들어:

<%= link_to "Delete post", post_path(post), data: { turbo_method: "delete" } %>

이렇게 생성됩니다:

<a data-turbo-method="delete" href="...">Delete post</a>

링크의 메서드를 data-turbo-method로 변경하는 대신 Rails button_to 헬퍼를 사용할 수 있습니다. 접근성 측면에서 GET 이외의 작업에는 실제 버튼과 폼이 더 적합합니다.

확인

data-turbo-confirm 속성을 링크와 폼에 추가하면 사용자에게 추가 확인을 요청할 수 있습니다. 링크를 클릭하거나 폼을 제출할 때 속성의 텍스트가 포함된 JavaScript confirm() 대화 상자가 표시됩니다. 사용자가 취소를 선택하면 작업이 실행되지 않습니다.

예를 들어 link_to 헬퍼를 사용하는 경우:

<%= link_to "Delete post", post_path(post), data: { turbo_method: "delete", turbo_confirm: "Are you sure?" } %>

이렇게 생성됩니다:

<a href="..." data-turbo-confirm="Are you sure?" data-turbo-method="delete">Delete post</a>

사용자가 "Delete post” 링크를 클릭하면 “Are you sure?” 확인 대화 상자가 표시됩니다.

이 속성은 button_to 헬퍼에서도 사용할 수 있지만, button_to가 내부적으로 렌더링하는 폼에 추가해야 합니다:

<%= button_to "Delete post", post, method: :delete, form: { data: { turbo_confirm: "Are you sure?" } } %>

Ajax 요청

JavaScript에서 GET 이외의 요청을 할 때는 X-CSRF-Token 헤더가 필요합니다. 이 헤더가 없으면 Rails에서 요청이 허용되지 않습니다.

참고: 이 토큰은 Rails에서 Cross-Site Request Forgery(CSRF) 공격을 방지하기 위해 필요합니다. 보안 가이드에서 자세히 알아볼 수 있습니다.

Rails Request.JS는 Rails에서 요구하는 요청 헤더를 추가하는 로직을 캡슐화합니다. FetchRequest 클래스를 패키지에서 가져와 인스턴스화하고 요청 메서드, URL, 옵션을 전달한 다음 await request.perform()을 호출하면 됩니다.

예:

import { FetchRequest } from '@rails/request.js'

....

async myMethod () {
  const request = new FetchRequest('post', 'localhost:3000/posts', {
    body: JSON.stringify({ name: 'Request.JS' })
  })
  const response = await request.perform()
  if (response.ok) {
    const body = await response.text
  }
}

다른 라이브러리를 사용하여 Ajax 호출을 하는 경우, 보안 토큰을 직접 기본 헤더로 추가해야 합니다. 토큰을 가져오려면 애플리케이션 뷰에 인쇄된 <meta name='csrf-token' content='THE-TOKEN'> 태그를 확인하세요. 다음과 같이 할 수 있습니다:

document.head.querySelector("meta[name=csrf-token]")?.content