はじめに
こんにちは!MOと申します。
未経験からのWebエンジニア転職を目指して、学習を続けております。
この度、プログラミングスクールRUNTEQの卒業制作として、
言葉探しゲーム風に楽しく暗記学習できるWebアプリ「NeuroWord~仲間の言葉を見つけよう~」を開発しました。
この記事では、機能紹介に加えて、個人開発で意識していた進め方や設計の考え方も紹介します。
1. NeuroWordとは
NeuroWord(ニューロワード)は、暗記学習を「言葉探しゲーム」のような体験に変えるWebアプリです。
一般的な暗記カードのように「答えを思い出す」形式ではなく、
複数の選択肢の中から関連する言葉を探して選ぶことで、
知識同士のつながりに自然と気づけることを目指しています。
主に、プログラミング学習や資格学習など、
用語や概念を関連づけて覚える必要がある学習シーンを想定しています。
また、学習するだけでなく、ユーザー自身が問題を作成できる点も特徴の一つです。
▼ アプリURLはこちら!(GitHubはこちら!)
2. なぜこのアプリを作ったのか
きっかけは、資格学習を始めた際に感じた「暗記カード学習の物足りなさ」でした。
単語と意味を1対1で覚える形式はシンプルですが、大量のカードを作る・管理するのは手間がかかります。
さらに、知識同士の関係性が見えにくく、用語を個別には覚えていても、
全体像としての理解につながりにくい点にも課題を感じていました。
そこで、答えを思い出すのではなく複数の選択肢の中から言葉を探すことで、
気軽に知識同士の関連性まで理解できるような学習体験ができないかと考えました。
「暗記を少しでも楽しくしたい」という気持ちと、「知識をつなげて理解したい」という自分自身の学習イメージを形にしたのが、NeuroWordの出発点です。
3. 解決したかった課題とアプリのコンセプト
NeuroWordを開発するにあたって、以下のような課題を解決したいと考えました。
解決したかった課題
- 単語を個別に覚えても、知識同士の関係性が見えにくい
- 暗記カードを作成・管理するコストが高く、継続しづらい
NeuroWordのコンセプト
- 答えを思い出すのではなく、選択肢の中から言葉を探す学習体験
- 知識を点ではなく、関連づけて覚えることを重視する
- 学習を「作業」ではなく、「ゲーム感覚」で続けられる形にする
4. 実装した主な機能
ここでは、NeuroWordで実装している主な機能について、
実際の画面イメージとあわせて紹介します。
(READMEと同様の内容ですが、Qiita記事用に一部構成を整理しています)
◇ ログイン不要で使える機能
アカウント登録をしなくても、トップページからすぐに学習を始められるようにしています。
興味のある問題から、「言葉探しゲーム」に挑戦
| 問題一覧 | ゲームをプレイ |
|---|---|
![]() |
|
| ユーザー登録なしで問題一覧を閲覧し、 興味のある問題を選んですぐにゲームを開始できます。 |
最大10枚のカードの中から、正しい単語と関連語の組み合わせを探します。 どうしても分からない場合は、ギブアップも可能です。 |
| ゲーム結果を見る | ゲーム結果をシェア |
|---|---|
![]() |
|
| ゲーム終了後の結果画面では、 正解の組み合わせとスコア(正答率・クリック数・解答時間)を確認できます。 | X(Twitter)への投稿機能で、学習結果をシェアできます。 |
◇ ログインすると使える機能
ログイン後は、学習を継続しやすくするための機能を利用できます。
問題投稿
| 問題投稿 |
|---|
![]() |
| アカウント作成・ログイン後は、オリジナル問題を投稿できます。 |
| 投稿した問題をシェア | |
|---|---|
| 作成した問題は、X(Twitter)への投稿機能でシェアできます。 | |
学習履歴の確認・理解済み問題スキップ
| 学習履歴を確認 | 理解した問題を非表示 |
|---|---|
![]() |
|
| これまでの学習履歴を確認できます。 | 直近2週間以内で正答率が80%以上となった問題を非表示にできます。 |
お気に入り・リスト管理
| お気に入り追加・削除 | リスト作成・削除 |
|---|---|
![]() |
![]() |
| 気になる問題をお気に入りとして管理できます。 | ユーザー自身で任意のリストを作成できます。 |
リストの問題をまとめてプレイ
| リストをまとめてプレイ |
|---|
![]() |
| リスト登録した問題をまとめてプレイし、効率的に学習できます。 |
問題修正依頼を投稿
| 修正依頼の投稿 |
|---|
![]() |
| 問題に対する修正依頼を投稿できます。 |
ゲームの流れ
問題例:
- 問題タイトル: "情報資産に対するリスク"
- 画面に表示されるカード(計6枚)
- 起点単語A: "脆弱性"
- Aの関連語群: ["ワイヤー固定していないデスクトップ", "暗号化しない通信"]
- 起点単語B: "脅威"
- Bの関連語群: ["不正アクセス", "火災"]
ゲームの進め方:
- まず起点Aのカード「脅威」を選択
- 「脅威」に関連する「不正アクセス」「火災」を選択(順不同)
- 次に起点Bのカード「脆弱性」を選択
- 「脆弱性」に関連する「暗号化しない通信」「ワイヤー固定していないデスクトップ」を選択(順不同)
- A/B全ての組み合わせが正解でクリア!
5. 技術構成と選定理由
初めての本格的なCRUDアプリ開発として、Railsを中心に
自分なりに「まず経験しておきたい」と感じた比較的よく使われている技術や機能を
一通り取り入れる構成で開発しました。
また、個人開発として「作り切ること」と「改善し続けられること」を重視しています。
使用技術一覧
| カテゴリ | 技術 |
|---|---|
| バックエンド | Ruby 3.3.6 / Ruby on Rails 7.2.3 |
| フロントエンド | Tailwind CSS / daisyUI / Hotwire(Turbo / Stimulus) |
| データベース | PostgreSQL |
| デプロイ | Render |
| 認証 | Devise / OmniAuth(Google OAuth2) |
主要な使用技術と用途
認証・セキュリティ
- Devise:メール/パスワード認証、パスワードリセット機能
- OmniAuth:Google OAuth2による外部認証
- Rack Attack:レート制限によるセキュリティ対策
データ検索・表示
- Ransack:問題一覧の検索・絞り込み
- Kaminari:ページネーション
- Ancestry:カテゴリの階層構造管理
UI / UX
- Tailwind CSS / daisyUI:UIデザイン・スタイリング
- Hotwire(Turbo / Stimulus):Rails中心でのインタラクティブなUI制御
- Gretel:パンくずリストの実装
フロントエンド関連
- esbuild:JavaScriptのバンドル
- stimulus-autocomplete:修正依頼時の問題指定オートコンプリート
- Tagify:タグ入力・編集のUI改善
データ可視化
- ECharts(rails_charts):マイページでの学習履歴の可視化
外部サービス連携
- Cloudinary:動的OGP画像の生成
- meta-tags:OGP・SEO対応
- Cloudflare:独自ドメイン管理
6. 実装にあたって意識していたこと
6-1. 進め方・時間管理
完璧を求めすぎない
最初から完成度の高いものを目指すのではなく、まずは動く形を作り、後から改善していく方針を取りました。
個人開発では手を広げすぎると止まってしまうため、「今やらないこと」を決めることも意識しました。
Issue作成時にあえて余白を持たせる
計画通りに進まないことを前提に、Issueは少し余裕を持たせて作成しました。
想定より時間がかかった場合は、無理に詰め込まず、次の週以降に回す判断をしています。
他タスク期間をあらかじめ織り込む
開発期間中に他のミニアプリ開発イベントへの参加を考えていたため、最初からNeuroWordに触らない期間をスケジュールに組み込みました。
結果として、焦らずに開発を再開できたと感じています。
週単位での進捗確認
週報を書くことで進捗を可視化し、完了できなかったタスクは次週以降に再配置しました。
また、週の初めに「水曜〜木曜時点でどこまで終わらせるか」を決め、中間地点でのセルフチェックを行っていました。
6-2. 作業効率のために行っていた工夫
よく使うコマンドのエイリアス化
RailsやGitで頻繁に使うコマンドはエイリアスを設定し、日々の作業コストを下げる工夫をしました。
AIツールの活用
実装方針の検討やコードの壁打ち相手として、AI(Claude Code)を適宜活用しました。
そのまま採用するのではなく、理解できたものだけを取り入れることを意識しています。
6-3. コード・設計
早い段階からのコード品質ツールの導入
実装初期からRubocopやBulletを導入し、不適切な書き方やN+1問題を早めに把握できるようにしました。
後から一気に直すのではなく、気づいたタイミングで修正する方針です。
MVP完成後、まずテストを書く
MVP完成後は、機能追加の前にMVP範囲のテストをできる限り書きました。
追加開発の中で「いつの間にか基本機能が動かなくなっていた」という状態を防ぐことを目的としています。
同じ処理を繰り返さない
可読性と保守性を意識し、同じような処理は極力まとめるようにしました。
具体的には、before_actionの活用や、コントローラ間の共通処理をconcernsに切り出す対応を行っています。
また、よく使うActiveRecordの条件はscopeとして定義しました。
ビューの責務を意識する
ビューはパーシャルを活用し、構造が分かりやすくなるよう意識しました。
見た目の調整だけでなく、後から変更しやすい状態を目指しています。
7. 特に力を入れた実装と工夫
NeuroWordの開発においては、単に機能を増やすのではなく、
「学習体験として成立しているか」「後から拡張・改善できるか」を意識しながら実装を進めました。
特に以下の点については、設計や実装の段階で試行錯誤を重ねています。
カード構造・問題構造の設計
NeuroWordの中核となるのが、「起点となる単語」と「関連語」を組み合わせて探すゲーム構造です。
1つの問題の中で、起点語と関連語を柔軟に扱えるよう、最初から1対多の関係を前提としたデータ構造を採用しました。
また、1ゲームあたりの負荷が大きくなりすぎないよう、1つの問題に含める単語数は最大10個までとしています。
カード枚数や関連語の組み合わせを調整しやすい構成にすることで、問題ごとの難易度調整や、今後の拡張もしやすい設計を意識しました。
学習フローを壊さないUI制御
学習アプリという性質上、操作中にストレスを感じさせないことを重視しました。
ゲーム中は、操作の流れが途切れないよう、画面遷移や状態の持たせ方について慎重に検討しています。
例えば、カードの選択状態や進行状況は、主にStimulusを用いてフロント側で管理するようにしました。
「非同期化すること」自体を目的にするのではなく、学習体験を優先した結果として現在のUI制御の形に落ち着いています。
投稿形式・修正依頼という割り切り設計
問題を投稿形式としたのは、特定の作成者がすべての問題を管理する形ではなく、複数人が関われる形にしたいと考えたためです。
投稿形式である以上問題の正確性が常に担保されるとは限らないため、学習用途として安心して使えるよう、ユーザー同士で内容を指摘・補足できる手段として修正依頼機能を用意しました。
結果として、問題を作る・修正点を考える過程そのものが学習内容の整理や理解につながる場面もあり、運用面と学習体験の両立につながっていると感じています。
8. 開発中につまずいた点と学び
開発中、最もつまずいたのは、
【Turbo Streamによる部分更新が、タブUIの状態を壊してしまう問題】です。
何が起きていたのか
当初のマイページでは、
- タブ切り替えは
radio_button_tag+CSSで制御(ブラウザ内で完結のため、都度通信は不要)
※radio_button_tagはあくまで制御のために使用しており、アプリ画面上にラジオボタンは表示されません。この時元にしたdaisyUI#radio tabs-lift + tab contentのHTMLを参照いただくとわかりやすいかと思います。 - お気に入りボタンやリスト操作は Turbo Stream で部分更新(
format.turbo_stream)
という構成になっていました。
この状態でお気に入りボタンを押すと、
Turbo StreamによるDOMの置換が発生し、
ラジオボタンの選択状態がリセットされ、タブが勝手に初期状態へ戻る
という挙動が起きていました。
🌟「タブが勝手に初期状態に戻る」の例
- マイページ以外の画面を開く
- ヘッダーやハンバーガーメニューから、マイページの「作った問題」に進む
→ マイページの「作った問題」タブが開く - マイページ内で、他のタブ(「リスト」「プレイ履歴」)を開く
- 3で開いたタブの中で、いいねボタンを押す
- タブが、2で開いた「作った問題」に戻る
→ \\\\これが、「タブが勝手に初期状態へ戻った」状態!////
当初は「非同期更新が複雑すぎるのでは?」と考えていましたが、
問題の根本は
「ブラウザ側だけで保持しているUI状態」と「サーバー起点のDOM更新」が混在していること
にありました。
解決のため、構成を変更
そこで発想を切り替え、
タブの状態をブラウザ側で無理に保持するのではなく、サーバー側で管理する構成に変更しました。
- タブ切り替えは
link_toによるページ遷移を伴う形に変更
(radio_button_tagの使用を止める) -
params[:tab]を元にコントローラーで現在のタブを決定 - ERBで表示状態を切り替える
タブを切り替える度にページ全体を再読み込みする形にはなりますが、
その分、Turbo Streamによる部分更新と競合せず、タブの状態が安定するようになりました。
リストの作成・編集も同じ判断
リストの作成・編集でも、
Turbo Streamで一部だけ更新しようとした際にモーダルやリスト表示の整合性が崩れる問題がありました。
ここでも、
data: { turbo: false } を指定して通常のHTTP通信に戻し、
redirect_to で画面全体を更新する構成に変更しました。
この経験から得たこと
この経験を通して、
「すべてを非同期にすることが最適とは限らない」
と実感しました。
- Turbo Streamが向いているのは、更新範囲が明確な処理
- UI状態を多く抱える画面では、あえて同期遷移に戻す方がシンプルで安定する
あえて非同期を使わない、という選択の意義を理解できたことが、
今回の大きな学びです。
▼ マイページ周りの実装に詰まっていた頃の記録
9. 今後追加したい機能・展望
NeuroWordは、まず「学習として成立する最小構成」を優先して実装してきました。
今後は、今回の開発で得た学びを踏まえながら、
学習体験の向上と、実装・設計の改善を両立させる方向で機能追加を進めていきたいと考えています。
学習状況に応じた問題の絞り込み
現在は、問題一覧を軸にユーザーが自由に学習を進める形ですが、今後は以下のような視点での絞り込みを検討しています。
- 正答率や挑戦回数に応じた問題の表示制御
- 一定期間で理解度が高い問題を一時的に非表示にする仕組みの拡張
これにより、「分からない問題に集中できる学習体験」をより強化したいと考えています。
学習の継続を後押しする仕組み
学習を続けるモチベーションを高めるため、以下のような要素も段階的に検討しています。
- 問題作成数やプレイ回数に応じたバッジ・称号の付与
- 学習履歴をもとにした簡易的な振り返り表示
機能自体はシンプルでも、「積み重ねが可視化される」ことを重視したいと考えています。
Turbo / Stimulusの理解を深めたうえでの改善
今回の開発では、Hotwireを積極的に取り入れつつも、あえて同期遷移に戻す判断を行う場面が多くありました。
今後は、
- Turbo Frame / Turbo Stream / Stimulus の責務整理
- UI状態とDOM更新範囲の設計を意識した構成
を改めて見直し、非同期を使うべき箇所・使わない箇所を、より意図的に設計できるようになることを目標としています。
実装・設計を改善し続けられるアプリへ
NeuroWordは、完成形を目指すというよりも、「改善し続けられる状態を保つ」ことを大切にしています。
今後も、
- 学習体験としての使いやすさ
- 実装や設計の分かりやすさ・保守性
の両方を意識しながら、少しずつアップデートを重ねていきたいと考えています。
10. おわりに
NeuroWordは、「暗記学習を、もう少し気軽で楽しい体験にできないか」という個人的な課題意識から開発を始めたアプリです。
開発を通して、学習体験を優先した設計判断や、非同期処理との向き合い方など、実装面でも多くの試行錯誤がありました。
まだ改善したい点や理解を深めたい技術は多くあります。
「作って終わり」ではなく、これからはNeuroWordを改善し続けていけることが楽しみです。
今後もNeuroWordを通して、学習体験と実装の両面から試行錯誤を重ねていきたいと思います。








