11
6

【個人開発】Rails API × React アプリの環境を構築しました

Last updated at Posted at 2024-06-03

はじめに

こんにちは!MaTTaと申します。プログラミングスクールRunteq50期生です。先日、生成AIを用いた習慣化支援RPGアプリ「3日目に魔王がいる」をMVPリリースしました。その技術要素を細かく切り出して順に備忘録として残していこうと思います。

今回は最初の行程である環境構築についてです。Tailswind CSSの導入もおまけで実施します。

参考

アプリ紹介記事

サービスURL

Githubリポジトリ

本編ここから

作業環境

  • MacBook Air 2020 (Apple M1)
  • macOS Sonoma 14.4.1

構成

このアプリでは、バックエンドにRails(APIモード)、フロントエンドにReactを採用しており、それぞれ個別に開発環境を構築しています。つまりアプリが二つあるような状態です。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313138323338302f37663633383033622d643234332d383134622d306336392d3164663761376139636165662e706e67.png

一方で、開発の管理は楽したいので、リポジトリとしては一つの統合アプリとして管理しています。

ディレクトリ構成
app
├backend/
│
├frontend/
│
└docker-compose.yml

# これらをまとめてリポジトリとする

バックエンド側の環境構築

Rails APIのDocker開発環境を構築します。リポジトリのルートディレクトリから操作を開始します。ファイル操作はGUIで行っても問題ありません。

touch docker-compose.yml

touch docker-compose.ymlに下記のように記述します。後にフロントエンド部分も追記するのですが、先に書くとエラーを起こすのでひとまずバックエンドのみです。なお、データベースはMySQLを用いています。

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
  back:
    build:
      context: ./backend
      dockerfile: Dockerfile
    env_file:
      - ./backend/.env.local
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
    volumes:
      - ./backend:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
    tty: true
    stdin_open: true
volumes:
  mysql_data:

補足
項目 説明
db:volumes: コンテナ内のデータを永続化するためのボリューム。mysql_dataというボリュームが/var/lib/mysqlにマウントされます。
build: アプリケーションのビルド設定を指定します。
- context: ./backend: ビルドコンテキスト(Dockerfileの場所)を指定します。
env_file: 環境変数をファイルから読み込むことを指定します。ここでは、./backend/.env.localファイルが使用されます。
command: コンテナ起動時に実行されるコマンドを指定します。ここでは、server.pidファイルを削除し、Railsサーバーを起動します。Railsは server.pidファイルを使用してサーバープロセスのPIDを保存します。古いPIDファイルが存在すると、サーバーの起動に問題が発生する可能性があるため、起動前にこのファイルを削除しています。
db:volumes: ./backendディレクトリがコンテナ内の/appディレクトリにマウントされます。
depends_on: サービス間の依存関係を定義します。backサービスはdbサービスに依存しているため、dbサービスが起動してからbackサービスが起動します。
tty: truestdin_open: true これらのオプションは、コンテナとのインタラクティブなセッションを可能にします。
volumes: 最後のvolumes:セクションでは、名前付きボリューム(Docker によって管理される永続的なデータ保存領域)を定義します。ここでは、mysql_dataというボリュームが定義されています。

バックエンドのワークスペースを作成し移動。必要な空ファイルを作成する。

mkdir backend
cd backend
touch Dockerfile Gemfile Gemfile.lock entrypoint.sh .env.local

Dockerfileに下記の通り記述します。

注意
一行目の--platform=linux/amd64について、M1以降のARMアーキテクチャを採用しているMacの場合に必要です。それ以外の開発環境の場合は削除してください。

Dockerfile
FROM --platform=linux/amd64 ruby:3.2.2
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client

WORKDIR /app

COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock

RUN gem install bundler
RUN bundle install

COPY . /app

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

EXPOSE 3002

CMD ["rails", "server", "-b", "0.0.0.0"]
補足
Dockerfile命令 説明
FROM --platform=linux/amd64 ruby:3.2.2 このイメージは、Ruby 3.2.2を使用します。--platform=linux/amd64は、プラットフォームを指定し、Dockerイメージが特定のアーキテクチャ(この場合はAMD64)で構築されることを保証します。
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client apt-get updateを実行して、パッケージリストを更新します。apt-get installを使用して、NodeJSとMySQLクライアントをインストールします。これらのパッケージは、Railsアプリケーションの実行に必要です。
WORKDIR /app コンテナ内の作業ディレクトリを/appに設定します。
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
GemfileGemfile.lockをコンテナ内の/appディレクトリにコピーします。これらのファイルは、アプリケーションの依存関係を定義します。
RUN gem install bundler
RUN bundle install
bundlerをインストールします。bundle installを実行して、GemfileとGemfile.lockに基づいて依存関係をインストールします。
COPY . /app カレントディレクトリ(.)の内容全体をコンテナ内の/appディレクトリにコピーします。これにより、アプリケーションのソースコードがコンテナ内に配置されます。
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
entrypoint.shファイルを/usr/bin/ディレクトリにコピーします。chmod +xを使用して、entrypoint.shファイルに実行権限を付与します。ENTRYPOINT命令を使用して、コンテナの起動時にentrypoint.shスクリプトが実行されるように設定します。
CMD ["rails", "server", "-b", "0.0.0.0"] CMD命令を使用して、コンテナの起動時に実行されるデフォルトのコマンドを指定します。この場合、rails server -b 0.0.0.0が実行され、Railsサーバーがすべてのネットワークインターフェイス(0.0.0.0)で起動します。

続いてentrypoint.shに下記のように記述します。entrypoint.shは、Dockerコンテナが起動する際に実行されるシェルスクリプトです。このスクリプトは、コンテナの起動プロセスをカスタマイズするために使用されます。

entrypoint.sh
#!/bin/bash
set -e

rm -f /app/tmp/pids/server.pid

exec "$@"
補足
スクリプトの行 説明
#!/bin/bash スクリプトがBashシェルを使用して実行されることを指定します。
set -e スクリプト内のコマンドがエラーを返した場合、スクリプトの実行を即座に終了するように指示します。
rm -f /app/tmp/pids/server.pid 古いRailsサーバーのPIDファイルを削除します。
exec "$@" スクリプトに渡された引数を使用して新しいプロセスを実行し、現在のシェルプロセスを置き換えます。これにより、コンテナのメインプロセスがスクリプトから実行されたプロセスになります。

さらにGemfileにRailsのインストールについて記述します。

source 'https://rubygems.org'
gem 'rails', '~> 7.1.2'

Gemfile.lock, .env.localファイルは空のままで構いません。

Railsアプリを作成する

ターミナルで下記の通り実行します。

docker-compose run back rails new . --force --database=mysql --skip-docker --api
補足 - --skip-docker: rails new時にDockerfileが自動作成されるのですが今回は不要なのでskip - --api : RailsをAPIモードで立ち上げます

Railsアプリのファイルがインストールされたらconfig/database.ymlを一部修正します

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  # 下記2 行を修正
  password: password
  host: db

準備が整ったのでコンテナを立ち上げます。

docker compose build
docker compose up

データベースの作成

docker compose exec back bash
rails db:create 

アプリが起動できたらhttp://localhost:3000/にアクセスして確認します

スクリーンショット 2024-06-03 22.01.49.png

.gitフォルダの削除

rails new時に.gitフォルダがbackend内に自動作成されるのですが、親ディレクトリでバック・フロント一括git管理しようとしている今回のような場合、backendにも.gitがあるとサブモジュールという扱いになり扱いが面倒になります。backendフォルダ内の.gitフォルダを削除しましょう。

rm -rf backend/.git

その他重要なファイルの格納

.env.local.gitignoreはbackendフォルダ内に移す(ない場合は作成)。
.gitignoreには.env.localの記述を忘れないようにする。

フロントエンド側の環境構築

ReactのDocker開発環境を構築します。create-react-appでfrontendディレクトリとReact初期ファイル類を揃えることから開始します。

# コンテナのルートで操作します。 backendにいるときは cd ../
npx create-react-app frontend
補足

ここで、npxは、npm(Node Package Manager)によって提供されるコマンドランナーで、ローカルもしくはリモートのnpmパッケージを実行するために使用されます。create-react-appは、Reactアプリケーションのプロジェクトを素早くセットアップするためのツールです。

次に、ワークスペースに移動して必要な空ファイルを作成します。

cd frontend
touch Dockerfile yarn.lock

frontend/Dockerfileに下記のように記述します。

frontend/Dockerfile
FROM --platform=linux/amd64 node:21.5.0
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
RUN yarn global add serve
EXPOSE 3000
CMD sh -c 'serve -s build -l tcp://0.0.0.0:$PORT'
それぞれの意味
コマンド 説明
FROM --platform=linux/amd64 node:21.5.0 ベースイメージとしてNode.jsのバージョン21.5.0を使用し、amd64プラットフォームで動作するように指定します。
WORKDIR /app コンテナ内の作業ディレクトリを/appに設定します。
COPY package.json yarn.lock ./ package.jsonyarn.lockをコンテナの作業ディレクトリにコピーします。
RUN yarn install 必要なパッケージをインストールします。
COPY . . 現在のディレクトリのすべてのファイルをコンテナの作業ディレクトリにコピーします。
RUN yarn build アプリケーションをビルドします。
RUN yarn global add serve serveパッケージをグローバルにインストールします。
EXPOSE 3000 コンテナがリッスンするポート3000を公開します。
CMD sh -c 'serve -s build -l tcp://0.0.0.0:$PORT' serveコマンドを使用してビルドされたアプリケーションを起動し、指定されたポートでリッスンします。

ルートディレクトリのdocker-compose.ymlにフロントエンド分を追記します

docker-compose.yml
version: "3"
services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
  back:
    build:
      context: ./backend
      dockerfile: Dockerfile
    env_file:
      - ./backend/.env.local
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
    volumes:
      - ./backend:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
    tty: true
    stdin_open: true
  # ここから追記
  front:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    volumes:
      - ./frontend:/app
    command: yarn start
    ports:
      - "8000:3000"
  # ここまで追記
volumes:
  mysql_data:

コンテナを立ち上げます

docker-compose up --build

アプリが起動できたらlocalhost:8000にアクセスして動作確認します。

スクリーンショット 2024-06-03 22.06.43.png

その他重要なファイルの格納

こちらも.env.local.gitignoreはfrontendフォルダ内に移す(ない場合は作成)。
.gitignoreには.env.localの記述を忘れないようにしましょう。

CORS対応

Rails APIとReactの組み合わせだとCORSによるエラーが発生するのであらかじめその対応をとっておきます。

CORSとは? CORS(Cross-Origin Resource Sharing)は、ウェブブラウザが異なるオリジン間でリソースを共有する際のセキュリティメカニズムです。デフォルトでは、ブラウザはセキュリティ上の理由から、スクリプトが異なるオリジンのリソースにアクセスすることを禁止しています。CORSは、サーバーがブラウザに対して特定のオリジンからのリクエストを許可するための仕組みです。

Rails APIとReactのフロントエンドが異なるオリジンにホストされている場合、CORSの設定が必要です。例えば、Rails APIがhttp://api.example.comにあり、Reactアプリがhttp://frontend.example.comにある場合、ReactアプリからRails APIにリクエストを送ると、CORSエラーが発生する可能性があります。

Gemfileに下記記述します。

Gemfile
gem "rack-cors"
# docker compose exec back bash
bunsle install

Railsのconfig/initializers/cors.rbに下記のとおり記述することでリクエスト元を明示的に許可するようにします。

config/initializers/cors.rb

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "localhost:8000", "https://www.本番環境のURLがあるならここにも追加"

    resource "*",
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

(おまけ)Tailswind CSSとDaisyUIを使えるようにする

React側でデザインのハンドリング性を高めるためにTailwindとDaisyUIは自分的には欠かせない存在なので、環境構築の段階でインストールしてしまいます。

公式

フロントのコンテナに入りインストールします。

docker compose exec front bash
yarn add tailwindcss@^3
yarn add postcss autoprefixer -D
npx tailwindcss init
yarn add -D daisyui@latest

frontend/src/index.cssにTailwindを記述します。先頭に追記する形で構いません。

frontend/src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

tailwind.config.js ファイルを開き、content セクションを編集して、Tailwindがスタイルを適用するファイルを指定します。また、DaisyUIプラグインを有効にします。

tailwind.config.js
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}", // すべてのJS、JSX、TS、TSXファイルをスキャン
  ],
  future: {
  },
  purge: ["./src/**/*.{js,jsx,ts,tsx}", "./public/index.html"],
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [require("daisyui")],
  daisyui: {
    themes: ["light", "dark", "cupcake", "aqua", "pastel"],
  },
};

最後のほうにある下記はDaisyUIのテンプレートテーマです。任意ですが使えるようにしておくと非常に便利です。

daisyui: {
    themes: ["light", "dark", "cupcake", "aqua", "pastel"],
  },

DaisyUIのテーマ

frontend直下にpostcss.config.jsを作成し、以下の内容を追加します。

postcss.config.js
module.exports = {
    plugins: [require("tailwindcss"), require("autoprefixer")],
};
PostCSS CSSを解析し、プラグインを通じて変換するツールです。多くの機能を持つプラグインを利用することで、CSSの構文チェックや最適化、新しいCSS仕様のサポートなどが行えます。

pluginsプロパティ
pluginsプロパティは、PostCSSが使用するプラグインを指定します。この例では、tailwindcssとautoprefixerの2つのプラグインを使用しています。

require("tailwindcss")
Tailwind CSSは、ユーティリティファーストのCSSフレームワークです。これにより、簡単にレスポンシブデザインやカスタマイズが可能なCSSクラスを使うことができます。require("tailwindcss")は、Tailwind CSSのプラグインを読み込み、PostCSSで使用できるようにします。

require("autoprefixer")
Autoprefixerは、ベンダープレフィックスを自動的に追加するPostCSSのプラグインです。これにより、異なるブラウザ間でのCSSの互換性を確保できます。require("autoprefixer")は、Autoprefixerのプラグインを読み込み、PostCSSで使用できるようにします。

dockerを再ビルドすればTailwind CSS, DaisyUIが使えるようになります。

docker compose build
docker compose up

環境構築は以上です。

締めくくり

Rails APIとReactで構成するDocker環境構築の例でした。個人的にはこの構成で2,3個アプリを作っているので安定感はあるのですが、もっとこうした方がいいといったことがあればぜひコメントでご指摘ください。

今回はここまでです。引き続き、MVPアプリの技術要素を切り出してアウトプットしていきたいと思います。

この記事を書いた人

友達募集中ですw

11
6
0

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
11
6