初心者でもできる！Docker + Rails(API) + Vite(React + TS) 開発環境構築まとめ

## 概要
最近 React + Typescript でアプリを作ったのですが、
Docker + Rails(API) + Vite( React + Typescript )での環境構築の記事があまりなかったので
備忘録と合わせてまとめておきました。

RailsをAPIモード、React+TypeScriptをViteで構築しています。

React触ってみたい！みたいな人におすすめです。

## 使用技術バージョン
| カテゴリ | 技術内容 |
| --- | --- | 
| サーバーサイド | Ruby on Rails 7.2.2.1・Ruby 3.3.0 |
| フロントエンド | React + TypeScript（Vite） |
| データベース |  PostgreSQL |

Rails 7.1以下だとアプリ作成時にエラーが出て修正方法が分からないバグパッチが配信されてしまっているようなので（修正されてるかもしれない）、今回は Ruby 3.3.0 と Rails 7.2.2.1 を使用しています。



## ファイル構成 ─ 【初期準備】
```:ファイル構成
your_app/
├── backend/        ← Rails（APIモード）
    └── Dockerfile
    └── Gemfile
├── frontend/       ← React + TypeScript（Vite）
    └── Dockerfile
├── docker-compose.yml
├── .env（任意）
```

## 作成手順
1.　【初期準備】の全ファイル構成を作成・記述
2. 　フロントエンド初期化 + コード修正
3. 　Docker compose build
4. 　バックエンド初期化 + コード修正
5. 　Docker compose build
6. 　完成！！

 ＊Ruby, RailsをDockerfileに指定したバージョンで作成するには、一度buildしてからRailsを初期化しないとDockerfileの内容を読み取らないため、このような順番になっています。


 ## 初期ファイル内容（折りたたんでます）
 【your_app直下】
<details><summary>docker-compose.yml</summary>

POSTGRES_DB: yourappname_dbのところは
youraappnameをあなたのアプリ名に置き換えてください
```docker-compose.yml
version: '3.9'

services:
  backend:
    build: 
      context: ./backend/
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    volumes:
      - ./backend:/app
    environment:
      - DATABASE_HOST=db
    depends_on:
      - db
    user: root

  frontend:
    build: 
      context: ./frontend/
      dockerfile: Dockerfile
    ports:
      - "5173:5173"
    volumes:
      - ./frontend:/app
      - /app/node_modules
    stdin_open: true
    tty: true

  db:
    image: postgres:15
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: yourappname_db

volumes:
  db_data:
```
</details>

【Backend】
<details><summary>backend/Dockerfile</summary>

```:backend/Dockerfile
FROM ruby:3.3.0

WORKDIR /app

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

COPY Gemfile* ./
RUN bundle install

COPY . .

CMD ["rails", "server", "-b", "0.0.0.0"]
```

</details>

<details>
<summary>backend/Gemfile</summary>

```:backend/Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.2.2'
gem 'pg'
```
</details>
【Frontend】
<details>
<summary>frontend/Dockerfile</summary>

```:frontend/Dockerfile
FROM node:20

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

CMD ["npm", "run", "dev"]
```
</details>

## ここから下は手順通りに書かれてるよ！
## Frontend初期化
```:your_app/frontend: 
cd frontend
npm create vite@latest . -- --template react-ts
npm install
```
> `npm create vite@latest` 実行時、以下のような選択が出ますが、"Ignore files and continue" を選んでOKです。

```
Current directory is not empty. Please choose how to proceed:
│  ○ Cancel operation
│  ○ Remove existing files and continue
│  ● Ignore files and continue
```


### frontend/vite.config.ts 修正
```vite.config.ts
export default defineConfig({
  plugins: [react()],
  server: {            // ←--追加
    host: true,
    port: 5173,
  }
})
```

## Dockerをビルド
　Dockerをビルドしてください
```:your_app
docker compose build
```
## Rails初期化
　Railsアプリをapiモードで作成します
 　gitを使っている人は
  　--skip-gitを入れないとgit addができなくなるのでオプションにつけます
```:your_app

docker-compose run backend rails new . --api --force --database=postgresql --skip-git
```
これで最初のbackend/Dockerfileに書いた
Ruby,Railsのバージョンでバックエンドが作成できていると思います。
無事に大量のファイルが作成されていたらファイルを編集していきます。↓

### backend/Dockerfile修正
本番環境用の設定なってるので開発用に修正する。
backend/Dockerfile
```Dockerfile
# backend/Dockerfile

ARG RUBY_VERSION=3.3.0
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base

# 修正
WORKDIR /app

RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y \
      curl libjemalloc2 libvips postgresql-client \
      git build-essential libpq-dev && \
    rm -rf /var/lib/apt/lists/*

#修正
ENV RAILS_ENV="development" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="production"

FROM base AS build

COPY Gemfile Gemfile.lock ./
RUN bundle install

COPY . .

# なくてもいい
RUN bundle exec bootsnap precompile app/ lib/

FROM base

# 修正（変えなくてもいい）
COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /app /app

RUN groupadd --system --gid 1000 rails && \
    useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER 1000:1000

ENTRYPOINT ["/app/bin/docker-entrypoint"]
EXPOSE 3000
CMD ["./bin/rails", "server", "-b", "0.0.0.0"]
```
### backend/bin/docker-entrypoint修正
Docker再起動時に前回起動時のtmp/pids/server.pid が存在しているとエラーになるため、強制削除するように設定する。

```:backend/bin/docker-entrypoint
#!/bin/bash
set -e

# ↓これ追加（サーバーの再起動対応）
rm -f /app/tmp/pids/server.pid

exec "$@"
```
### backend/config/database.yml を修正
backendのデータベースを設定します
yourappnameをあなたのアプリ名に置き換えてください
```database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: <%= ENV["DATABASE_HOST"] %>
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: yourappname_db  #←--ここ修正

test:
  <<: *default
  database: yourappname_test  #←--ここ修正
```
### backend/config/initializers/cors.rbを修正
CORSは、「このフロントエンドからのリクエストは許可するよ！」とバックエンド側が明示的に許可する仕組みです。
```cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:5173' # React側のURL
    resource '*',
      headers: :any,
      methods: %i[get post put patch delete options head],
      credentials: true
  end
end
```
CORSを使うためにbackend/Gemfileに
gem 'rack-cors'を追加します
```:Gemfile
  gem "bootsnap", require: false

　gem 'rack-cors'　#←----これを追加!!

 group :development, :test do
```



## Docker再構築
backendのDockerfileやdatabase.ymlの変更を適用するために
もう一度Buildし直します。（--no-cacheはなくてもいいよ）
```:your_app
# 上はさっき作ったボリュームも消してる（お好みで）
docker compose down --volumes --remove-orphans
docker compose down

docker compose build --no-cache
```

## 起動/確認
```
 docker compose up
```
バックエンド/フロントエンドのURLにアクセスして表示されていればOK！

http://localhost:3000/


http://localhost:5173/


これで、特にエラーが出ていなければ完成！！

## 終わりに
  今回はDocker+Rails+React+Typescriptの環境構築を解説しました。

  Reactって難しいけどぬるぬる動いて楽しいですよね。
  あと、Viteは Tailwind CSS や TypeScript との統合もスムーズで、設定がシンプルなのがとても有能だなと思います。
  PythonとかTailwindも追加したりしているので、また今度の記事でPythonコンテナの作成とか、tailwindの追加の手順を書いていこうかなと思います。