
LMSをゼロから自作した理由と設計思想
この記事のポイント
- ✓既製品のLMSを使わず
- ✓学習管理システムをゼロからNext.js + Express + PostgreSQLで自作した理由と
- ✓設計で重視したポイントを解説します
LMSをゼロから自作した理由と設計思想
「LMSなんて自作する必要はない。既製品を使えばいい」
99%の場合、これは正しいアドバイスです。
Teachable、Thinkific、Udemy。
既製品のLMS(Learning Management System:学習管理システム)は数多くあり、どれも十分に成熟しています。
ところが、私はあえてゼロから自作するという道を選びました。
なぜか。
この記事では、その理由と設計思想を包み隠さず書きます。
「車輪の再発明はするな」という格言を破ってまで自作した、その判断の背景を。
既製品LMSの限界
試したサービスたち
LMSの自作を決断する前に、私は複数の既製品を試しました。
Teachable
月額$39〜$199。
UIは洗練されており、動画コンテンツの配信に強い。
ところが、カスタマイズ性が低いのが致命的でした。
コースの構成、進捗管理のロジック、修了証の発行条件。
これらを自分の仕様に合わせようとすると、すぐに壁にぶつかります。
「この機能はProプランにアップグレードすれば使えます」というアップセルの壁です。
Thinkific
月額$49〜$199。
Teachableと似た機能セットですが、より柔軟なカスタマイズが可能。
ただし、日本語対応が不十分でした。
UIの一部が英語のまま残り、日本語のフォント表示も微妙。
日本のユーザー向けのサービスとしては、ユーザー体験が満足できるレベルではありませんでした。
Moodle(オープンソース)
無料で使えるオープンソースLMS。
機能は豊富ですが、UIが2010年代で止まっているという印象でした。
また、PHPベースで構築されているため、私の技術スタック(Next.js + Express)との親和性が低い。
カスタマイズしようとすると、PHPとMoodleの独自アーキテクチャを深く理解する必要があり、学習コストが高すぎました。
既製品を使わないと決めた3つの理由
1. 月額料金の積み重なり: Teachableの場合、年間$2,388(約36万円)。5年で180万円。自作すれば、サーバー代の月数千円で運用できる 2. ベンダーロックイン: 既製品にコンテンツを蓄積した後、他のサービスに移行するのは非常に困難。自作なら、データは常に自分のPostgreSQLにある 3. 差別化の不可能性: 既製品を使う限り、同じサービスを使っている競合と同じUXになる。独自のユーザー体験を提供したいなら、自作しかない
設計思想:「学習者ファースト」
LMSの設計で最も重要なのは、学習者の体験です。
管理者にとって便利であることよりも、学習者が「学びやすい」「続けやすい」と感じることを最優先にしました。
設計原則1:摩擦を最小にする
学習の最大の敵は摩擦です。
ログインが面倒、次のレッスンへの遷移が複雑、進捗がわかりにくい。
こうした小さな摩擦が積み重なると、学習者は離脱します。
Duolingoのデータによると、学習アプリのユーザーは最初の1週間で70%が離脱するそうです。
この数字を聞いた時、「いかに学習を続けさせるか」がLMSの最大の課題だと確信しました。
私のLMSでは、以下の工夫をしています。
- ログイン不要でサンプルレッスンを閲覧可能: 登録のハードルを下げる
- 前回の続きから自動的に再開: ブラウザを閉じても、次回アクセス時に前回の位置から始まる
- 次のレッスンへのワンクリック遷移: レッスン完了後、ボタン1つで次に進める
- 進捗バーの常時表示: 「あと何%で完了するか」が常にわかる
設計原則2:小さなフィードバックループ
学習の継続には、「できた」という実感が欠かせません。
私のLMSでは、各レッスンの最後に小さなクイズを配置しています。
レッスン視聴(5〜10分)
↓
理解度チェック(3〜5問のクイズ)
↓
正解フィードバック + 次のレッスンへの導線このサイクルを15分以内で1周できるように設計しました。
心理学の研究では、学習のフィードバックは即時であるほど効果的だとされています(Hattie & Timperley, 2007)。
1時間の動画を見た後にまとめてテスト、ではなく、5〜10分の動画ごとにクイズ。
この小さなフィードバックループが、学習の定着率を大幅に向上させます。
設計原則3:進捗の可視化
人間は、ゴールまでの距離がわかるとモチベーションが維持しやすくなります。
これは心理学で『目標勾配効果(Goal Gradient Effect)』と呼ばれています。
ゴールに近づくほど、努力量が増加する現象です。
スタンプカードの例がわかりやすいです。
10個中8個のスタンプが溜まっている人は、2個中0個の人よりも熱心にスタンプを集めようとします。
LMSでも同じ原理を活用しています。
- コース全体の進捗率をパーセンテージと進捗バーで表示
- 完了したレッスンにはチェックマークを表示
- 修了まであと何レッスンかを数値で表示
「あと3レッスンで修了」と表示されると、「もう少しだから頑張ろう」と思えるものです。
技術スタックと全体設計
技術スタック
フロントエンド: Next.js + React + Tailwind CSS
バックエンド: Express.js + TypeScript
データベース: PostgreSQL (Supabase)
認証: Supabase Auth
ストレージ: Supabase Storage(動画・画像)
決済: Stripeすべて、私が日常的に使い慣れている技術です。
LMSのために新しい技術を学ぶ必要はありませんでした。
これは1人開発の大きな利点です。
チーム開発では「このプロジェクトに最適な技術」を選ぶ必要がありますが、1人開発では「自分が最も生産性の高い技術」を選べばいいのです。
データベース設計
LMSの核心は、「誰が、何を、どこまで学習したか」を正確に記録することです。
主要なテーブルは以下の通りです。
-- コース
CREATE TABLE courses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
description TEXT,
price INTEGER NOT NULL DEFAULT 0, -- 0は無料
published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- レッスン
CREATE TABLE lessons (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
course_id UUID REFERENCES courses(id),
title TEXT NOT NULL,
content_type TEXT NOT NULL, -- 'video', 'text', 'quiz'
content_url TEXT,
sort_order INTEGER NOT NULL,
duration_minutes INTEGER,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 学習進捗
CREATE TABLE progress (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id),
lesson_id UUID REFERENCES lessons(id),
completed BOOLEAN DEFAULT FALSE,
completed_at TIMESTAMPTZ,
quiz_score INTEGER,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_id, lesson_id)
);
-- 受講登録
CREATE TABLE enrollments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id),
course_id UUID REFERENCES courses(id),
enrolled_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ,
UNIQUE(user_id, course_id)
);設計で特にこだわった点は、progressテーブルのUNIQUE制約です。
UNIQUE(user_id, lesson_id)により、1人のユーザーが同じレッスンに対して複数のレコードを持つことを防いでいます。
これにより、「進捗の重複」という厄介なバグを根本から排除できます。
動画配信の設計
LMSで最も技術的に難しいのが動画配信です。
以下の要件を満たす必要があります。
1. 大容量ファイルの保存: 1本の動画が数百MBになることも 2. ストリーミング配信: ダウンロード完了を待たずに再生開始 3. アクセス制御: 購入したユーザーだけが視聴できる 4. 不正ダウンロードの防止: URLの直リンクによる不正視聴を防ぐ
これらの要件を満たすために、以下の設計にしました。
動画ファイルはSupabase Storageに保存し、署名付きURL(Signed URL)を使ってアクセス制御を行います。
// 署名付きURLの生成(有効期限: 1時間)
const { data, error } = await supabase.storage
.from('videos')
.createSignedUrl(`courses/${courseId}/${lessonId}.mp4`, 3600);署名付きURLは有効期限付きのため、URLを共有されても一定時間後にアクセスできなくなります。
完璧な不正防止策ではありませんが、1人開発の規模感では十分な対策です。
決済(Stripe連携)
有料コースの決済にはStripeを使っています。
詳しくは別の記事で書いていますが、LMS特有のポイントだけ触れておきます。
1. 一括購入 vs サブスクリプション
私のLMSでは、コースごとの一括購入を採用しました。
サブスクリプションモデルも検討しましたが、コンテンツ量が限られている段階では「月額料金を払い続ける価値があるか?」という不安を学習者に与えてしまいます。
一括購入なら、「このコースに○○円を払う」という判断だけで済むため、購入のハードルが低いのです。
2. 返金ポリシー
Stripeでは最大120日間の返金が可能です。
私のLMSでは30日間の返金保証を設けています。
返金保証があることで、購入者の心理的ハードルが下がり、コンバージョン率が約20%向上しました。
実際に返金を請求されたのは、全購入の3%以下です。
返金保証は「損失」ではなく「投資」です。
開発期間と工数
LMSの開発にかかった期間と工数をまとめます。
| フェーズ | 期間 | 主な作業 |
|---|---|---|
| 設計 | 1週間 | DB設計、UI設計、機能要件定義 |
| コース管理(管理者側) | 2週間 | コースCRUD、レッスン管理、並び替え |
| 学習画面(受講者側) | 2週間 | 動画プレイヤー、進捗管理、クイズ |
| 決済(Stripe連携) | 1週間 | 購入フロー、Webhook、返金処理 |
| 認証・権限管理 | 1週間 | Supabase Auth、RLS設定 |
| テスト・バグ修正 | 1週間 | E2Eテスト、エッジケース対応 |
| 合計 | 約8週間 |
8週間、1人で。
既製品を使えば1日でセットアップできることを考えると、客観的に見れば非効率です。
ところが、8週間の投資で得られたものは大きいです。
- 月額料金ゼロ(年間36万円の節約)
- 完全なカスタマイズ自由
- データの完全所有
- 技術的な学び
5年間の運用を考えれば、既製品の総コスト180万円に対して、自作のコストは開発時間 + サーバー代(年間数万円)。
長期的には、自作のほうが圧倒的にコストパフォーマンスが高いのです。
自作して良かったこと、後悔していること
良かったこと
1. ユーザー体験を完全にコントロールできる
既製品では「あとちょっとこうしたい」ができません。
自作なら、ユーザーからのフィードバックを受けて翌日にはUIを改善できます。
このスピード感は、既製品では絶対に得られません。
2. 他のサービスとのシームレスな連携
@SOHOのユーザーデータとLMSのユーザーデータを統合し、@SOHOの登録者にLMSの案内を送る、といった連携が簡単にできます。
異なるプラットフォームのデータを連携させるのは、通常は非常に面倒です。
自作なら、同じデータベース内でJOINするだけです。
後悔していること
1. 動画のトランスコーディング
動画のフォーマット変換(トランスコーディング)を自前で実装しようとして、2週間を無駄にしました。
結局、この部分はCloudflare Stream(月額$5〜)を使うことにしました。
動画のトランスコーディングは、1人で実装するには複雑すぎる領域です。
すべてを自作する必要はない。 苦手な部分は既存のサービスに任せる。
この判断ができるまでに、無駄な2週間を過ごしてしまいました。
読者の皆さんへ
LMSの自作を検討している方に、正直にお伝えします。
ほとんどの場合、既製品を使うべきです。
自作が正当化されるのは、以下の条件をすべて満たす場合だけです。
1. 既製品では実現できない独自の要件がある 2. 長期的(3年以上)に運用する予定がある 3. 技術力があり、1人で開発・保守できる 4. 既製品の月額料金が長期的に見てコスト高になる
これらの条件を満たさないなら、TeachableやThinkificを使ったほうが、はるかに早く、安く、確実です。
「作ること」自体が目的になってはいけません。
「ユーザーに価値を届けること」が目的であり、自作はその手段の一つに過ぎないのです。
それでも自作したいと思ったなら。
その情熱は本物です。
8週間の開発期間を楽しんでください。
この記事が、LMS開発の判断材料になれば幸いです。

