React × MUI × TypeScriptでTODOアプリを作って学んだこと(初心者向け)
1. はじめに
転職に向けたポートフォリオとして、TODOアプリを作ってみました!
この記事では、アプリを作る中で学んだことや「ここで詰まった…!」というポイント、そこをどうやって解決したかなどを振り返っています。
フロントエンドを中心に、実際にコードを書きながら学んだことをまとめているので、同じようにアプリ開発にチャレンジしている人の参考になれば嬉しいです!
2. 作成したアプリの概要
毎日のタスクをパパっと登録&管理できる、シンプルなToDoアプリを作りました。
フロントエンドは React + TypeScript でSPA(シングルページアプリ)として構築し、バックエンドに Node.js + PostgreSQL を使っています。
TODOアプリを作成した理由
これまで学んできたフロントエンドの技術を一通り使えそうだったのと、UIの工夫や状態管理の練習にもピッタリだと思ったからです。
あと、日常的に使えるものを自分で作ってみたかった!というのも理由のひとつです。
3. 使用技術と主な機能
このTODOアプリでは、以下の技術を使って作りました:
- フロントエンド:TypeScript / React / MUI(Material UI)
- バックエンド:Node.js / Express(APIサーバー)
- データベース:PostgreSQL
MUIで見た目を整えつつ、API経由でタスクのデータをやり取りしています。
フロントとバックエンドのやり取りを通じて、実践的な開発の流れを体験できたのが大きな収穫でした!
実装した主な機能
- タスクの追加
- タスクの削除
- タスクの完了/未完了の切り替え
4. 苦労したこと・工夫したこと
MUIの使い方
MUIは今回初めて使用しました。タスク表示には Paper コンポーネントを使って、カード風に仕上げています。
MUIはあらかじめ用意されたUIコンポーネントが豊富で、見た目も整ったパーツを簡単に使えるのが便利だと感じました。
ただ、使っていくうちに「ちょっとこの部分を細かく調整したい…」と思ったときに、sx だけだと限界を感じる場面もありました。
レイアウト全体や細かい装飾など、MUIだけでは調整しづらい部分は CSSモジュール を併用して対応しました。

状態管理の考え方
状態管理まわりで結構苦戦しました。DBとの連携ミスで「UI上ではタスクが追加されているのに、リロードすると消えてる!」みたいな現象が起きました。
原因としては、UIに表示されているタスクが ローカルの状態(useState) にしか存在しておらず、DBへの保存処理(POST)がうまくできてなかったという状況でした。
そこで、タスク追加時には必ず API経由でDBに保存 するように修正し、保存が成功したあとでフロントの状態を更新するようにしました。
地味な点かもしれませんが、大事な修正だったと思います。
余談:iPadで開発したかった話
GW中、自宅を離れデスクトップPCを触れない期間があったので、「iPadでも開発したい!」と思い立ち、GitHub Codespaces に挑戦。
ただ、思い立ってからGWに入るまでに時間がなく、環境構築が間に合わなかった為今回は断念。
パソコンで開発できるのが1番良いと思いますが、iPadで作業できれば都合がいいときもあるので、次の長期休みにでも再チャレンジしたいです。
5. 学び・気づき
エラーハンドリングの必要性
4の状態管理の考え方で触れたようなエラーに遭遇するなかで、
「エラーがどこで起きているのか、ちゃんとログに残しておかないと原因が追えないな…」と実感しました。
なので、例えば以下のコードでは、タスクのタイトルが送られてこなかった場合や、DBの挿入処理が失敗したときに、クライアント側にわかりやすいレスポンスを返すように工夫しています。
app.post('/todos', async (req, res) => {
console.log('POST受信データ:', req.body);
const { title } = req.body;
if (!title) {
return res.status(400).json({ error: 'Title is required' });
}
try {
const result = await pool.query(
'INSERT INTO todos (title, is_done) VALUES ($1, $2) RETURNING *',
[title, false]
);
console.log('Query result:', result);
if (result.rows && result.rows.length > 0) {
res.json(result.rows[0]);
} else {
res.status(400).json({ error: 'Failed to add todo' });
}
} catch (err) {
console.error('Error adding todo:', err);
res.status(500).json({ error: 'Internal Server Error' });
}
});
UIを設計しながら開発する楽しさ
今回のアプリは機能としてはとてもシンプルですが、色合いや余白、フォントの雰囲気などを調整しながら、自分の好みに合うUIを少しずつ形にしていく工程がとても楽しかったです。
「この配置のほうが見やすいかも」とか「フォントを変えるだけで印象が変わるな」といった気づきを得ながら、UIを試行錯誤して作っていくプロセス自体が学びになりました。
今回は MUI をベースに使ったことで、デザインの骨組みをサクッと整えながら、細かい部分だけ自分でカスタマイズするというやり方で開発しました。
将来的には、Figma などを使ってワイヤーフレームやモックを先に作ってから、それをもとに実装してみる…といった流れも試してみたいと思っています。
コンポーネントの分け方によってコードの可読性が変わる
開発を進めながら、「コンポーネントの切り分けって、見た目以上に重要だな」と実感しました。
最初はひとつのファイルにいろいろ書いてしまっていたのですが、機能ごとにパーツを分けていくことで、コードの見通しがよくなり、後から見返したときにどこを修正すればいいかがすぐにわかるようになりました。
6. 今後の改善点/やりたいこと
- ローディング画面をつくる
デプロイ後、タスクの読み込みに時間がかかることがあったため、読み込み中に表示するローディング画面を追加する予定です。 - サーバー側のエラーをフロントに表示させる
- タスクのカテゴリーわけ
- ユーザー認証機能の追加 …など
7. おわりに
一つのものを自分でいちからつくるのはやはり達成感が大きかったです。
小規模なアプリですが、UIデザインを工夫したりと、たくさんの学びが詰まった開発になりました。
やってみたいこと、改善したいところはまだまだあるので、今後も学んだことを活かしながら、引き続き開発を続けていきたいと思っています!

Discussion