16
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rails+Reactアプリを1つのdynoでデプロイする

Posted at

railsAPIモードとReactで記事投稿アプリを作りました。API側とクライアント側を別々にHerokuにデプロイすると、SPAとは思えないほどアプリの動きが重くなったので、1つのdynoでデプロイし直した時のことをメモします。

##参考になった記事
A Rock Solid, Modern Web Stack—Rails 5 API + ActiveAdmin + Create React App on Heroku | Heroku
ReactJS + Ruby on Rails API + Heroku App - Medium
React + RailsのアプリをHerokuで動かす方法 - Qiita

##はじめに
###ディレクトリ構成

project
      ┝ app
      ┝ bin
      ┝ config
      ┝ db
      ┝ front <--reactのディレクトリ
    ┝ lib
      ┝ log
      ┝ public
      ┝ storage
      ┝ test
      ┝ tmp
      ┗ vendor

アプリ名をprojectとしています
ディレクトリ構成はrailsアプリの中にreactアプリがあるという状態です
アプリのルートディレクトリにGemfileがないと後々buildpackを用いてデプロイする時にエラーが出るのでこのような構成にしました
参考:Heroku:Buildpackエラーでハマった

##.envファイル生成
開発環境でのAPI側のURLを.envファイルに書きます
frontディレクトリ下で.envファイルを作成し、以下を記述します

project/front/.env
REACT_APP_SERVER_URL=http://localhost:3001

URLを取得する時はprocess.env.REACT_APP_SERVER_URLで取得できます
axiosでHTTP通信する場合は以下のように書けます

project/front/Component/UserIndex.js
axios
      .get(`${process.env.REACT_APP_SERVER_URL}/api/users`, headers)

##Foremanの導入
Foremanは複数のプロセスをまとめて管理できるツールです
APIとクライアント側を一つのコマンドで動かせるので便利です
Gemfileに以下を記述し、bundle install --path vendor/bundleします

project/Gemfile
gem 'foreman'

次にProcfile.devを作成し、以下を記述します

project/Procfile.dev
frontend: exec /usr/bin/env PORT=3000 sh -c 'cd front && yarn start'
backend: exec /usr/bin/env PORT=3001 sh -c 'bundle exec rails s'

ここでforeman start -f Procfile.devを実行し、http://localhost:3000にアクセスすると確認できます

ただ、lib/tasks下にstart.rakeファイルを作成し、以下を記述すると、rake startとコマンドに打つだけでブラウザで確認できます

project/lib/tasks/start.rake
namespace :start do
    desc 'Start dev server'
    task :development do
      exec 'foreman start -f Procfile.dev'
    end
    
    desc 'Start production server'
    task :production do
      exec 'NPM_CONFIG_PRODUCTION=true yarn heroku-postbuild && foreman start'
    end  
end
task :start => 'start:development'

また、rake start:productionコマンドでデプロイ前にローカルで簡単に本番環境のテストを行うことができるように記述しています

##Herokuにデプロイ
###package.json作成

まず、projectディレクトリ下にpackage.jsonファイルをnpm initコマンドで作成します

project/package.json
{
  "name": "project",
  "version": "1.0.0",
  "description": "You can post articles using this application.",
  "main": "index.js",
  "scripts": {
    "build": "cd front && npm install && npm run build && cd ..",
    "deploy": "cp -a front/build/. public/",
    "heroku-postbuild": "npm run build && npm run deploy && echo 'Client built!'"
  }
}

Herokuはheroku-postbuildに定義されていることを実行します。ここでは、frontディレクトリでnpm install, npm run buildを実行し、front/buildの内容をpublic/にコピーします

###Herokuアプリ作成
heroku create アプリ名でHerokuアプリを生成します
そして、Herokuのbuildpackを追加します

$ heroku buildpacks:add heroku/nodejs --index 1
$ heroku buildpacks:add heroku/ruby --index 2

heroku buildpacksで確認して、以下のような順番になっていればOKです

=== project Buildpack URLs
1. heroku/nodejs
2. heroku/ruby

###Procfile作成

次にProcfileを作成し、Herokuがrailsアプリを起動するためのコマンドを記述します

project/Procfile
web: bundle exec rails s

Procfileを作成したため、ここでrake start:productionコマンドによりローカルで本番環境のテストを行うことができます

###本番環境用のエンドポイント設定

Reactアプリが本番環境でのAPI側のURLを参照できるように.env.productionファイルを作成します

project/front/.env.production
REACT_APP_SERVER_URL=https://project.herokuapp.com

また、クライアント側からのアクセスを許可するため、cors.rbのoriginに追記します

project/config/initializers/cors.rb
origins 'http://localhost:3000', 'https://project.herokuapp.com/api'

本番環境では、APIはhttps://project.herokuapp.com/api/で、クライアント側はhttps://project.herokuapp.com/で起動するようになっています

###.gitignoreに追記

次に.gitignoreに以下を追記します

project/.gitignore
/public
/vendor/bundle

###Postgre.SQLを使用するための設定
本番環境のDBにPostgre.SQLを使用するための設定を行います
Gemfileに以下を追記し、bundle install --without productionを実行します

project/Gemfile
group :production do
  gem 'pg', '>= 0.18', '< 2.0'
end

次にdatabase.ymlを以下のように書き換えます

project/config/database.yml
production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  database: project_production
  username: project
  password: <%= ENV['PROJECT_DATABASE_PASSWORD'] %>

###デプロイ
これでデプロイする準備が整いました
Herokuにpushしてください

git add .
git commit -m 'ready for first push to heroku'
git push heroku master

heroku run rails db:migrateして終了です

##React Routerを使っている場合
React Routerを機能させるために以下の設定をします
###fallback_index_htmlメソッド定義
ApplicationControllerにfallback_index_htmlメソッドを追加します

project/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  def fallback_index_html
    render :file => 'public/index.html'
  end
end

ApplicationControllerがActionController::APIを継承している場合はhtmlファイルを返すことができないため、以下のように追記します

project/app/controllers/application_controller.rb
class ApplicationController < ActionController::API
    include ActionController::MimeResponds
    def fallback_index_html
        respond_to do |format|
            format.html { render body: Rails.root.join('public/index.html').read }
        end
    end
end

###route.rbでfallback_index_html
そして、routes.rbにも以下を追記します
railsのルーティングについて書いている部分より下に追記してください
ここでfallback_index_htmlメソッドによりpublic/index.htmlが返されるため、React Routerを参照することができます

project/config/routes.rb
Rails.application.routes.draw do
  get '*path', to: "application#fallback_index_html", constraints: ->(request) do
    !request.xhr? && request.format.html?
  end
end

これでpushすると、React Routerによるルーティングが反映されると思います
Herokuにデプロイするまでの手順は以上です

##最後に

参考にしたサイトを見ながらでも結構エラーで詰まるところがあり、デプロイするまでに時間がかかりました。しかし、APIとクライアントを別々でデプロイするよりもアプリの動きは軽くなったのでよかったです。
この記事について間違っているとこなどあればご指摘いただけると嬉しいです。

16
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?