本記事では、Django(バックエンド)、React(フロントエンド)、MySQL(データベース)を使用してMVP(最小限の実用的製品)を開発する際の、機能追加、検証、開発方法、開発終了の手順を解説します。これにより、効率的な開発フローを確立し、最終的な製品がユーザーの期待を満たすことを目指します。
1. プロジェクトのセットアップ
開発環境の構築
まずはプロジェクトの開発環境を準備します。
Docker Composeを利用した環境構築
Docker Composeを使うことで、開発環境の構築を簡単に行えます。以下にdocker-compose.yml
の例を示します:
version: '3.8'
services:
db:
image: mysql:8.0
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
interval: 5s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
command: >
sh -c "python manage.py migrate &&
./wait-for-it.sh db:3306 -- python manage.py runserver 0.0.0.0:8000"
volumes:
- ./backend:/app
ports:
- "8000:8000"
environment:
- DATABASE_HOST=db
- DATABASE_NAME=${MYSQL_DATABASE}
- DATABASE_USER=${MYSQL_USER}
- DATABASE_PASSWORD=${MYSQL_PASSWORD}
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/admin"]
interval: 10s
timeout: 5s
retries: 3
frontend:
build:
context: ./frontend
command: npm start
volumes:
- ./frontend:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:8000
- CHOKIDAR_USEPOLLING=true
depends_on:
- backend
volumes:
mysql_data:
環境変数ファイル(.env
)
以下のような.env
ファイルをプロジェクトのルートに作成します。これにより、認証情報や接続設定がコードベースから分離され、セキュリティが向上します。
# .env
MYSQL_ROOT_PASSWORD=root_password
MYSQL_DATABASE=mvp_db
MYSQL_USER=mvp_user
MYSQL_PASSWORD=mvp_password
バックエンドのセットアップ
backend
ディレクトリ内にDockerfile
を作成します:
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
requirements.txt
には必要なパッケージを記述します:
Django==4.0.0
djangorestframework==3.13.0
mysqlclient==2.1.0
django-cors-headers==3.10.0
フロントエンドのセットアップ
frontend
ディレクトリ内にDockerfile
を作成します:
FROM node:16
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm install
COPY . .
CMD ["npm", "start"]
プロジェクト初期化
Docker環境が構築されたら、Djangoプロジェクトとアプリ、そしてReactアプリを初期化します。
Djangoプロジェクトの初期化
docker-compose run backend django-admin startproject config .
docker-compose run backend python manage.py startapp api
Reactアプリの初期化
docker-compose run frontend npx create-react-app .
2. バックエンド (Django) の機能追加手順
モデルの定義
まず、データベースのスキーマを定義するためのモデルを作成します。api/models.py
を編集して、必要なモデルを定義します:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class User(models.Model):
username = models.CharField(max_length=50, unique=True)
email = models.EmailField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.username
モデルを定義したら、マイグレーションを実行してデータベースに反映させます:
docker-compose run backend python manage.py makemigrations
docker-compose run backend python manage.py migrate
シリアライザの作成
REST APIでデータをJSON形式で送受信するために、シリアライザを定義します。api/serializers.py
を作成します:
from rest_framework import serializers
from .models import Product, User
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
ビューの実装
データを操作するためのAPIビューを実装します。api/views.py
を編集します:
from rest_framework import viewsets
from .models import Product, User
from .serializers import ProductSerializer, UserSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
URLの設定
APIエンドポイントを定義するために、URLを設定します。api/urls.py
を作成します:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet, UserViewSet
router = DefaultRouter()
router.register(r'products', ProductViewSet)
router.register(r'users', UserViewSet)
urlpatterns = [
path('', include(router.urls)),
]
メインのconfig/urls.py
も編集して、APIのURLを含めます:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
DjangoとMySQLの接続設定
config/settings.py
を編集して、MySQLデータベースへの接続を設定します:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mvp_db',
'USER': 'mvp_user',
'PASSWORD': 'mvp_password',
'HOST': 'db',
'PORT': '3306',
}
}
また、CORSの設定も追加して、フロントエンドからのAPIリクエストを許可します:
INSTALLED_APPS = [
# ...
'rest_framework',
'corsheaders',
'api',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# ...
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
]
3. フロントエンド (React) の機能追加手順
APIとの通信設定
まず、バックエンドAPIと通信するための設定を行います。src/api/index.js
を作成します:
import axios from 'axios';
const API_URL = 'http://localhost:8000/api';
const api = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
},
});
export const getProducts = () => api.get('/products/');
export const getProduct = (id) => api.get(`/products/${id}/`);
export const createProduct = (data) => api.post('/products/', data);
export const updateProduct = (id, data) => api.put(`/products/${id}/`, data);
export const deleteProduct = (id) => api.delete(`/products/${id}/`);
export const getUsers = () => api.get('/users/');
export const getUser = (id) => api.get(`/users/${id}/`);
export const createUser = (data) => api.post('/users/', data);
export const updateUser = (id, data) => api.put(`/users/${id}/`, data);
export const deleteUser = (id) => api.delete(`/users/${id}/`);
export default api;
コンポーネントの作成
次に、必要なコンポーネントを作成します。以下は商品一覧を表示するコンポーネントの例です。
src/components/ProductList.js
:
import React, { useState, useEffect } from 'react';
import { getProducts, deleteProduct } from '../api';
import { Link } from 'react-router-dom';
const ProductList = () => {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchProducts = async () => {
try {
const response = await getProducts();
setProducts(response.data);
setLoading(false);
} catch (err) {
setError('商品データの取得に失敗しました');
setLoading(false);
}
};
fetchProducts();
}, []);
const handleDelete = async (id) => {
if (window.confirm('本当に削除しますか?')) {
try {
await deleteProduct(id);
setProducts(products.filter(product => product.id !== id));
} catch (err) {
setError('商品の削除に失敗しました');
}
}
};
if (loading) return <div>読み込み中...</div>;
if (error) return <div>{error}</div>;
return (
<div>
<h2>商品一覧</h2>
<Link to="/products/new">新規作成</Link>
<table>
<thead>
<tr>
<th>ID</th>
<th>商品名</th>
<th>価格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{products.map(product => (
<tr key={product.id}>
<td>{product.id}</td>
<td>{product.name}</td>
<td>{product.price}円</td>
<td>
<Link to={`/products/${product.id}`}>詳細</Link>
<Link to={`/products/${product.id}/edit`}>編集</Link>
<button onClick={() => handleDelete(product.id)}>削除</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default ProductList;
ルーティングの設定
React Routerを使用して、アプリケーションのルーティングを設定します。
src/App.js
:
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Navbar from './components/Navbar';
import Home from './components/Home';
import ProductList from './components/ProductList';
import ProductDetail from './components/ProductDetail';
import ProductForm from './components/ProductForm';
import UserList from './components/UserList';
import UserDetail from './components/UserDetail';
import UserForm from './components/UserForm';
function App() {
return (
<Router>
<div className="App">
<Navbar />
<div className="container">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<ProductList />} />
<Route path="/products/new" element={<ProductForm />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/products/:id/edit" element={<ProductForm />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/new" element={<UserForm />} />
<Route path="/users/:id" element={<UserDetail />} />
<Route path="/users/:id/edit" element={<UserForm />} />
</Routes>
</div>
</div>
</Router>
);
}
export default App;
4. データベース (MySQL) の操作と検証
データベース接続の確認
開発中、データベース接続が正常に機能しているか確認することが重要です。以下のコマンドでMySQLサーバーに直接接続できます:
docker-compose exec db mysql -u mvp_user -p mvp_db
パスワードを入力すると、MySQLコンソールが起動します。
データベースクエリの実行
テーブル一覧を確認するには:
SHOW TABLES;
テーブルのスキーマを確認するには:
DESCRIBE api_product;
DESCRIBE api_user;
テーブル内のデータを確認するには:
SELECT * FROM api_product;
SELECT * FROM api_user;
Djangoシェルを使用したデータ操作
Django管理シェルを使用して、Pythonからデータベースを操作することもできます:
docker-compose exec backend python manage.py shell
シェル内で以下のようにモデルを操作できます:
from api.models import Product
# 新しい商品を作成
product = Product.objects.create(
name="テスト商品",
description="これはテスト商品です",
price=1000
)
# 商品を取得して表示
products = Product.objects.all()
for p in products:
print(f"{p.id}: {p.name} - {p.price}円")
# 商品を更新
product = Product.objects.get(id=1)
product.price = 1500
product.save()
# 商品を削除
product.delete()
5. 機能追加の検証手順
単体テスト
機能を追加した後は、単体テストを実行して正しく動作するか確認します。
バックエンドのテスト例(api/tests.py
):
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status
from .models import Product
class ProductTests(TestCase):
def setUp(self):
self.client = APIClient()
self.product_data = {
'name': 'テスト商品',
'description': 'これはテスト商品です',
'price': '1000.00'
}
self.product = Product.objects.create(**self.product_data)
def test_get_all_products(self):
response = self.client.get(reverse('product-list'))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(len(response.data) > 0)
def test_create_product(self):
new_product = {
'name': '新商品',
'description': '新しいテスト商品です',
'price': '2000.00'
}
response = self.client.post(reverse('product-list'), new_product, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Product.objects.count(), 2)
テストを実行するには:
docker-compose exec backend python manage.py test
統合テスト
バックエンドとフロントエンドの連携をテストするために、以下の手順を実行します:
- バックエンドとフロントエンドを起動します:
docker-compose up
- フロントエンドのテストを実行します:
docker-compose exec frontend npm test
- ブラウザを開いて
http://localhost:3000
にアクセスし、機能が正しく動作するか手動でテストします。
手動テスト
開発中は定期的に手動テストを行い、以下の点を確認します:
- 新規データの作成
- データの読み取り
- データの更新
- データの削除
- バリデーションの動作
- エラー処理
- ユーザーインターフェースの使いやすさ
6. 開発終了時の作業とデプロイ方法
本番環境用の設定
本番環境用の設定を行います。
Djangoの本番設定(config/settings_prod.py
):
from .settings import *
DEBUG = False
ALLOWED_HOSTS = ['your-domain.com']
# セキュリティ設定
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# 静的ファイル設定
STATIC_ROOT = '/var/www/static/'
ビルドとデプロイ
フロントエンドのビルド
本番環境用のReactアプリをビルドします:
docker-compose exec frontend npm run build
本番環境用のDockerファイル
本番環境用のdocker-compose.prod.yml
:
version: '3'
services:
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
restart: always
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/app/static
depends_on:
- db
env_file:
- .env
restart: always
nginx:
image: nginx:1.21
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
- static_volume:/var/www/static
- ./frontend/build:/var/www/frontend
depends_on:
- backend
restart: always
volumes:
mysql_data:
static_volume:
バックエンドの本番用Dockerfile(backend/Dockerfile.prod
):
FROM python:3.9
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi:application"]
デプロイ手順
- 環境変数ファイル(
.env
)を作成し、必要な値を設定します:DB_NAME=mvp_db_prod DB_USER=mvp_user_prod DB_PASSWORD=secure_password DB_ROOT_PASSWORD=root_secure_password DJANGO_SETTINGS_MODULE=config.settings_prod SECRET_KEY=your_secret_key
- 本番環境用のコンテナをビルドして起動します:
docker-compose -f docker-compose.prod.yml build docker-compose -f docker-compose.prod.yml up -d
- データベースのマイグレーションを実行します:
docker-compose -f docker-compose.prod.yml exec backend python manage.py migrate
開発終了後の手順
- コードの整理とリファクタリング
- 使っていないコードの削除
- コメントの追加
- コードのフォーマット
- ドキュメントの作成
- API仕様書
- 使用方法ガイド
- インストール手順
- バックアップの作成
docker-compose exec db mysqldump -u mvp_user -p mvp_db > backup.sql
- 継続的インテグレーションとデプロイの設定(CI/CD)
- GitHubアクションやJenkinsなどのCI/CDツールを設定
- テスト、ビルド、デプロイの自動化
まとめ
Django、React、MySQLを使ったMVP開発では、バックエンドAPI、フロントエンドコンポーネント、データベース操作を統合し、最小限の機能を迅速に開発することができます。機能追加後はテストを実施し、手動での検証やユニットテストを通じて品質を確保します。開発が終了した後は、Dockerを利用して本番環境にデプロイし、ユーザーに届けます。この流れを実践することで、効果的にMVPを開発し、リリースに至ることができます。
本記事で解説した手順は、MVPの段階から始まる製品開発の基礎となります。ユーザーフィードバックを収集し、継続的に改善していくことで、より価値のある製品へと発展させていくことが可能です。
コメント