🚀ミニプロジェクト10連発:実践で学ぶToDoリストアプリの本質と進化
1. はじめに 〜なぜ今、ToDoリスト?〜
「ToDoリストアプリなんて、もう作り尽くされたでしょ?」
そんな声が聞こえてきそうですが、実はこの定番アプリこそ、ソフトウェアエンジニアとしての基礎力を身につける絶好の教材です。
✅ フロントエンド・バックエンド・データベース連携
✅ 状態管理・UI/UX設計・API設計
✅ デプロイ・運用・セキュリティ対策
これらすべてを詰め込める、言わば“技術の詰め合わせ弁当”なのです。
2. 技術スタックと全体像
今回の実装では以下の技術を使います:
機能 | 技術 |
---|---|
フロントエンド | React + Vite |
バックエンドAPI | Node.js (Express) |
データベース | SQLite(開発環境)/PostgreSQL(本番環境) |
状態管理 | Zustand(軽量) |
デプロイ | Railway(簡単デプロイ) |
構成図:
[ React Frontend ] ←API→ [ Express Backend ] ←→ [ DB ]
この構成で、最小限のコストでフルスタック開発に挑戦できます。
3. 実装ステップとコード解説
ステップ1: フロントエンドの骨組み
npm create vite@latest my-todo-app --template react
cd my-todo-app
npm install
App.tsx
import { useTodoStore } from './store';
function App() {
const { todos, addTodo, toggleTodo } = useTodoStore();
return (
<div>
<h1>📝 ToDo List</h1>
<input onKeyDown={(e) => {
if (e.key === 'Enter') addTodo(e.currentTarget.value);
}} />
<ul>
{todos.map((todo) => (
<li key={todo.id}
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
{todo.title}
</li>
))}
</ul>
</div>
);
}
Zustandストア store.ts
import { create } from 'zustand';
type Todo = { id: number; title: string; done: boolean };
export const useTodoStore = create<{
todos: Todo[];
addTodo: (title: string) => void;
toggleTodo: (id: number) => void;
}>((set) => ({
todos: [],
addTodo: (title) =>
set((state) => ({
todos: [...state.todos, { id: Date.now(), title, done: false }],
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((t) =>
t.id === id ? { ...t, done: !t.done } : t
),
})),
}));
ステップ2: バックエンド(API)構築
npm init -y
npm install express cors sqlite3
server.js
const express = require('express');
const cors = require('cors');
const app = express();
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./todo.db');
app.use(cors());
app.use(express.json());
db.run(`CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY,
title TEXT,
done INTEGER
)`);
app.get('/todos', (req, res) => {
db.all('SELECT * FROM todos', (err, rows) => {
res.json(rows);
});
});
app.post('/todos', (req, res) => {
const { title } = req.body;
db.run('INSERT INTO todos (title, done) VALUES (?, ?)', [title, 0], function () {
res.json({ id: this.lastID, title, done: 0 });
});
});
app.listen(3000, () => console.log('API server running on port 3000'));
4. 実務で役立つTips & よくある落とし穴
💡 Tips
- ZustandはReduxよりシンプルで学習コストが低い
- SQLiteは開発効率が良く、本番ではPostgreSQLに切り替えれば移行しやすい
- RailwayなどのPaaSを活用すると、CI/CDやDB管理が圧倒的に楽に
⚠️ よくあるエラー
- CORS設定を忘れて、APIがブロックされる
- SQLiteのファイルパスが相対になってて実行場所によって動かない
- ZustandのsetStateが非同期だと勘違いしてバグる(実際は同期)
5. 応用例・発展アイデア
このシンプルなToDoをベースに、以下のような拡張が可能です:
- 🔔 通知機能:Firebase Cloud Messagingなど
- 📅 期限付きタスク:日付入力 + ソートロジック
- 👥 ユーザーごとのタスク:OAuth認証 + DBスキーマ変更
- 📱 モバイルアプリ化:React Nativeで再利用可能
- 📊 タスク分析ダッシュボード:完了率や集中時間の可視化(AI連携も可)
6. まとめ:ToDoアプリは最高の登竜門
✅ このプロジェクトで学べること
- 状態管理、API連携、データ永続化、デプロイの一連の流れ
- 設計から実装までの実務的な感覚
❌ 課題点
- 規模が小さいため、マイクロサービスや分散処理の要素は入れづらい
- セキュリティやスケーラビリティの観点は限定的
🌟 将来への展望
このプロジェクトを足がかりに、次のような高度なミニプロジェクトに挑戦してみましょう:
- ChatGPT APIを使った「AI議事録アプリ」
- リアルタイム通信を学べる「ミニSlackクローン」
- Cloud Run + Supabaseで「ノーサーバー構成」
次回:「天気予報APIを使う」