ブログをさぼってしまった。毎日続けている人はすごいなあ。
Next.js と AWS で ToDo アプリを作ったので紹介する。
はじめに
Next.js × AWS で ToDo アプリを作った 🚀
アカウント登録はメール認証を行っていないため、
test@examle.com
のようなダミーのメールアドレスで可能。
💻 フロントエンド
🔑 ログイン画面の実装
シンプルなログイン画面を作った。
工夫したところはパスワードマスクの解除。目のアイコンをクリックすると
type
を text
にすることでマスクを解除できる。
<div className={styles.formGroup}> <label htmlFor="password">パスワード</label> <div className={styles.passwordContainer}> <input id="password" type={showPassword ? "text" : "password"} // textにすることでパスワードマスク解除 value={password} onChange={(e) => setPassword(e.target.value)} required minLength={6} className={styles.input} /> <span className={styles.passwordToggleIcon} onClick={() => setShowPassword(!showPassword)} // アイコンクリック時ステートを更新 > {/* パスワードマスクアイコン切り替え */} {showPassword ? <PasswordEyeIcon /> : <PasswordCloseEyeIcon />} </span> </div> </div>
✏️ ToDo 登録画面
ToDo 登録画面はサイドメニューに ToDo 一覧を表示し、右側にタスク詳細を表示するように実装した。
画面サイズが 950 px 以下になったとき、サイドメニューをアイコンで表示/非表示を切り替えることができるようにした。
アイコンは以下を利用。
https://fonts.google.com/icons
画面サイズの変更イベントをリッスンするために
useEffect
で addEventListener
を行う。
useEffect(() => { if (userInfo.userId) { getTodos(); } // 画面サイズのステートを更新する関数 const handleResize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; handleResize(); // イベントリスナーに追加 window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, [userInfo.userId]);
参考: https://ja.react.dev/learn/separating-events-from-effects
メニューアイコンをクリックするとオーバーレイを施す。
ToDo チェックボックスをおしゃれにする
To-Do のチェックボックスをおしゃれにした。
参考: https://getcssscan.com/css-checkboxes-examples
👉 こちらのサイトはとても便利でおしゃれな CSS の例が載っています。
ローディングアイコンをおしゃれにする
ローディングアイコンもおしゃれにした。
👉 こちらのサイトは豊富なローディングアイコンの例が載っている。
⚡ バックエンド
バックエンドはコスト削減のためサーバーレス構成にした。
DynamoDB キー設計
属性名 | 型 | 理由 |
---|---|---|
userId | S | ユーザー ID(UUID) |
todoId | S | ToDo ID(UUID) |
title | S | ToDo タイトル |
description | S | ToDo 説明 |
status | N | 0: 未完了、1: 完了 |
createdAt | S | 作成日時(ISO8601 JST) |
updatedAt | S | 更新日時(ISO8601 JST) |
Lambda 関数による DynamoDB CRUD 操作
SDK for JavaScript を用いたデータ登録・更新・削除を実装した。
- 共通設定
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand, ScanCommand, UpdateCommand, DeleteCommand, } from '@aws-sdk/lib-dynamodb'; // DynamoDBクライアント設定 const client = new DynamoDBClient({ region: process.env.AWS_REGION }); const dynamoDb = DynamoDBDocumentClient.from(client); // DynamoDBテーブル const TABLE_NAME = 'next-todo-app'; const USER_TABLE_NAME = 'next-todo-app-users';
登録、更新、削除は以下にコード例がある。
ToDo 登録
const newItem = { userId, todoId, title, description: description || '', status, createdAt: timestamp, updatedAt: timestamp, }; const params = { TableName: TABLE_NAME, Item: newItem, }; await dynamoDb.send(new PutCommand(params));
ToDo 更新
const params = { TableName: TABLE_NAME, Key: { userId: userId, todoId: todoId, }, UpdateExpression: 'set #title = :title, #description = :description, #status = :status, #updatedAt = :updatedAt', ExpressionAttributeNames: { '#title': 'title', '#description': 'description', '#status': 'status', '#updatedAt': 'updatedAt', }, ExpressionAttributeValues: { ':title': title, ':description': description, ':status': status, ':updatedAt': updatedAt, }, ReturnValues: 'ALL_NEW', }; const result = await dynamoDb.send(new UpdateCommand(params)); res.json(result.Attributes);
ToDo 削除
const params = { TableName: TABLE_NAME, Key: { userId: userId, todoId: todoId, }, }; await dynamoDb.send(new DeleteCommand(params));
API Gateway
リソースはルートパスの直下に
{proxy+}
で作成する。 Lambda プロキシ統合を True にする。URL パスパラメータは設定していないが、勝手に追加されていた。
CORS 設定とデプロイを忘れがちなので注意が必要。
最後に
個人開発はハマると楽しい。しかしアイデアが浮かばなかったりモチベーション維持が難しい。そんなときに無理にひねり出そうとすると結局何もやらないことになってしまいがちだ。私もしばらく何も手がつかなかった。そんなときは初心に帰ってシンプルなアプリを作ってみると案外ハマることに気づいた。