はじめに
業務や本番運用を見据えた、モノレポ構成のフロントエンド + バックエンド + DB環境 を Docker で構築します。
-
フロントエンド: React + TypeScript + Vite
-
バックエンド: Express + TypeScript
-
データベース: PostgreSQL
-
開発用はホットリロードあり
-
本番用は軽量化
を実現します。
モノレポ構成とは何か?
モノレポ(Monorepo)とは、複数のプロジェクトやサービスを1つのリポジトリで管理する構成のことです。
特徴
- 1つのリポジトリで複数サービスを管理
例:フロントエンド、バックエンド、ライブラリ、DBスキーマなど - 依存関係を明示的に管理
共通ライブラリや型定義を容易に共有可能 - ビルドやデプロイの一元化が可能
1つのCI/CDパイプラインで複数サービスを扱える
プロジェクトの準備
下記をpowerShellで実行する。
- DockerFile, docker-compose.ymlなどの設定を追加する部分も含むため、結構長い。
# =====================================
# プロジェクトルート作成
# =====================================
mkdir my-app
cd my-app
# =====================================
# backend ディレクトリ作成・初期化
# =====================================
mkdir backend
cd backend
# npm 初期化
npm init -y # package.json 作成
# 依存パッケージインストール
npm install express # 本番依存
npm install -D typescript ts-node nodemon @types/node @types/express # 開発用依存
# TypeScript 初期化
npx tsc --init # tsconfig.json 作成
# src ディレクトリ作成
mkdir src
# index.ts 作成(Express サーバーの最小構成)
Set-Content -Path src\index.ts -Value @'
import express from "express";
const app = express();
const port = 4000;
app.get("/", (_req, res) => res.send("Hello from Backend!"));
app.listen(port, () => console.log(`Backend running on port ${port}`));
'@
# Dockerfile.dev 作成(開発用)
Set-Content -Path Dockerfile.dev -Value @'
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
'@
# Dockerfile.prod 作成(本番用)
Set-Content -Path Dockerfile.prod -Value @'
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --only=production
CMD ["npm", "run", "start"]
'@
cd ..
# =====================================
# frontend ディレクトリ作成・初期化(完全非対話式)
# =====================================
# Vite + React + TypeScript を作成
npx create-vite@latest frontend -- --template react-ts
cd frontend
npm install
# Dockerfile.dev 作成(開発用)
Set-Content -Path Dockerfile.dev -Value @'
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
'@
# Dockerfile.prod 作成(本番用)
Set-Content -Path Dockerfile.prod -Value @'
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
'@
cd ..
# =====================================
# db ディレクトリ作成・初期化SQL作成
# =====================================
mkdir db
Set-Content -Path db\init.sql -Value @'
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
'@
# =====================================
# docker-compose 作成(開発用 / 本番用)
# =====================================
# 開発用 docker-compose
Set-Content -Path docker-compose.dev.yml -Value @'
version: "3.9"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
volumes:
- ./backend:/app
- /app/node_modules
ports:
- "4000:4000"
depends_on:
- db
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
volumes:
- ./frontend:/app
- /app/node_modules
ports:
- "5173:5173"
depends_on:
- backend
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: appdb
ports:
- "5433:5432"
volumes:
- db_data:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
db_data:
'@
# 本番用 docker-compose
Set-Content -Path docker-compose.prod.yml -Value @'
version: "3.9"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
ports:
- "4000:4000"
depends_on:
- db
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
ports:
- "80:80"
depends_on:
- backend
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: appdb
ports:
- "5433:5432"
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
'@
# =====================================
# git 初期化 & .gitignore 作成
# =====================================
git init
Set-Content -Path .gitignore -Value @'
# Node.js 標準
node_modules/
dist/
.env
# backend
/backend/node_modules
/backend/dist
# frontend
/frontend/node_modules
/frontend/dist
'@
補足
下記コマンドで対話形式の設定を要求されるので、添付画像のように選択してください。
npx create-vite@latest frontend -- --template react-ts
backendのtsconfig.jsonの内容を手動で設定必要
デフォルトだとコメントアウトされている "outDir": "./dist" のコメントアウトを外す。
{
"compilerOptions": {
"outDir": "./dist",
"module": "nodenext",
"target": "esnext",
"types": [],
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
}
}
補足
"outDir": "./dist" は、TypeScript のコンパイル結果(.js ファイルや型定義ファイルなど)を出力するディレクトリを指定しています。
- TypeScript では、
tsconfig.jsonに書かれた設定に従って.tsファイルが.jsにコンパイルされます。 - デフォルトではコンパイル結果はソースファイルと同じ階層に出力されるため、ソースとコンパイル済みファイルが混在してしまいます。
-
outDirを指定することで、コンパイル結果をまとめて別ディレクトリ(ここでは./dist)に出力でき、管理がしやすくなります。 - 例えば Docker イメージに組み込む際や、本番環境で Node.js から実行する際には、このディレクトリをそのまま参照できるため便利です。
本番用 nginx.conf の設定
下記ファイルをfrontend直下に作成
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# React Router 対応
location / {
try_files $uri /index.html;
}
# API のプロキシ設定
location /api/ {
proxy_pass http://backend:4000/;
}
}
補足
-
React SPA のルーティング対応
- ブラウザで /users にアクセスしても、サーバー側にそのファイルは存在しないので index.html を返す
- React Router が URL に応じて適切なコンポーネントをレンダリングする
-
API プロキシ
- フロントとバックエンドが別コンテナでも、同じ nginx を経由して通信可能
- CORS の問題も解消しやすい(フロントから見ると同一オリジンに見える)
-
Docker Compose と連携
- proxy_pass http://backend:4000/; の backend は Compose のサービス名
- ローカルの localhost:4000 ではなく、コンテナ間ネットワーク上の名前で通信
vite.config.tsの設定
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
// コンテナ内で開発用サーバーを立てるため、外部からアクセスできるように 0.0.0.0 を指定
host: '0.0.0.0',
watch: {
// WSL2 や Docker 環境ではファイル変更イベント (inotify) がうまく伝わらないことがある
// その場合、ポーリング方式に切り替えることでホットリロードを確実に動作させる
usePolling: true,
},
},
});
package.jsonの内容を手動で設定必要
frontend, backend 共にscripts,typeを後述の内容に合わせてください。
プロジェクト構成
my-app/
├── .gitignore # Git で管理しないファイル/フォルダを指定
├── docker-compose.dev.yml # 開発用 docker-compose 設定
├── docker-compose.prod.yml # 本番用 docker-compose 設定
├── backend/ # バックエンド(Express + TypeScript)
│ ├── Dockerfile.dev # 開発用 Dockerfile
│ ├── Dockerfile.prod # 本番用 Dockerfile
│ ├── package.json # バックエンド依存関係
│ ├── package-lock.json
│ ├── tsconfig.json # TypeScript 設定
│ └── src/ # バックエンドソースコード
│ ├── index.ts # エントリーポイント
│ └── routes/
│ └── users.ts # ユーザー API
├── frontend/ # フロントエンド(React + Vite)
│ ├── Dockerfile.dev # 開発用 Dockerfile
│ ├── Dockerfile.prod # 本番用 Dockerfile
│ ├── nginx.conf # Nginx 設定
│ ├── package.json # フロント依存関係
│ ├── package-lock.json
│ ├── tsconfig.json # 共通 TS 設定
│ ├── tsconfig.app.json # アプリ用 TS 設定
│ ├── tsconfig.node.json # Vite 用 TS 設定
│ ├── vite.config.ts # Vite 設定
│ ├── eslint.config.js # ESLint 設定
│ ├── index.html # SPA エントリーポイント
│ ├── public/
│ │ └── vite.svg
│ └── src/
│ ├── main.tsx # React エントリーポイント
│ ├── App.tsx # アプリ全体のルーティング
│ ├── App.css
│ ├── index.css # グローバルCSS
│ ├── assets/ # アセット(画像など)
│ │ └── react.svg
│ └── pages/ # ページコンポーネント
│ ├── Home.tsx
│ ├── Home.css
│ └── Users.tsx
├── db/ # データベース関連
│ └── init.sql # コンテナ初期化時に流すSQL(テーブル定義, seedデータなど)
.gitignore
# 共通
node_modules/
dist/
.env
# backend 固有
/backend/node_modules
/backend/dist
# frontend 固有
/frontend/node_modules
/frontend/dist
補足
node_modules を Git 管理すると、リポジトリが巨大になったり、OS や環境によって動作が変わったり、差分管理が困難になるため、基本的には Git に含めない ことが推奨されます。
必要な依存関係は package.json と package-lock.jsonで管理し、開発者は各自の環境で npm install で再現可能です。
Backend (Express + TypeScript)
package.json のスクリプト
{
"type": "module",
"scripts": {
"dev": "nodemon --watch src --ext ts --exec \"node --loader ts-node/esm src/index.ts\" --legacy-watch",
"build": "tsc",
"start": "node dist/index.js"
}
}
dev は nodemon + ts-node を使用してホットリロード。
補足
- ts-node
TypeScript のファイルを コンパイルせずにそのまま実行できるツール。開発中の素早い確認やテストに便利。 - nodemon
コードの変更を監視して 自動で再実行してくれるツール。開発時の手動再起動を省ける。 - --legacy-watch
WSL2 上で Docker を動かしている場合、ファイルの変更検知に失敗することがあります。特に Windows 側のディレクトリをマウントしている場合 に発生しやすいです。
これは、WSL2 のファイルシステムが inotify イベントを正しく伝えられない。
→そのため nodemon / vite などが ファイル変更を検知できないといった理由です。
このようなときに --legacy-watch を付けると、inotify ではなく ポーリング方式 で監視するため、ホットリロードが機能するようになります。
→
純粋な Linux → 基本的に --legacy-watch は不要
WSL2 / Docker Desktop (Mac/Win) → ファイル監視が弱いため、--legacy-watch が役立つ
Dockerfile.dev(開発用)
# Node.js 20 をベースイメージとして使用
FROM node:20
# 作業ディレクトリを /app に設定
WORKDIR /app
# package.json と package-lock.json をコンテナにコピー
# 依存関係をインストールする前にコピーすることで
# Docker キャッシュを活用でき、再ビルドを高速化
COPY package*.json ./
# npm install を実行して依存パッケージをインストール
RUN npm install
# 残りのアプリケーションコードをコンテナにコピー
COPY . .
# コンテナ起動時に実行するコマンド
# 開発用なので nodemon や vite の dev サーバーを実行
CMD ["npm", "run", "dev"]
Dockerfile.prod(本番用)
# ===========================
# ビルドステージ
# ===========================
# Node.js 20 をベースにしたビルド用イメージ
FROM node:20 AS builder
# 作業ディレクトリを /app に設定
WORKDIR /app
# 依存関係のインストールに必要な package.json と package-lock.json をコピー
COPY package*.json ./
# 依存パッケージをインストール(開発用も含む)
RUN npm install
# 残りのソースコードをコピー
COPY . .
# アプリケーションをビルド(例:TypeScript をコンパイル)
RUN npm run build
# ===========================
# 本番ステージ
# ===========================
# 軽量な Node.js イメージを使用
FROM node:20-slim
# 作業ディレクトリを /app に設定
WORKDIR /app
# ビルド済みの成果物を builder からコピー
COPY --from=builder /app/dist ./dist
# 依存関係の情報をコピー(本番用のみ)
COPY package*.json ./
# 本番用依存パッケージのみインストール
RUN npm install --only=production
# コンテナ起動時に実行するコマンド
CMD ["npm", "run", "start"]
補足
-
ビルドステージ (builder)
TypeScript のコンパイルやフロントエンドのビルドなどを行う
開発用依存もインストール
ビルド成果物(dist)だけを本番ステージに渡す -
本番ステージ
軽量イメージを使用して最小限のコンテナを作る
本番依存だけをインストール
ビルド済み成果物のみ配置してアプリを起動
Frontend (React + Vite)
package.json のスクリプト
{
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
}
}
開発は vite でホットリロード。
補足
- vite --host 0.0.0.0 の意味
- デフォルトでは Vite は localhost だけで待ち受ける
0.0.0.0 を指定すると 外部IPからもアクセス可能 になる
例:Docker コンテナ内で起動してホストPCのブラウザからアクセス可能 - 即時リロード
Vite は ファイル変更を監視して、自動でブラウザを更新
通常の HTML/JS の変更も即時反映
React や Vue の場合は HMR によりコンポーネント単位で差分更新されるのでページ全体のリロードなしで反映される
- デフォルトでは Vite は localhost だけで待ち受ける
- "tsc -b && vite build"の意味
- tsc -b
TypeScript プロジェクトを ビルドモードでコンパイル
tsconfig.json の設定に従い、型チェックと JS 出力を行う - vite build
Vite が 最適化済みのフロントエンド静的ファイル( .js )を生成
出力先は通常 dist/ フォルダ
- tsc -b
Dockerfile.dev
# Node.js 20 をベースにしたイメージを使用
FROM node:20
# 作業ディレクトリを /app に設定
WORKDIR /app
# 依存関係の定義ファイルをコピー
COPY package*.json ./
# npm install で依存パッケージをインストール(開発用も含む)
RUN npm install
# ソースコード全体をコンテナ内にコピー
COPY . .
# コンテナ起動時に開発用サーバーを起動(即時リロード付き)
CMD ["npm", "run", "dev"]
Dockerfile.prod
# ============================
# 1. ビルド用の Node 環境を準備
# ============================
# Node.js 20 の公式イメージを使用
FROM node:20 AS builder
# 作業ディレクトリを /app に設定
WORKDIR /app
# 依存関係の定義ファイルをコピー
COPY package*.json ./
# 依存関係をインストール
RUN npm install
# プロジェクト全体をコピー
COPY . .
# React/Vite プロジェクトをビルドして dist フォルダを生成
RUN npm run build
# ============================
# 2. ビルド結果を nginx に配置
# ============================
# 軽量な nginx アルパインイメージを使用
FROM nginx:alpine
# 先ほどビルドした dist フォルダを nginx の公開ディレクトリにコピー
COPY --from=builder /app/dist /usr/share/nginx/html
# ============================
# 3. カスタム nginx 設定を反映
# ============================
# 自分で用意した nginx.conf を nginx のデフォルト設定に上書き
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
# Nginx が自動的にポート 80 で公開するので CMD は不要
DB (PostgreSQL)
db/init.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
ここにCREATE文などDB初期化のSQLを追記していく。
Docker Compose 設定ファイル
docker-compose.dev.yml (開発用)
version: "3.9"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
volumes:
- ./backend:/app # ホスト側 ./backend をコンテナ /app にマウント
- /app/node_modules # マウントしないとホスト側とコンテナ側の依存が衝突する
ports:
- "4000:4000" # ホスト:コンテナ (左側を変えるとホストからアクセスする際のポートを変更可能)
depends_on:
- db
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
volumes:
- ./frontend:/app
- /app/node_modules
ports:
- "5173:5173" # ホスト:コンテナ (左側を変えるとホストからアクセスする際のポートを変更可能)
depends_on:
- backend
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: appdb
ports:
- "5433:5432" # ホスト:コンテナ (左側を変えるとホストからアクセスする際のポートを変更可能)
volumes:
- db_data:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
db_data: # 永続化ボリューム
docker-compose.prod.yml (本番用)
version: "3.9"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
ports:
- "4000:4000"
depends_on:
- db
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.prod
ports:
- "80:80"
depends_on:
- backend
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: appdb
ports:
- "5433:5432"
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
開発フロー例
実行手順
開発用ビルド & 起動
docker compose -f docker-compose.dev.yml build
docker compose -f docker-compose.dev.yml up
- フロント: http://localhost:5173
- バックエンド: http://localhost:4000
開発用 Compose ファイルで、コンテナ・イメージ・ボリュームを全て削除
docker compose -f docker-compose.dev.yml down --rmi all -v
本番用ビルド & 起動
docker compose -f docker-compose.prod.yml up --build -d
- フロント: http://localhost:80
- バックエンド: http://localhost:4000
本番用 Compose ファイルで、コンテナ・イメージ・ボリュームを全て削除
docker compose -f docker-compose.prod.yml down --rmi all -v
API の追加方法 (Backend)
1. CORSの設定
import cors from "cors";
// // 開発用:全オリジンを許可
// app.use(cors());
app.use(cors({ origin: "http://localhost:5173" }));
補足
corsモジュールは下記でインストール
npm install cors
npm install -D @types/cors
2. ルートファイル作成
import { Router } from "express";
import { Pool } from "pg";
const router = Router();
// PostgreSQLの接続設定
const pool = new Pool({
host: "db",
port: 5432,
user: "user",
password: "pass",
database: "appdb",
});
// GET /users
router.get("/", async (_req, res) => {
try {
const result = await pool.query("SELECT id, name FROM users");
res.json(result.rows);
} catch (err) {
console.error(err);
res.status(500).json({ error: "Internal server error" });
}
});
export default router;
補足
- コンテナ間通信のため、ホスト名はサービス名の'db'を使用
- ポートもコンテナ内の5432を使用
3. エントリポイントに組み込み
import userRouter from "./routes/user";
app.use("/api/users", userRouter);
上記の追記を行ったindex.ts
import express from "express";
import cors from "cors";
import userRouter from "./routes/users.js";
const app = express();
const PORT = 4000;
// 全てのオリジンを許可
app.use(cors());
// 特定オリジンのみ許可
// 開発用
// app.use(cors({ origin: "http://localhost:5173" }));
// 本番用
// app.use(cors({ origin: "http://localhost" }));
app.get("/", (req, res) => {
res.send("Hello World");
});
app.use("/api/users", userRouter);
app.listen(PORT, () => {
console.log(`Backend running on port ${PORT}`);
});
補足
- http://localhost はブラウザのデフォルトポート80を省略した形
- http://localhost:80 にアクセスすると、ブラウザのoriginがhttp://localhost になってしまう
- ブラウザのoriginと完全に一致させる必要があるため、ここでは http://localhost を指定する必要がある
4. 動作確認
curl http://localhost:4000/api/users
画面の追加方法 (Frontend)
1. ページコンポーネント作成
Home.tsx(デフォルトで作成されるApp.tsx ほぼそのままです)
import { useState } from 'react'
import reactLogo from '../assets/react.svg'
import viteLogo from '/vite.svg'
import './Home.css'
function Home() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default Home
Users.tsx(API呼び出しを行うページコンポーネント)
import { useEffect, useState } from "react";
export default function Users() {
const [users, setUsers] = useState<{ id: number; name: string }[]>([]);
useEffect(() => {
// backendサーバー (http://localhost:4000/api/users) に GET リクエストを送る
fetch("http://localhost:4000/api/users")
// サーバーからのレスポンス (Response オブジェクト) を JSON に変換する
.then((res) => res.json())
// JSON に変換したデータを setUsers に渡して state を更新する
.then(setUsers);
}, []);
return (
<div>
<h1>ユーザー一覧</h1>
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
</div>
);
}
2. ルーティングに追加
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import Users from "./pages/Users";
function App() {
return (
<BrowserRouter>
<Routes>
{/* http://localhost:5173/ でアクセス可能 */}
<Route path="/" element={<Home />} />
{/* http://localhost:5173/users でアクセス可能 */}
<Route path="/users" element={<Users />} />
</Routes>
</BrowserRouter>
);
}
export default App;
補足:react-router-domモジュールは下記でインストール
npm install react-router-dom@6
npm install -D @types/react-router-dom
ブラウザで確認
http://localhost:5173/users
本番用なら
http://localhost:80/users
トラブルシューティング:Dockerでモジュールが認識されない場合
開発中に pg をインストールしても Node.js 側で認識されず、Docker コンテナがクラッシュすることがありました。原因と解決手順を整理します。
- 現象
Docker Compose で backend コンテナを起動するとアプリがクラッシュ
pg モジュールを import すると以下のようなエラーが出る
node:internal/modules/run_main:123
triggerUncaughtException(...)
[Object: null prototype] { ... }
pg を import しない場合は正常に動作する
-
原因
原因は Docker の キャッシュや古いボリューム にありました。-
古い Node モジュール
Docker コンテナ内で /app/node_modules をボリュームマウントしている場合、新しくインストールした pg が反映されないことがあります。 -
古い DB ボリューム
PostgreSQL コンテナが以前のボリューム(古いデータ)を使用していると、接続設定が正しくても動作しない場合があります。 -
Docker イメージキャッシュ
古いビルドキャッシュが残っていると、新しい依存関係が正しく反映されません。
-
-
解決方法
開発環境を完全リセットして再構築することで解決しました。
# 1. 現在起動中の Docker Compose サービスを停止して削除
sudo docker compose down
# → コンテナ、ネットワーク、デフォルトで作成されたボリュームは削除される(ただし named volumes は残る)
# 2. 不要な Docker リソースを一括削除
sudo docker system prune -a --volumes
# -a: 使用されていないすべてのイメージ、コンテナ、ネットワークを削除
# --volumes: 使用されていないボリュームも削除
# → 完全にクリーンな状態にすることで、古いイメージやキャッシュで起きる問題を防ぐ
# 3. 開発用 Docker Compose をビルドして起動
sudo docker compose -f docker-compose.dev.yml up --build
system prune -a --volumes により、未使用のコンテナ、イメージ、ネットワーク、ボリュームが全て削除されます
これでクリーンな状態で pg をインストールしたコンテナが起動でき、正常に動作するようになります
本番環境ではボリューム削除に注意してください。DB データが消える可能性があります。
-
まとめ
-
Docker で Node.js + PostgreSQL を組み合わせる場合、ボリュームやキャッシュの状態が依存関係の反映に大きく影響する
-
開発環境でのトラブルはまず「キャッシュや古いボリュームのクリア」を検討する
-
本番ではデータ消失に注意しつつ、必要に応じてイメージをビルドし直す
-
最後に
今回紹介した構成はあくまで 最小構成の例 です。
実際の業務環境では、以下の点に注意して設計をする必要があります。
- localhost ではなくサービス名やドメインでの接続設定
- バックエンド・フロントエンドの階層構造の整理
- DB 接続やマイグレーションの自動化
- CORS 設定やセキュリティ対策
- 開発・本番環境での差分管理
これらについても後々深掘っていきたいと思います。
学習のために構築してみたものなので、不完全な部分もあるかと思います。
アドバイスを頂けたら幸いです。


