Junhee Park

Rails는 app/javascript를 어떻게 처리할까?

Rails에서는 Asset Pipeline이라는 기능을 통해 정적 애셋 파일을 처리한다. Rails Asset Pipeline(Propshaft)은 기본적으로 app/assets 디렉토리 하위 디렉토리(e.g. app/assets/images, app/assets/javascripts, app/assets/stylesheets)의 파일을 public/assets로 옮겨주는 기능을 제공한다.

Rails에서 js 파일을 사용하기 위해서는 가장 단순하게는 app/assest/javascripts에 js 파일을 작성하는 방법이 있다. 하지만 이 방법을 사용하는 경우 필요한 js 파일을 직접 로드해줘야 하고, 외부 의존성을 사용하는 경우에는 관리하기 어려워지는 단점이 있다. 이러한 문제를 해결하기 위해 Rails에서는 js 파일을 사용하는 두 가지 방법을 제안하는데 첫 번째는 importmap이고 다른 하나는 jsbundling-rails를 사용하는 방법이다.

이 두 가지 방법을 사용해 Rails 프로젝트를 세팅해보면 javascript 파일을 app/assets/javascripts 디렉토리가 아닌 app/javascript 디렉토리 하위에 작성하게 되는 것을 알 수 있다. 위에서 설명했듯이 Asset pipeline은 app/assets 디렉토리 하위 디렉토리의 파일만 처리한다고 했는데, 그렇다면 importmap 또는 jsbundling-rails을 사용하는 경우 어떻게 app/javascript의 파일을 처리하는 걸까?

Asset Pipeline에서는 Rails.application.config.assets.paths을 통해 app/assets 이외의 원하는 경로를 Asset Pipeline의 경로로 추가할 수 있도록 하고 있다. importmap에서는 바로 이 방법을 사용하는데, importmap의 소스 코드를 보면 알 수 있다. 코드를 보면 Rails 초기화 과정에서 app.config.assets.pathsapp/javascriptvendor/javascript 디렉토리를 추가하고 있다. 이로 인해 importmap을 사용하는 경우 app/javascript 디렉토리 하위의 파일도 Asset Pipeline에 의해 처리될 수 있게 된다.

initializer "importmap.assets" do |app|
  if app.config.respond_to?(:assets)
    app.config.assets.paths << Rails.root.join("app/javascript")
    app.config.assets.paths << Rails.root.join("vendor/javascript")
  end
end

반면 jsbundling-rails는 다른 접근을 사용한다. jsbundling-rails는 importmap처럼 app/javascript 디렉토리를 처리하는 기능을 직접 제공하지 않으며, 사용자가 직접 번들링 output의 위치를 app/assets/builds로 설정하도록 안내한다. 예를 들어 bin/rails javascript:install:esbuild로 esbuild를 설정해보면 build 스크립트가 아래와 같이 설정되어 번들링 후 결과물이 app/assets/builds에 들어가는 것을 볼 수 있다. 해당 위치는 app/assets 하위에 존재하므로 자연스럽게 Asset Pipeline에 의해 처리된다.

{
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets"
  }
}