第0章|今さら学ぶ「全体像」
📚 シリーズ目次はこちら → 「今さら学ぶ」シリーズ — はじめに
この章でわかること
- Webアプリが動く全体の仕組み(リクエスト → レスポンスの流れ)
- 「ブラウザでURLを打ってからページが表示されるまで」に何が起きているか
- MVCとは何か — なぜ「分ける」ことが大事なのか(技術的な定義)
- MVC・ルーティング・DBがどう連携しているか
- エラーメッセージの読み方 — 「どこで → 何が → なぜ」の3ステップ
🏠 たとえ話で掴む「Webアプリの全体像」
Webアプリの仕組みは、 宅配ピザの注文 にたとえると整理しやすい。
スマホでピザを注文する場面を思い浮かべてほしい。
| 宅配ピザ | Webアプリ |
|---|---|
| スマホの注文アプリ | ブラウザ (Chrome等) |
| 「マルゲリータ1枚」と注文 | リクエスト (GET /articles/1) |
| コールセンター(注文の振り分け) | ルーティング |
| 店長(指示を出す人) | コントローラ |
| ピザ職人 + 材料棚 | モデル + データベース |
| ピザを箱に入れて梱包 | ビュー(HTML) |
| バイクで届く | レスポンス |
流れはこうなる。
- スマホで注文する → ブラウザがURLにアクセスする(リクエスト)
- コールセンターが受け付ける → ルーティングが「どのコントローラのどのアクションか」を振り分ける
- 店長がピザ職人に伝える → コントローラがモデルにデータを要求する
- ピザ職人が材料棚から取り出して焼く → モデルがDBからデータを取得して加工する
- ピザを箱に詰める → ビューがHTMLを組み立てる
- バイクでお届け → ブラウザにレスポンスが返る
これがWebアプリの「リクエスト → レスポンス」の全体像です。Railsの学習で触れてきたことは全部、この流れのどこかに当てはまります。
🏗️ MVCとは何か — 技術的な定義
MVCはスクールで必ず出てくるが、面接で「MVCの各層の責務を説明してください」と聞かれると意外と詰まる。自分もそうでした。改めて整理してみると、ポイントは 「責務の分離」 でした。
宅配ピザのたとえで「店長」「ピザ職人」「梱包担当」と役割が分かれていたように、この 「役割を分ける」設計の考え方 を、技術的には MVC(Model-View-Controller)パターン と呼びます。
MVCは1970年代にSmalltalkというプログラミング言語の中で生まれた、歴史のある デザインパターン(設計の定石) です。では、なぜ「分ける」必要があるのか。
もしMVCがなかったら
# ❌ MVCがない世界 — 1つのファイルに全部入り
# (実際にこう書くわけではないが、イメージとして)
if request.path == "/articles/1"
# --- DB接続 ---
db = PG.connect(dbname: "knowledgenote")
result = db.exec("SELECT * FROM articles WHERE id = 1")
article = result.first
# --- HTML組み立て ---
html = "<html><body>"
html += "<h1>#{article['title']}</h1>"
html += "<p>#{article['body']}</p>"
html += "</body></html>"
# --- レスポンス ---
[200, { "Content-Type" => "text/html" }, [html]]
end
たった1ページ分のコードで、もう問題が見えてくる。
- タイトルの表示を変えたいだけなのに、DB接続のコードと同じファイルを触る必要がある
- 「記事一覧」ページを追加するとき、DB接続のコードをコピペすることになる
- ファイルが1,000行を超えたら、どこに何があるか誰もわからなくなる
つまり 変更のたびに関係ない部分まで壊すリスクがある。これが「全部入り」の最大の問題です。
MVCの3つの層 — 「責務の分離」
MVCは、この問題を 「責務の分離(Separation of Concerns)」 で解決する。プログラムを3つの層に分けて、それぞれに明確な役割を持たせます。
| 層 | 正式な責務 | やること | やらないこと |
|---|---|---|---|
| Model (モデル) | ビジネスロジックとデータの永続化 | データの取得・保存・加工・バリデーション | HTMLの組み立て、リクエストの受付 |
| View (ビュー) | ユーザーへの情報の表示 | HTMLの組み立て、データの見せ方の整形 | DB問い合わせ、ビジネスロジック |
| Controller (コントローラ) | リクエストの受付と橋渡し | リクエストを受け取り、Modelに頼み、Viewに渡す | 自分でデータを加工する、自分でHTMLを組み立てる |
大事なのは 「やらないこと」 の列です。各層は自分の責務以外のことをしない。この制約があるからこそ、「デザインを変えたい → Viewだけ触ればいい」「バリデーションを追加したい → Modelだけ触ればいい」と、変更の影響範囲を限定できます。
なぜMVCが面接で必ず聞かれるのか
MVCの本当の価値は、 「変更に強いコードになる」 ことです。
Webアプリは完成したら終わりではありません。機能追加、デザイン変更、バグ修正…実務では日々変更が入ります。MVCで責務を分けてあれば、変更を局所化できる。だからチーム開発では必須の考え方とされています。
逆にMVCを守らず、コントローラにビジネスロジックを詰め込んでしまう状態を Fat Controller(太ったコントローラ) と呼びます。
# ❌ Fat Controller — コントローラがやりすぎ
class ArticlesController < ApplicationController
def index
@articles = Article.where(status: "published")
.where("created_at > ?", 30.days.ago)
.order(likes_count: :desc)
# ↑ 本来はModelの仕事(どの記事を「人気」とするかはビジネスロジック)
end
end
# ✅ Modelに責務を移す
class Article < ApplicationRecord
scope :popular_recent, -> {
where(status: "published")
.where("created_at > ?", 30.days.ago)
.order(likes_count: :desc)
}
end
class ArticlesController < ApplicationController
def index
@articles = Article.popular_recent # コントローラはModelに頼むだけ
end
end
Fat Controllerは実務で最もよく見かけるアンチパターンの1つです。「MVCを知っている」と「MVCを守れる」は別の話で、この違いがわかっていると、コードレビューでも的確に指摘できるようになります(→ 第25章で詳しく扱います)。
🔄 もう少し技術的に:リクエストからレスポンスまでの旅
宅配ピザのたとえとMVCの定義を踏まえて、Railsアプリの実際の流れを追ってみます。
KnowledgeNoteで「記事の詳細ページを開く」場合の例です。
ステップ1:ブラウザがリクエストを送る
ユーザーが記事のリンクをクリックすると、ブラウザは次のようなリクエストをサーバに送ります。
GET /articles/1 HTTP/1.1
Host: knowledgenote.example.com
これは「/articles/1 のページをちょうだい(GET)」という意味です。HTTPメソッド(GET / POST / PATCH / DELETE)については第6章で詳しく扱いますが、ここでは「GETは『見せて』という要求」程度の理解で大丈夫です。
ステップ2:ルーティングが振り分ける
リクエストがRailsサーバに届くと、最初に仕事をするのが ルーティング です。
# config/routes.rb
resources :articles
この1行で、Railsは「GET /articles/1 は ArticlesController の show アクションだな」と自動で判断します。宅配ピザのコールセンターが「マルゲリータ → 渋谷店のピザ担当へ」と振り分けるのと同じ役割です(→ 第10章で詳しく扱います)。
ステップ3:コントローラが指示を出す
ルーティングに指名されたコントローラが動きます。
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id]) # モデルにデータを要求
end
end
コントローラは自分でピザは焼かない。 「id=1の記事を持ってきて」とモデルに頼む のが仕事です。宅配ピザでいう店長と同じで、自分で生地をこねることはしません。
ステップ4:モデルがDBからデータを取得する
モデル(Article)がデータベースに問い合わせます。
# 裏側で実行されるSQL
SELECT * FROM articles WHERE id = 1;
データベースは「材料棚」です。必要な材料(データ)を取り出して、モデルが受け取る。モデルはただ取り出すだけでなく、 バリデーション(材料の品質チェック) や アソシエーション(関連する材料も一緒に取り出す) も担当しています(→ 第12章、第15章で詳しく扱います)。
ステップ5:ビューがHTMLを組み立てる
コントローラが受け取ったデータを、ビューに渡します。
<!-- app/views/articles/show.html.erb -->
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
<span>by <%= @article.user.name %></span>
ビューは「梱包担当」です。データをHTMLという「ピザ箱」にきれいに詰める。<%= %> の中にRubyのコードを書けるのがERBの特徴です(→ 第13章で詳しく扱います)。
ステップ6:レスポンスがブラウザに届く
完成したHTMLがブラウザに届き、ユーザーの画面に記事が表示されます。
HTTP/1.1 200 OK
Content-Type: text/html
<html>
<body>
<h1>Railsのルーティングを完全理解する</h1>
...
</body>
</html>
ここまでの所要時間は、だいたい 数十ミリ秒〜数百ミリ秒。一瞬で「注文→調理→配達」が完了しています。
📍 全体像マップ:Railsで学んだことの「位置」
ここまでの流れを図にすると、こうなります。
ブラウザ Railsサーバ
┌──────┐ リクエスト ┌────────────────────────────────┐
│ │ ────────────> │ ルーティング(config/routes.rb)│
│ │ │ │ │
│ │ │ ▼ │
│ │ │ コントローラ(app/controllers/)│
│ │ │ ↓依頼 ↑データ受取 │
│ │ │ モデル(app/models/) │
│ │ │ ↓SQL照会 ↑結果返却 │
│ │ │ データベース(SQLite/PostgreSQL)│
│ │ │ │ │
│ │ │ ▼(コントローラ経由) │
│ │ レスポンス │ ビュー(app/views/) │
│ │ <──────────── │ │
└──────┘ └────────────────────────────────┘
Rails学習で触れてきた内容は、すべてこの図のどこかに位置しています。
| 図の位置 | やったこと | このシリーズでの章 |
|---|---|---|
| ルーティング |
resources でURL自動生成 |
第10章 |
| コントローラ |
before_action でアクセス制限 |
第14章 |
| モデル |
has_many で関連付け |
第15章 |
| データベース | マイグレーションでテーブル作成 | 第16,18章 |
| ビュー | ERBでデータを画面に表示 | 第13章 |
| リクエスト全般 | Turbo Streamsで部分更新 | 第19章 |
「あの時やったあれ、全体のここだったのか」と繋がれば、この章の役割は果たせています。
🔍 「遅い」「エラー」のとき、どこを疑うか
全体の流れがわかっていると、 トラブルシューティングが格段に楽になります。
問題が起きたとき、「宅配ピザのどの工程で詰まっているか」で切り分けられる。
パターン①:ページが表示されない(エラー)
ルーティングエラー → 注文が通っていない → routes.rbを確認
例:No route matches [GET] "/artcles/1"(スペルミス)
コントローラエラー → 店長が混乱 → コントローラを確認
例:NameError: undefined local variable 'artcle'
モデルエラー → 材料が見つからない → モデル/DBを確認
例:ActiveRecord::RecordNotFound(id=999の記事が存在しない)
ビューエラー → 梱包に失敗 → ビューテンプレートを確認
例:NoMethodError: undefined method 'name' for nil:NilClass
パターン②:ページは出るけど遅い
DBが遅い → 材料棚が散らかっている
→ インデックスが足りない?N+1問題?(→ 第17章)
コントローラが遅い → 店長が余計な仕事をしている
→ 不要なクエリを投げていないか?(→ 第24章)
ビューが遅い → 梱包が複雑すぎる
→ パーシャルのループが多すぎないか?(→ 第24章)
実践:「nilのエラー」を3ステップで切り分ける
一覧だけだとピンとこないので、実際に一番よく遭遇するエラーで切り分けてみる。
ブラウザに赤い画面が出て、こんなエラーメッセージが表示されたとします。
NoMethodError: undefined method 'name' for nil:NilClass
慌てず、3ステップで追いかけます。
① どこで起きたか?(スタックトレースを見る)
エラー画面の下の方に、こんな行が並んでいます。これが スタックトレース(呼び出し履歴) です。
app/views/articles/show.html.erb:3:in `_app_views_articles_show_html_erb__...`
app/controllers/articles_controller.rb:5:in `show`
一番上の行が「エラーが実際に起きた場所」です。show.html.erb の3行目 — つまり ビュー(梱包の工程)で起きた とわかります。
② 何がnilか?(エラーメッセージを読む)
「nil に対して name メソッドを呼んだ」と言っています。3行目を見ると:
<span>by <%= @article.user.name %></span>
@article.user が nil — つまり 「記事に紐づくユーザーがいない」 ということです。
③ なぜnilか?(原因を探る)
ここで考えられる原因はいくつかある。
-
articlesテーブルのuser_idに存在しないIDが入っていた(外部キー制約の未設定) - ユーザーが退会したのにその人の記事が残っていた(
dependent: :destroyの未設定) - そもそも
user_idがnilだった(バリデーションの漏れ)
このように 「どこで → 何が → なぜ」 の順で追いかけると、エラーの原因に最短でたどり着けます(→ 第23章で詳しく扱います)。
💼 面接で聞かれたら?
Q:RailsのMVCについて説明してください。
「MVCはModel・View・Controllerの3つの層に責務を分けるデザインパターンです。Modelはビジネスロジックとデータの永続化、Viewはユーザーへの情報の表示、Controllerはリクエストの受付とModelへの指示・Viewへのデータの受け渡しを担当します。各層の責務を分離することで、変更の影響範囲を限定できます。たとえばデザインを変えたいときはViewだけ、バリデーションを追加したいときはModelだけを修正すればよく、他の層に影響が出にくくなります。」
深掘りされたら:
- 「なぜ分けるのか?」→ 変更の影響範囲を限定するため。1つのファイルにすべてを書くと、画面修正のつもりでDB処理まで壊すリスクがある。「責務の分離(Separation of Concerns)」という設計原則にもとづいている。
- 「Fat Controllerとは?」→ Controllerにビジネスロジックを詰め込みすぎた状態。本来Modelがやるべきデータの加工や判定をControllerに書いてしまうこと。MVCの恩恵を自ら捨てているアンチパターン。
🔗 もっと深く知りたい人へ(1次情報リンク)
- Rails ガイド:Rails をはじめよう — リクエスト〜レスポンスの流れを手を動かしながら確認できる
-
Rails ガイド:Rails のルーティング —
resourcesが生むルートの全体像 - MDN Web Docs:HTTP の概要 — HTTPリクエスト/レスポンスの仕組みを基礎から解説
まとめ
- ✅ Webアプリは「リクエスト → ルーティング → コントローラ → モデル → DB → ビュー → レスポンス」の流れで動く
- ✅ MVCは「責務の分離」のデザインパターン。Model(データとロジック)・View(表示)・Controller(橋渡し)に分けることで、変更の影響範囲を限定する
- ✅ MVCがない世界では、1つの変更が全体を壊すリスクがある。だから分ける
- ✅ Fat Controllerは、Controllerにビジネスロジックを詰め込んだアンチパターン
- ✅ エラーは「どこで → 何が → なぜ」の3ステップで追いかけると最短で原因にたどり着ける
📚 シリーズ目次:「今さら学ぶ」シリーズ — はじめに