ACTALE

https://github.com/406040/future-quest.git

Next.js

GitHub

React

Node.js

PostgreSQL

予定をこなすだけでは、続かなかった。 予定を、冒険に変えよう。

原口藍理

岩下

木下こころ

推しアイデア

「予定を、冒険に変えよう」がコンセプト。プロジェクトは冒険、タスクはクエストとして管理します。クエストを達成するとモンスターに攻撃でき、毎日の行動がゲームの物語になります。

作った背景

タスクを登録しても、一覧を見るだけでは行動を続けにくいと感じたことが出発点です。義務になりがちな予定管理にゲームの達成感を加え、楽しく最初の一歩を踏み出せるサービスを目指しました。

推し技術

Cookieベース認証、RLS、レスポンシブUI、CSSアニメーションに挑戦しました。演出中の表示対象を固定しながらHPだけを更新するなど、データの正しさとゲーム体験の両立を工夫しました。

プロジェクト詳細

ACTALE開発記録

日々の予定やタスクを管理するサービスはたくさんあります。しかし、私自身はタスクを登録しても、一覧を見るだけではなかなか行動につなげられませんでした。

そこで考えたのが、予定や目標をRPGのクエストとして管理するWebアプリ「ACTALE」です。 ACTALEでは、プロジェクトを「冒険」、タスクを「クエスト」として扱います。クエストを完了するとモンスターへダメージを与え、討伐するとEXPを獲得できます。

単に予定を管理するのではなく、予定へ向き合うこと自体をゲーム体験に変え、その積み重ねを自分自身の物語として残すことを目指しました。

あなたは主人公。ACTALEは、あなたの現実を冒険譚に変える。

ACTALEでは、プロジェクトを「冒険」、タスクを「クエスト」、向き合う課題を「モンスター」として表現します。

クエストを達成するとモンスターへダメージを与え、討伐するとEXPを獲得できます。さらに、達成してきた出来事は「冒険ログ」に刻まれ、自分がこれまで歩いてきた道を振り返ることができます。

予定を、冒険に変えよう。

これは、ACTALEの体験を短く表したキャッチコピーです。

image ACTALEのホーム画面。次に取り組むクエストとモンスターを確認し、日々の行動を冒険として進めます。

名前に込めた意味と、これから

ACTALEという名前は、ACT(行動)とTALE(物語)組み合わせて生まれました。

予定を立てるだけで終わらせず、最初の一歩を踏み出してほしい。小さなクエストの達成が次の行動につながり、毎日の積み重ねが自分自身の物語になってほしい。

そんな思いを、ACTALEという名前と「予定を、冒険に変えよう。」という言葉に込めています。 ACTALEが目指しているのは、タスクにゲーム風の見た目を付けることだけではありません。

今日の行動が、未来から振り返ることのできる自分の物語として残っていく。その体験を形にすることを目指しています。

作品名

ACTALE

旧作品名

Future Quest

対応端末

PC スマートフォン image PCとスマートフォンの両方に対応しています。PCでは情報を広く確認でき、スマートフォンでは下部ナビゲーションから主要画面へ移動できます。

公開URL

https://actale.vercel.app

使用技術

  • Next.js
  • App Router
  • TypeScript
  • Supabase
  • Supabase Auth
  • Supabase SSR
  • PostgreSQL
  • Vercel
  • GitHub
  • CSS Animation
  • レスポンシブデザイン
  • Google OAuth 2.0
  • Google Calendar API
  • Next.js Route Handler
  • Node.js Crypto
  • AES-256-GCMによる認証付き暗号化
  • HttpOnlyCookie

image

1. 挑戦度

初めての技術を避けずに選んだ

ACTALEでは、画面を作るだけでなく、認証、データベース、セキュリティ、本番公開まで含めたWebサービスの開発に挑戦しました。

特に難しかったのは、ユーザーごとに安全にデータを分離することです。

ログインユーザーが別のユーザーのプロジェクトやクエストを閲覧できてしまうと、アプリとして成立しません。そのため、Supabase Authによる認証だけでなく、Row Level Securityも設定しました。 projects、tasks、profilesの各データにユーザーIDを関連付け、ログイン中の本人が所有するデータだけを操作できる設計にしました。

挑戦した技術

  • Next.js App Routerによるファイルベースルーティング
  • TypeScriptによる型を意識した開発
  • Supabase Authによる新規登録とログイン
  • Cookieを利用したセッション管理
  • メール確認とパスワード再設定
  • Row Level Securityによるユーザーデータの分離
  • SupabaseとNext.jsの接続
  • GitHubとVercelを連携した自動デプロイ
  • PCとスマートフォンのレスポンシブ対応
  • クエスト完了と連動したアニメーション
  • BGMと効果音の制御
  • テーマ選択と解放レベルの実装

タスク管理とゲームを組み合わせた

ACTALEで最も大きな挑戦は、タスク管理機能とRPGの演出を同時に成立させることでした。 ゲーム演出を優先しすぎると、予定を管理するアプリとして使いづらくなります。一方で、管理機能だけを重視すると、ACTALEらしい楽しさがなくなります。

そこで、中心となる操作は一般的なタスク管理と同じようにシンプルにし、クエストを完了した瞬間だけゲーム演出が始まる設計にしました。

クエスト完了 ↓ モンスターへダメージ ↓ HPの減少 ↓ モンスター討伐 ↓ QUEST CLEAR ↓ EXP獲得 ↓ 必要な場合のみLEVEL UP ↓ 次のクエストへ

予定を管理する機能と、続けたくなるゲーム性の両立に挑戦しました。


架空のデータに頼らない設計へ変更した

開発初期には、画面を分かりやすく見せるための固定デモデータがありました。

しかし、本番サービスとして考えると、新規ユーザーの画面に他人のサンプルプロジェクトが表示されることは不自然です。また、実際の保存データと固定データが混在すると、不具合の原因にもなります。

そこで、固定されたプロジェクト、クエスト、カレンダー予定、EXP、レベル、モンスター進捗を廃止しました。 新規ユーザーにはオンボーディングを表示し、ユーザー自身が入力した最初のプロジェクトとクエストを、本人のユーザーIDと一緒に保存する設計へ変更しました。 スキップした場合も、デモデータを作らず、適切な空状態を表示します。

見栄えのよいデモを残すのではなく、実際の利用者にとって正しい動作を優先しました。


既存ログインと分離したGoogleカレンダー連携

GoogleアカウントによるACTALEへのログインとは別に、Googleカレンダーの予定を読み取り専用で表示する連携機能を実装しました。

既存のGoogleログインへカレンダー権限を追加すると、ログインするだけの利用者にも不要な権限を求めることになります。

そこで、利用者がカレンダー画面の「Googleカレンダーと連携」を押した場合だけ、Calendar専用のOAuth認可を開始する設計にしました。

要求する権限も、Googleカレンダーのイベントを読み取るための範囲だけに限定しています。

取得した予定について、ACTALEから次の操作は行いません。

・Google予定の編集 ・Google予定の削除 ・完了状態の変更 ・ACTALEのクエストへの保存 ・プロジェクトとの関連付け ・モンスターへの攻撃 ・EXPやレベルの更新

Googleから取得したアクセストークンはブラウザへ直接返さず、サーバー側で暗号化してHttpOnly Cookieへ保存します。参加者、主催者、説明、場所、Google MeetのURLなどは取得結果としてブラウザへ返さず、予定タイトルと日時だけを表示します。

機能を追加することだけでなく、既存ログインを壊さず、必要な権限だけを求め、安全に連携を解除できるところまで設計することに挑戦しました。

2. 完成度

実装した主な機能

ACTALEは、画面デザインだけの作品ではありません。ユーザー登録からデータ保存、ゲーム演出、本番公開まで、実際に操作できるWebアプリとして完成させました。

アカウント機能

・メールアドレスとパスワードによる新規登録 ・ログイン ・ログアウト ・メール確認 ・パスワード再設定 ・ログイン状態の保持 ・未ログインユーザーのアクセス制御 ・ユーザーごとのデータ分離

プロジェクト機能

・プロジェクト機能 ・プロジェクトの作成 ・プロジェクトの表示 ・プロジェクトの編集 ・プロジェクトの削除 ・プロジェクトごとのクエスト管理 ・プロジェクトとモンスターの関連付け

クエスト機能

・クエストの作成 ・クエストの一覧表示 ・クエストの完了 ・完了状態の解除 ・期限の設定 ・今日のクエスト表示 ・プロジェクト別のクエスト表示 ・おすすめクエストの表示

カレンダー機能

・期限付きクエストの表示 ・ACTALE内で登録した予定の表示 ・本人が所有するデータだけを表示 ・データがない場合の空状態 ・今日、昨日、明日の判定 ・日本時間を考慮した表示 ・Googleカレンダーとの任意連携 ・Google予定のタイトルと日時の読み取り専用表示 ・Google予定とACTALE予定の視覚的な区別 ・Googleカレンダー連携の解除 ・連携期限が切れた場合の再連携案内 ・Google APIに失敗してもACTALEの画面を利用できる部分エラー表示

RPG機能

・モンスターのHP表示 ・クエスト完了時の攻撃 ・ダメージ数値の表示 ・モンスターの揺れ演出 ・HPバーのアニメーション ・モンスター討伐 ・QUEST CLEAR演出 ・EXP獲得 ・レベルアップ ・次のクエストへの切り替え ・攻撃、討伐、レベルアップの効果音

冒険ログ機能

・達成してきた出来事の時系列表示 ・過去から現在へつながる縦型の冒険ルート ・年ごとに区切った歩みの表示 ・クエスト達成やモンスター討伐などの記録 ・現在地の表示 ・履歴がない場合の物語の開始を伝える空状態

初回ユーザー体験

・ACTALEの説明 ・最初のプロジェクト入力 ・最初のクエスト入力 ・入力内容のSupabase保存 ・オンボーディングのスキップ ・サンプルデータを使用しない空状態

UIとカスタマイズ

・PC表示への対応 ・スマートフォン表示への対応 ・モバイル用下部ナビゲーション ・BGMのオンとオフ ・BGM設定の保持 ・テーマカラーの切り替え ・レベルによるテーマ解放 ・エメラルドグリーンテーマ ・Lv.20で解放されるアメジストテーマ ・未ログイン時のタイトル画面 ・Safe Areaへの対応

アカウント機能

・メールアドレスとパスワードによる新規登録 ・メールアドレスとパスワードによるログイン ・Googleアカウントによるログイン ・ログアウト ・メール確認 ・パスワード再設定 ・Cookieによるログイン状態の保持 ・未ログインユーザーのアクセス制御 ・ユーザーごとのデータ分離


未ログイン画面とログイン後画面を分離した

未ログイン状態でトップページを開いた場合は、ACTALEの世界観を伝えるタイトル画面を表示します。

「冒険を始める」ボタンからログインへ進み、ログイン済みの場合は、ユーザー自身のクエストやプロジェクトが表示されるホーム画面へ移動します。

タイトル画像はPC用とモバイル用を用意し、画面サイズに応じて切り替えています。画像が過剰に拡大されず、ACTALEのロゴや主要部分が見切れないように調整しました。


見た目だけでなく、データの安全性も完成度に含めた

ACTALEでは、認証後のデータを単に画面上で分けるだけではなく、データベース側でもアクセスを制限しています。

ユーザーが操作できるのは、ログイン中の本人と一致するユーザーIDを持つデータだけです。 また、管理者用の強い権限を持つキーをブラウザへ公開しない構成にしました。環境変数や認証情報もGitHubへ登録しないように管理しています。 作品の完成度を、画面数やアニメーションの多さだけで判断せず、安全に利用できることまで含めて考えました。Googleカレンダー連携でも、権限を必要以上に広げないことを重視しました。

既存のGoogleログインとはOAuthフローを分離し、利用者が明示的に連携ボタンを押した場合だけ、予定を読み取るための権限を求めます。

GoogleのアクセストークンはブラウザのlocalStorageやsessionStorageへ保存せず、サーバー側で暗号化したうえでHttpOnly Cookieとして管理します。また、Google APIのレスポンス全体をブラウザへ返さず、予定タイトル、開始日時、終了日時、終日予定かどうかという、表示に必要な最小限の情報だけを返す設計にしました。

Google予定はACTALEのクエストとして保存されないため、表示しただけでモンスターへの攻撃、EXP加算、レベル更新が発生することもありません。

3. バグとの戦い

完成に近づくほど、問題が複雑になった

ACTALEの開発では、コードが書けた時点を完成とは考えませんでした。

実際にPCとスマートフォンで操作し、ログイン、再読み込み、ログアウト、再ログインなど、利用者が行う操作を繰り返しました。

その結果、通常の画面確認だけでは見つからない問題が数多く発生しました。


新規ユーザーがLv.8から始まる問題

発生した問題

本来、新規ユーザーはLv.1、EXP 0から始まる必要があります。しかし、一部の画面では新規ユーザーにもLv.8が表示されていました。

原因

開発初期に使用していた固定値やモックデータが残っていたことに加え、プロフィール取得前の初期表示にも問題がありました。

修正

・レベルの固定値を削除 ・新規プロフィールの初期値をLv.1に統一 ・プロフィール読み込み前後の表示を整理 ・再ログイン時にも本人のプロフィールを取得

学んだこと

データベース側の値が正しくても、画面側の初期値が誤っていれば、利用者には誤った情報が表示されます。保存処理だけでなく、取得前、取得中、取得後の状態を分けて考える必要があると学びました。


カレンダーに固定予定が表示される問題

発生した問題

新規ユーザーのカレンダーに、本人が作成していない予定が表示されることがありました。

原因

データが0件だった場合に、開発用の固定予定を表示するフォールバック処理が残っていました。

修正

・固定カレンダー予定を削除 ・本人が所有する期限付きクエストだけを表示 ・データが0件の場合は空状態を表示 ・ユーザーIDが設定されていない古いデータを表示しない

学んだこと

デモでは親切に見えるフォールバックも、本番では利用者を混乱させる可能性があります。データがない状態を隠さず、次の操作を伝える空状態が必要だと学びました。


モンスター演出中に表示対象が切り替わる問題

発生した問題

クエストを完了してモンスターへ攻撃した直後、アニメーションの途中で次のクエストやモンスターへ表示が切り替わることがありました。

その結果、攻撃したモンスターと討伐演出に表示されるモンスターが一致しない場合がありました。

原因

HPや討伐状態の更新と、おすすめクエストを切り替える処理が同時に進んでいたためです。

修正

演出中は、次の情報を一時的に固定するようにしました。

・表示中のプロジェクト ・表示中のクエスト ・表示中のモンスター

一方で、HP、ダメージ数値、攻撃アニメーションの状態は更新できるようにしました。

次のおすすめクエストへの切り替えは、演出がすべて終了した後だけ行います。

学んだこと

画面には、リアルタイムで更新すべき情報と、演出が終わるまで固定すべき情報があります。すべてを同じタイミングで更新すると、データは正しくても画面体験が壊れることを学びました。


今日のクエストがすべて表示されない問題

発生した問題

期限が今日に設定されているクエストが複数あっても、一部しか表示されないことがありました。

調査した項目

・配列へsliceが適用されていないか ・CSSに固定の高さがないか ・verflow: hiddenで隠れていないか ・Reactのkeyが重複していないか ・UTCと日本時間のずれがないか ・チェック操作後に全件取得が崩れていないか ・修正

不要な件数制限を取り除き、今日が期限の本人所有クエストを全件表示できるようにしました。

学んだこと

「データを取得できていない問題」と「取得したデータが画面で隠れている問題」は別です。データベース、配列処理、Reactの描画、CSSの順番で切り分けて調査する重要性を学びました。


モバイル画面でプロフィールメニューがページを押し下げる問題

発生した問題

プロフィールアイコンをタップすると、メニューの高さに合わせてヘッダーが縦に伸び、本文全体が下へ移動していました。

修正

プロフィールメニューを通常のレイアウトから分離し、カードの上へ重なるポップオーバーとして表示しました。

これにより、メニューを開いても次の要素が動かなくなりました。

・ヘッダーの高さ ・レベル表示 ・サウンドボタン ・ホーム画面のカード ・NEXT QUEST

学んだこと

メニューは表示できればよいのではなく、開閉によって周囲のレイアウトを壊さないことも重要です。要素の配置方法と重なり順を意識するようになりました。

4. 学びと成長

開発前と開発後で変わったこと

認証

開発前 ログイン機能は、メールアドレスとパスワードを確認するだけだと考えていました。

開発後 認証には、メール確認、Cookie、セッション、ログアウト、パスワード再設定、未ログイン時のアクセス制御など、多くの要素が関係していると理解しました。


データベース

開発前 画面に表示できれば、データ保存は完成だと考えていました。

開発後 テーブル設計、外部キー、ユーザーID、アクセス権限、0件状態、読み込み状態まで含めて設計する必要があると理解しました。


セキュリティ

開発前 フロントエンド上で他人のデータを非表示にすれば安全だと思っていました。

開発後 画面上の条件分岐だけでは不十分であり、データベース側でもRLSを設定する必要があると学びました。


UI設計

開発前 PCで整っていれば、スマートフォンでもある程度は使えると思っていました。

開発後 スマートフォンでは、画面幅、Safe Area、固定ナビゲーション、タップ領域、文字の折り返し、ポップオーバーの位置を個別に確認する必要があると分かりました。


デバッグ

開発前 エラーが起きたら、関係しそうなコードをまとめて変更していました。

開発後 次の順序で問題を切り分けるようになりました。

  1. 問題を一つに限定する
  2. 再現条件を明確にする
  3. 根本原因を調査する
  4. 原因に関係する箇所だけを変更する
  5. 型チェック、Lint、buildを行う
  6. 実際の画面で確認する
  7. Gitの差分を確認する
  8. 本番環境で再確認する

修正範囲を小さくすることで、別の機能を壊す危険を減らせるようになりました。


Gitと本番運用

開発前 ローカルで動けば完成だと考えていました。

開発後 GitHubへ反映されても、Vercelの本番デプロイが成功したとは限らないことを経験しました。

現在は、次の項目まで確認しています。

・作業中のブランチ ・Gitの変更状態 ・リモートとの差分 ・rebase時の競合 ・GitHubの最新コミット ・Vercel Productionの状態 ・本番が使用しているコミット ・PCとスマートフォンでの実動作


OAuthと外部API

開発前 Googleアカウントでログインできれば、Googleの他のサービスも同じ仕組みで利用できると考えていました。

開発後 GoogleログインとGoogle Calendarへのアクセス許可は、目的も必要な権限も異なると理解しました。

ACTALEでは、既存GoogleログインへCalendar権限を追加せず、利用者が必要な場合だけ開始する専用OAuthフローとして分離しました。

また、外部APIを利用するときは、取得できる情報をすべて使うのではなく、機能に必要な情報だけを取得し、ブラウザへ返す情報も最小限にすることが重要だと学びました。

5. がむしゃら度

何度失敗しても、何度新しいバグが見つかっても、ACTALEを諦めませんでした。認証が動かない、データが表示されない、演出が途中で止まる、スマートフォンで画面が崩れる。一つ直すたびに、また次の壁が現れました。

それでも、簡単な作品へ逃げたり、動いているように見せるだけのモックで終わらせたりはしませんでした。原因を追い、修正し、PCとスマートフォンで確認し、本番へ反映する。動かなければ、また原因を探してやり直す。その繰り返しでした。

完成が遠く感じるときもありました。それでも、「予定を、冒険に変える」という最初のアイデアを絶対に形にしたかった。認証も、データ保存も、モンスターとの戦いも、レベルアップも、すべてが本当に動くサービスとして届けたかった。

直して、試して、壊れて、また直す。最後の最後まで手を止めず、ACTALEとして胸を張って公開できる状態になるまで、泥臭く食らいついて完成させました。ACTALEは、一度でうまく作れた作品ではありません。失敗しても諦めず、完成するまで挑み続けた作品です。

6. 今後の課題

今後は、ゲームとしての楽しさだけでなく、スケジュール・タスク管理サービスとして安心して継続利用できる品質を高めたいと考えています。 現在の主な課題は、以下のとおりです。

・アカウント削除機能の整備 ・ゲストログイン機能の安全な実現 ・プライバシーポリシーと利用上の案内の継続的な更新 ・問い合わせ窓口の用意 ・認証やセッション管理の継続的な検証 ・PCとスマートフォンにおける表示品質の改善 ・クエスト完了時の操作速度と演出の両立 ・エラー発生時の案内と復旧手段の改善 ・アクセシビリティへの対応 ・セキュリティテストと依存パッケージの継続的な更新 ・Googleカレンダー連携の長期利用に向けた運用改善 ・Google Calendar OAuthアプリの本番公開に向けた検証 ・連携期限切れやGoogle API障害時の案内改善

なお、Googleカレンダー連携は現在テストユーザー限定で提供しています。今後は、OAuthアプリの公開に必要な設定や運用条件を確認し、安全性を維持したまま利用対象を広げることが課題です。

アカウント削除機能について

現時点では、ユーザーが画面上から自分のアカウントを即時削除できる機能は実装していません。 アカウントの完全削除には、認証情報だけでなく、そのユーザーに紐づくプロフィール、プロジェクト、クエストなどを安全な順序で処理する必要があります。また、強い権限を持つ秘密鍵をブラウザへ公開してはいけません。 そのため、現在は本人確認後に運営者が管理画面から手動で削除する方針です。

ゲストログイン機能について

ゲストログインは、登録前にACTALEのゲーム体験を試してもらうための有効な機能だと考えています。一方で、十分な分離設計を行わないまま実装すると、複数のゲストが同じデータを閲覧・編集したり、放置されたデータが増え続けたりする可能性があります。 そのため、現時点ではゲストログインの提供を見送っています。

7. 完成へ

ACTALEは、完成が見えてからも簡単には終わりませんでした。

より気軽に利用してもらうため、開発の最後にGoogleアカウントによるログイン機能へ挑戦しました。

しかし、Googleアカウントの認証には成功しているのにACTALEへログインできず、認証コードだけがURLに残る問題が発生しました。ローカル環境で直ったと思っても、本番環境へデプロイすると同じ問題が再発しました。 そこで、Google Cloudの設定、Supabase AuthのリダイレクトURL、Next.jsの認証コールバック、Cookieによるセッション保持、Vercelで使用されているコミットを一つずつ確認しました。

Google側の認証が失敗しているのか、Supabaseでユーザーが作成されていないのか、それともACTALE側でセッションを確定できていないのか。認証処理を段階ごとに切り分け、GoogleからSupabase、SupabaseからACTALEという二段階のリダイレクトを追いかけました。

直して、試して、デプロイして、本番で再発して、また原因を探す。その繰り返しでした。

それでも、Googleログインを諦めたり、ローカルで動いたことだけを理由に完成としたりはしませんでした。最終的に本番環境でGoogleログインできること、再読み込み後もセッションが維持されること、本人のデータだけが表示されること、ログアウト後にタイトル画面へ戻ることまで確認しました。

Googleカレンダーとの連携は、一度は安全性を優先して実装を見送りました。 しかし、その後、既存のGoogleログインとは完全に分離したCalendar専用OAuthフローを設計し、読み取り専用連携として実装しました。

利用者が「Googleカレンダーと連携」を押した場合だけ追加権限を求め、取得する情報は予定タイトルと日時に限定しています。Google予定はACTALEから編集、削除、完了できず、クエストやプロジェクトとして保存されることもありません。

アクセストークンはブラウザへ直接返さず、サーバー側で認証付き暗号化を行い、HttpOnly Cookieとして短時間だけ保持します。連携解除時にはCalendar専用Cookieだけを削除し、ACTALEのログイン状態やクエストデータには影響を与えません。

最初に実装を見送った経験があったからこそ、機能を追加する前に、権限の範囲、トークン管理、連携解除、エラー時の挙動まで設計することができました。 機能数を増やすことだけが完成ではありません。安全に実装できる条件を整理し、必要な対策を一つずつ確認してから追加することも、開発を通して得た大切な学びです。

認証が動かない。データが表示されない。演出が途中で止まる。スマートフォンでは画面が崩れる。一つ直すたびに、また次の問題が見つかりました。 それでも、簡単な作品へ逃げたり、動いているように見せるだけのモックで終わらせたりはしませんでした。

原因を調べ、修正し、PCとスマートフォンで確認し、本番へ反映する。動かなければ、もう一度原因を探してやり直す。その積み重ねで、認証も、データ保存も、予定管理も、モンスターとの戦いも、本当に動くWebサービスとして完成させました。

ローカルで動くことをゴールにせず、本番で利用者が実際に使えるところまで、最後の一つの問題からも逃げずに完成させました。

最後の最後まで手を止めず、「予定を、冒険に変える」ACTALEとして胸を張って公開できる状態まで、泥臭く食らいついて完成させました!

原口藍理

@4060