2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

民泊清掃のシフト管理アプリをAWSサーバーレスで作った話

2
Posted at

作ったもの

親戚が経営している民泊清掃代行会社のために制作した、シフト管理Webアプリです。
ググると同名のサービスがいくつかヒットしますが、全て無関係です。

主な機能は次のとおり。

シフトカレンダー

カードの色はオーナー会社ごとに色を設定できます。
施設名の下に担当スタッフの名前が表示されます。

月表示
image.png

週表示
image.png

シフト詳細

カレンダーのカードをクリックするとシフト詳細を閲覧できます。

「シフトを確認しました」を右方向にスライドすると状態が「シフト確認済み」になります。
スマホの場合、指でスライドして「確認済み」にできるUIが直感的で楽しいと好評でした。

会社・施設管理

オーナー会社とその保有する施設を管理する画面です。
スクリーンショット 2026-10-21 15.08.12.png

施設ごとに支払い単価と請求単価を設定できます。
この情報はスタッフごとの給与計算やオーナー会社への請求金額計算に使うことができます。
image.png

スタッフ管理

スタッフ一覧画面です。
image.png

スタッフ詳細画面では表示名、権限、パスワードリセットなどが行えます。
スクリーンショット 2026-10-21 15.21.09.png

給与計算

月ごとのスタッフの給与を表示する画面です。
スクリーンショット 2026-10-21 15.25.49.png

多言語対応(日本語/英語/中国語)

スタッフは中国人の方が何人かいるので、中国語と英語にも対応しました。
右上の自分の名前をクリックすると言語選択できます。

制作の背景

親戚「シフト管理アプリを半年ほど使ってるんだけど」
親戚「開発をお願いしてた人が連絡とれなくなっちゃって・・・」
親戚「引き継いでバグ修正とか機能追加とかやってくれない?」

ぼく「いいですよ」
ぼく「どうやら前任者の方はVercel上にアプリを構築してたみたいですね」
ぼく「じゃあぼくをVercelのプロジェクトに招待してもらえますか?」

親戚「できない」

ぼく「え?」

親戚「前任者の人のアカウントで全部作ってもらってたから」
親戚「私はプロジェクトに入れない」

ぼく「入れないって・・・」
ぼく「前任者の方とは連絡取れないんですよね?」
ぼく「じゃあぼくは今動いてるシステムには手を出せないですよ?」

親戚「なんとかならない?」

ぼく「無理ですね」
・
・
・
ということで親戚のメールアドレスでAWSとGitHubのアカウントを作り直してもらいました。
(僕がVercelを使ったことがなかったのでAWSに変更)

技術スタック

フロントエンド

  • React + TypeScript

  • Tailwind CSS + shadcn/ui

  • TanStack Query(サーバー状態管理)

  • React Router v7

  • react-i18next(多言語対応)

  • FullCalendar(シフトカレンダー表示)

  • orval(OpenAPIスキーマからAPIクライアントを自動生成)

  • Vitest + React Testing Library(単体・コンポーネントテスト)

バックエンド

  • Hono + @hono/zod-openapi(APIルーティング・OpenAPIスキーマ生成)

  • Drizzle ORM

  • Zod(スキーマ検証)

  • Vitest(単体テスト)

インフラ・外部サービス

  • AWS SAM(IaC・Lambdaローカルエミュレート)
  • AWS Lambda
  • Amazon API Gateway(REST + WebSocket)
  • Amazon Aurora DSQL(サーバーレス分散SQL)
  • Amazon DynamoDB(WebSocket接続管理)
  • Amazon Cognito(認証・認可)
  • Amazon S3(画像アップロード)
  • GitHub Actions(CI/CD)
  • Google Calendar API + Google Apps Script(外部予約システムとの連携)

インフラアーキテクチャ

cleanshift-architecture.drawio.png

  • バックエンド本体
    • CloudFront → API Gateway → Lambda → Aurora DSQL
  • リアルタイム通知(お知らせ、シフト更新、チャット)
    • CloudFront → WebSocket API Gateway → 接続管理Lambda → DynamoDB
  • 清掃予定取り込み
    • 予約管理システム(Beds24・AirHost) → Google Apps Script → Googleカレンダー → 本アプリ
    • ここだけは親戚がログインできたので、旧アプリからほぼそのまま受け継ぎました
  • 認証/認可
    • Amazon Cognito(User Pool)
    • 管理者/スタッフのロールをCognitoのグループで管理

データベースには以下の観点からAurora DSQLを採用

  • RDBである
  • VPCが不要(コストを抑えられる)
  • AWSで閉じる

PostgreSQL互換のサーバーレス分散SQLで、コネクションプールやスケーリングの管理から解放されるのが利点です。
一方で、ローカル環境向けのエミュレータが無い、外部キー制約が使えない(カスケード処理をアプリ側で実装する必要がある)など、既存のPostgreSQLを前提にしたツールやノウハウがそのまま使えない場面もありました。

環境はdev・stg・prdの3つで、それぞれ別のAWSアカウントに分離しています。

ランニングコスト

参考情報

  • ユーザー数:15人程度
  • 施設数:30件程度
  • シフト数:1ヶ月で250件程度
PRD STG DEV

dev・stg・prdを合わせて$0.3程度です。
これに.comのドメイン代が年間$16かかってきます。

ユーザー数が少ないので、無料枠に収まって非常に安くなってます。
サーバーレス構成のため利用の少ない時間帯はほぼコストが発生しない点が、この規模のサービスには合っていると感じています。

セキュリティ

DDoS対策

通信経路の限定

アプリケーション本体は必ずCloudFrontを通るように設計しています。
各サービスは1つ前のサービスからのリクエストのみ受け付けるように権限を絞っています。

カスタムヘッダ検証

CloudFrontを回避して直接API Gatewayに来るリクエストを弾くため、CloudFrontでリクエストヘッダに検証用の値を付与し、アプリ内で検証しています。

この値はGitHub Actionsワークフロー内でランダム値を生成しており、デプロイのたびに変わります。

デプロイ時のOIDC認証

GitHub ActionsからAWSへデプロイするとき、IAMユーザーのアクセスキーをGitHub Secretsに置く方法が手軽ですが、漏れると期限なく悪用され得る長期認証情報になります。そこで、GitHubのOIDC(OpenID Connect)でIAMロールを一時的に引き受ける方式にしています。

開発で工夫した点・苦労した点

JST/UTCの境界をめぐる同期バグ

Googleカレンダーからアプリに清掃シフトを同期する際、Googleカレンダーから消えたシフトをアプリ側でも削除する処理があります。

カレンダー同期の対象期間を「現在時刻より7日前〜現在時刻から3ヶ月後」としていたところ、JSTの深夜〜早朝に同期が走ると、UTC基準の日付境界とズレてちょうど7日前あたりのシフトが丸ごと削除されてしまうという不具合がありました。

Google APIへの問い合わせ範囲と、DB側の削除判定の範囲を、どちらもJSTの暦日に揃えることで解消しました。

外部iCalのUIDが安定しない問題

一部施設の予約情報をAirHostのiCalフィードから取り込んでいましたが、このiCalは取得するたびにUID(イベントの一意識別子)が変わる仕様でした。

イベントの同一性判定にUIDを使っていたため、同期のたびにGoogleカレンダーのイベントが削除・再作成され、アプリ側のシフトのアサインやチャットも一緒に失われていました。

識別子を「変わらない情報(日付など)」から組み立て直すことで解消しました。

Apps Scriptの実行時間制限

外部連携のGoogle Apps Script側で、同期処理の後始末に使う管理用データが削除されずに溜まり続け、実行時間が伸びて6分の上限を超えるようになっていました。不要になったデータを都度掃除する処理を追加して解消しました。

AIがAurora DSQLで使えないSQLを生成してしまう問題

まだまだDSQLのドキュメントが少ないためか、使えないSQL構文を何度も作っては失敗するのを繰り返していました。

そこで、使える構文と使えない構文を整理してReadmeにメモするようにしたところ、使えないSQLを生成することがなくなりました。

モバイル対応

ユーザーはスマホからアクセスすることが多いです。
そのためモバイルではサイドバーはデフォルトでは非表示にするなど、最適化を重点的に行いました。

AIの使いどころ

かなりの範囲でAIにお世話になりました。
簡単な実装や調査ではClaude Sonet 5.5を、複雑な実装にはOpus 5.5を使用しました。

人間がやったところ

  • 要件定義
  • 画面構成設計・初期実装
  • DB・API設計・初期実装
  • ベースのAWSアーキテクチャ設計
  • AWSへのデプロイ設定

AIがやったところ

  • 画面の量産
  • APIの量産
  • 複雑な仕様検討の壁打ち
  • DBへのアクセス権限を与えて、データのクリーニング処理

こだわりポイント

アニメーションUI

通知が1件以上未読のとき、ベルアイコンが揺れます。
bell.gif

シフト詳細を開くとき、モーダルが下から出現します。
shift.gif

作った感想

初めて触る技術がたくさんあった

このアプリは前から触ってみたかった技術(Hono、Reactなど)を使うことができたので、非常に勉強になりました。

とくにReactを触ったおかげでフロントエンドの解像度がかなり上がりました。
Tailwind CSSはどういう書き方をするのか、リンタ・フォーマッタはBiomeが使いやすい、OvalでOpenAPIスキーマからClientコードを自動生成するのが便利、などなど。

AIを使った開発

AI駆動開発がしやすいように初めてモノレポを採用しましたが、かなり良かったです。
バックエンドとフロントエンドさらにはインフラまでAIが一気に見てくれるので、機能実装やバグ修正がめちゃくちゃ速くなりました。

ランニングコスト

できるだけAWSの費用が少なくなるように意識していたので、本番運用が始まってからコストが非常に低く抑えられているのを見て安心しました。

個人開発

個人開発ではこれが初めて完成させられたWebアプリでした。
これまでも何度か開発を始めては、飽きてやめるを繰り返してきました。

僕は意志の弱い人間なので、クライアントに監視されないと手が動かないみたいです。

今後実装予定のもの

請求・給与機能の強化

清掃にはオプションをつけることができ、オプションを実施すると請求金額や給与に上乗せされます。

オプション例

  • エアコンフィルター掃除
  • 電球交換

これらもアプリ上で管理できるようにしたいという要望が挙がっています。

スタッフ割り当てサポート機能

スタッフごとに稼働できる曜日や経験の多い少ないがあり、それを加味しながらシフトを組むのが非常に大変です。

アプリ上でスタッフの出勤可能日や休みたい日を登録しておけば、シフト割り当てをよりスムーズに行えるようにしたいという要望が挙がっています。

スマホへの通知

シフト予定日の前日にスタッフにリマインド通知を送れるようにしたいという要望が挙がっています。

ユーザー種別に「オーナー」を追加

現在ユーザー種別は「管理者」と「スタッフ」だけですが、ここに「オーナー」を追加したいという要望が挙がっています。

オーナーは自分が所有する施設のシフト情報を閲覧することが可能です。

古いシフトデータの定期削除(DBコスト削減)

Aurora DSQLは保存しているデータ量が課金額に影響するため、1年以上前など、古いデータを削除するようにしたいと考えています。

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?