目次
はじめに
こんにちは、吉田です。
LaravelとVue.jsを用いて、SPA(Single Page Application)の学習ルート共有サービス 「TADORI(たどり)」 を開発しました。
私は約20年間、自動車向け組込みソフトウェアの開発に携わってきました。その後、Webエンジニアへの転向を目指してLaravelやVue.jsを中心に学習を続け、2026年3月には念願だったフリーランスとして独立し、現在はWebエンジニアとして初案件に従事しております。
ポートフォリオを制作するにあたり、単なるCRUDアプリではなく、実際のWebサービスを意識した設計・機能を備えたサービスを作りたいと考え、本サービスを開発しました。
この記事では、
- サービス概要
- 開発した背景
- 主な機能
- 使用技術
- システム構成
について紹介します。
開発概要
| 項目 | 内容 |
|---|---|
| 開発期間 | 約3か月 |
| 開発人数 | 1人(個人開発) |
| 開発形態 | SPA(Laravel + Vue.js) |
| 主要ソースコード | 約17,000行(PHP・Vue・SCSS) |
| Vueコンポーネント | 約50コンポーネント |
| API | 約35本 |
| Controller | 約16クラス |
| Service | 3クラス |
| Model | 7クラス |
| Migration | 12本 |
| テーブル数 | 9テーブル |
ソースコードはGitHubで公開しており、本記事では設計や実装で工夫した点についても紹介します。
本サービスは、LaravelとVue.jsを用いたSPA開発のポートフォリオとして制作しました。
単なるCRUDアプリではなく、認証・状態管理・REST API・ER設計・責務分離など、実務を意識した構成となるよう設計・実装しています。
サービス概要
TADORI
あなたがたどった学びを、次の人へ
「TADORI」は、資格試験やプログラミング、語学など、自分が歩んできた学習ルートを共有できるWebサービスです。
同じ目標を目指していても、そこへたどり着くまでの学び方は人それぞれです。
例えばプログラミングを学ぶ場合でも、
- Progateから始める人
- Udemyで体系的に学ぶ人
- 書籍を読みながら進める人
- 実際にアプリを作りながら習得する人
など、学習スタイルはさまざまです。
「TADORI」では、
- どの教材を使ったのか
- どの順番で学習したのか
- どこで苦労したのか
- どのように乗り越えたのか
といった、学習の過程そのものを投稿・共有できます。
これから新しいことに挑戦する人が、自分に合った学習ルートを見つけられるサービスを目指しています。
なぜ作ったか
資格試験やスキル習得では、「合格」「習得」「上達」といったゴールは同じでも、そこへ至るまでの道のりは人によって大きく異なります。
私自身、これまで
- プログラミング
- 英語
- FP
- 漢字検定
- 剣道
など、さまざまなことを学んできました。
その中で強く感じたのは、
「何を学んだか」だけではなく、「どう学んだか」にこそ価値がある
ということです。
インターネットには教材紹介や勉強法の記事は数多くあります。
しかし、
- どの教材をどの順番で使ったのか
- どのくらいの期間取り組んだのか
- どこでつまずいたのか
- 今振り返ると何がおすすめなのか
までまとめられている情報は、それほど多くないようにみえました。
自分が遠回りして得た経験が、これから挑戦する誰かの近道になる。
そんなサービスがあれば、多くの人の学習を後押しできるのではないかと思い、「TADORI」を開発しました。
主な機能
まずは、本サービスの利用イメージをご紹介します。
学習者の利用フロー
投稿者の利用フロー
本サービスは、単に学習ルートを閲覧するだけではなく、実際に学習を進め、その経験を次の学習者へ共有することをコンセプトとしています。
学習ルート投稿
ユーザーは、自分が実際に経験した学習ルートを投稿できます。
タイトルやカテゴリだけではなく、学習期間や概要、各ステップの詳細まで登録できるため、自分だけの学習記録として残すことができます。
「この順番で学んだから理解しやすかった」という経験を、そのまま他のユーザーへ共有できることが本サービスの特徴です。
ステップ・サブステップ管理
1つの学習ルートは複数のステップで構成されます。
さらに各ステップにはサブステップを登録できるため、
- 書籍を読む
- 動画を視聴する
- 実際にアプリを作る
- 問題集を解く
など、細かな学習手順まで整理できます。
学習全体を可視化することで、「次に何をすればよいか」が分かりやすい構成を目指しました。
学習ルート検索・閲覧
投稿された学習ルートは一覧画面から自由に閲覧できます。
カテゴリごとの絞り込みにも対応しており、自分が学びたい分野の学習ルートを効率よく探すことができます。
チャレンジ機能・学習進捗管理
参考になった学習ルートは「チャレンジ」として登録できます。
チャレンジ後は、各ステップ・サブステップごとに達成状況を管理できるため、現在どこまで学習が進んでいるかを確認しながら取り組めます。
お気に入り機能
気になった学習ルートはお気に入り登録できます。
あとから見返したい学習ルートを保存できるため、自分専用のブックマークとして利用できます。
マイページ
マイページでは、自分の学習状況をまとめて確認できます。
以下の情報をそれぞれ最大5件まで表示し、「全件表示」から一覧画面へ遷移できます。
- 投稿した学習ルート
- チャレンジ中の学習ルート
- お気に入り登録した学習ルート
日々の学習状況をひと目で確認できるダッシュボードとして設計しました。
ユーザー管理機能
一般的なWebサービスとして必要な機能も実装しています。
- ユーザー登録
- ログイン・ログアウト
- プロフィール編集
- パスワード変更
- パスワードリマインダー
認証にはJWTを採用し、SPAでもログイン状態を維持できるようにしています。
管理者機能
管理者専用画面を用意しており、現在は学習カテゴリの登録・編集・削除を行えるようにしています。
今後は投稿管理やユーザー管理など、運用を想定した機能も追加していく予定です。
使用技術
今回の開発では、フロントエンドとバックエンドを分離したSPA構成を採用しました。
| 分類 | 使用技術 |
|---|---|
| フロントエンド | Vue.js 2 |
| 状態管理 | Vuex |
| ルーティング | Vue Router |
| HTTP通信 | Axios |
| バックエンド | Laravel 5.8 |
| 言語 | PHP 7 |
| 認証 | JWT Authentication |
| データベース | MySQL |
| CSS | SCSS(FLOCSS設計) |
| 画像処理 | Intervention Image |
| 開発環境 | MAMP |
| インフラ | エックスサーバ |
| バージョン管理 | Git / GitHub |
フロントエンドはVue.jsによるSPAとして構築し、LaravelとはREST APIで通信しています。
状態管理にはVuexを採用し、画面遷移にはVue Routerを利用しています。
また、認証にはJWTを採用し、SPAでもログイン状態を維持できるようにしました。
CSSはSCSSとFLOCSSを用いて設計し、保守性を意識したディレクトリ構成としています。
システム構成
- フロントエンドはVue.jsによるSPAとして構築し、画面描画や状態管理を担当しています
- バックエンドはLaravelでREST APIを提供し、認証やデータの登録・更新・検索などを担当しています
- また、Controllerへ処理を集中させないよう、一部のビジネスロジックはServiceクラスへ切り出しています
- 認証・認可についてはJWT認証やPolicy、管理者用Middlewareを利用し、役割ごとのアクセス制御を行っています
ER図
ER図は、データの正規化を意識して設計しました。
各テーブルの役割を明確に分離し、データの重複を避けることで、更新時の整合性や保守性を高めています。
また、「投稿」「チャレンジ」「お気に入り」「進捗管理」をそれぞれ独立したテーブルとして管理することで、機能追加にも対応しやすい構成としています。
ディレクトリ構成図
app
├── Constants
│ ├── DefaultIcon.php
│ ├── DefaultStepImg.php
│ ├── EmailVerificationStatus.php
│ └── UserType.php
│
├── Http
│ ├── Controllers
│ │ ├── Auth
│ │ ├── StepsController.php
│ │ ├── SubStepsController.php
│ │ ├── ChallengesController.php
│ │ ├── FavoritesController.php
│ │ ├── CategoriesController.php
│ │ └── UsersController.php
│ │
│ ├── Middleware
│ │ └── AdminOnly.php
│ │
│ └── Requests
│
├── Policies
│
├── Service
│ ├── GuestApi.php
│ ├── ImageUploadService.php
│ └── StepSearchService.php
│
├── Category.php
├── Challenge.php
├── Favorite.php
├── Progress.php
├── Step.php
├── SubStep.php
└── User.php
Controllerに処理を集中させないよう、画像アップロードや検索処理などのビジネスロジックはServiceクラスへ切り出しました。また、認可にはPolicy、入力値検証にはFormRequestを利用し、役割ごとに責務を分離しています。
開発で工夫した点
フロントエンドとバックエンドを完全に分離したSPA構成
本サービスでは、LaravelをREST API、Vue.jsをフロントエンドとして構築し、SPA(Single Page Application)として実装しました。
画面遷移にはVue Router、API通信にはAxiosを採用し、ページ全体をリロードすることなく快適に操作できるようにしています。
また、状態管理にはVuexを利用し、ログインユーザー情報やカテゴリ一覧など複数画面で利用するデータを一元管理しました。
Controllerへ処理を集中させない設計
Controllerが肥大化しないよう、画像アップロードや検索処理などのビジネスロジックはServiceクラスへ切り出しました。
- ImageUploadService
- StepSearchService
などを作成し、責務を分離することで保守性を向上させています。
また、入力チェックにはFormRequest、認可にはPolicy、管理者機能にはMiddlewareを利用し、それぞれの役割を明確にしました。
Axiosの共通処理を実装
API通信では、各コンポーネントで同じ処理を書かないようAxiosの共通処理を実装しました。
レスポンスエラー時のフラッシュメッセージ表示などを共通化することで、コードの重複を削減しています。
学習サービスとして利用しやすいデータ構造
単純な投稿サービスではなく、
- 学習ルート投稿
- チャレンジ開始
- サブステップごとの進捗管理
- お気に入り登録
をそれぞれ独立したテーブルとして設計しました。
「投稿する」だけではなく、「実際に学習を進める」ことまで考慮したデータ構造を意識しています。
苦労した点
今回の開発では、LaravelとVue.jsを用いたSPAならではの課題に数多く直面しました。
エラーが発生するたびに原因を調査し、検証を繰り返しながら一つずつ解決していきました。
SPAにおける認証・状態管理
最も苦労したのは認証機能です。
Laravel標準の認証はBladeを前提としているため、そのままではSPAへ適用できませんでした。
そのため、
- JWT認証
- Vuexによるログイン状態の管理
- Router Guardによるアクセス制御
- メール認証
- パスワードリセット
などをSPA向けに一つずつ実装しました。
特に、画面リロード時のログイン状態保持や非同期処理のタイミングによる不具合には何度も悩まされ、最終的にはVuexやasync/awaitを活用して安定した動作を実現しました。
Vue.jsのリアクティブシステムへの理解
Vue.jsではデータが更新されても画面が切り替わらない問題に何度も遭遇しました。
原因を調査すると、
- watchしていない
- Vuexの更新を検知できていない
- propsとローカル変数の使い分け
など、Vueのリアクティブシステムへの理解不足が原因であることが多くありました。
お気に入り一覧や学習一覧、マイページなどもこの問題に直面しましたが、状態管理の考え方を見直すことで解決できました。
REST API設計
フロントエンドとバックエンドを完全に分離したため、REST APIの設計にも苦労しました。
HTTPステータスコードやバリデーションエラーの返却形式を統一し、Axios側で共通処理を実装することで、各画面で同じ処理を書かなくて済むよう改善しました。
また、Interceptorを利用して認証エラーやフラッシュメッセージの処理も共通化し、保守性を高めています。
データベース設計
データベース設計では、データの重複を避けることを意識しました。
「投稿」「チャレンジ」「お気に入り」「進捗管理」をそれぞれ独立したテーブルとして設計し、正規化を意識することで保守性・拡張性の高い構成を目指しました。
また、Eloquentのリレーション設計や複雑なJOINでは想定外のデータ取得に苦労する場面もあり、クエリの見直しを重ねながら改善しました。
コンポーネント設計と責務分離
画面数やコンポーネント数が増えるにつれ、責務の分離にも苦労しました。
Controllerへ処理を書きすぎないよう、
- Service
- Policy
- FormRequest
- Middleware
へ責務を分離し、保守しやすい構成を目指しました。
Vue側でも共通コンポーネント化を進め、再利用性を意識した設計を心掛けています。
画像アップロード
画像アップロード機能では、
- FormData
- Intervention Image
- Storage
- プレビュー表示
を組み合わせて実装しました。
特に、画像削除後に同じ画像を再選択するとchangeイベントが発火しない問題では原因の特定に時間がかかりましたが、refを利用してinput要素をリセットすることで解決しました。
エラーを一つずつ解決した経験
開発中は、
- npmの依存関係によるビルドエラー
- 相対パス指定によるAPI通信エラー
- 論理削除後にログインできない問題
- Doctrine DBALのバージョン問題
- EloquentのJOINによる重複取得
など、多くのトラブルに直面しました。
原因を一つずつ調査・検証しながら解決したことで、単に機能を実装するだけではなく、「なぜそのエラーが発生したのか」を考えながら開発する姿勢を身に付けることができました。
今後追加したい機能
今後は、より実際のサービスとして利用できるよう、以下の機能追加を検討しています。
- コメント機能
- STEPのレビュー・評価機能
- SNSログイン(Google・GitHubなど)
- 通知機能
- 学習履歴の可視化(グラフ表示)
- タグ検索・複合検索
- 学習ルートのランキング機能
- フォロー機能
- 学習ルートの共有機能
- PHPUnit・Jestによるテストコードの追加
また、現在は個人開発として実装していますが、継続的に改善を重ねながら、保守性・拡張性・セキュリティ面もさらに強化していきたいと考えています。
GitHub・デモ
GitHub
- GitHubはこちらです。
デモ
- 実際のデモ画面はこちらです。
まとめ
最後までご覧いただき、ありがとうございました。
今回の開発では、単にCRUD機能を実装するだけではなく、実際のWebサービスを意識しながら設計・開発を行いました。
特に、
- LaravelとVue.jsを用いたSPA構成
- JWT認証によるログイン機能
- Vuexを利用した状態管理
- Serviceクラスによる責務の分離
- 学習ルート・チャレンジ・進捗管理を考慮したデータ設計
など、保守性や拡張性を意識した実装に取り組めたことは、実際の開発現場でも活きる経験だと考えております。
また、開発を通して、SPA特有の認証や状態管理、API設計など、実務でも重要となる知識や考え方を数多く学ぶことができました。
もちろん、まだ改善したい点や追加したい機能も多くありますが、今後も継続してブラッシュアップしながら、より実用的なサービスへ成長させていきたいと考えています。
本記事が、Laravel・Vue.jsでSPA開発を学習している方や、個人開発に取り組んでいる方の参考になれば幸いです。
ご意見・ご感想・改善案などがありましたら、ぜひコメントで教えていただけると嬉しいです。
今回の開発を通して、「動くものを作る」だけではなく、保守性や責務の分離を意識した設計の重要性を改めて実感しました。今後も継続的に改善を重ねながら、より実践的なWebサービス開発に取り組んでいきたいと思います。











