💧

before_actionの使いすぎ注意!Railsのコントローラ設計を見直す

に公開

はじめに:なぜ「before_action地獄」が起こるのか?

ruby
class UsersController < ApplicationController
  before_action :set_user
  before_action :check_admin, only: [:destroy]
  before_action :authenticate_user!
  before_action :set_posts, only: [:show, :edit]

  def show; end
  def edit; end
  def destroy; end
end

一見整理されているように見えるけど…

  • 各メソッドが何に依存しているか見えにくいです
  • set_user が全アクションで呼ばれるが、不要なアクションもあります
  • check_admin の中で @user に依存していたりします
    → 依存関係がスパゲッティ化しやすいです

before_action の役割を正しく理解してみる

before_action は、「リクエスト前に共通処理を挟む」ための仕組みです。

よく使われる代表例:

  • ログインチェック (authenticate_user!)
  • リソースの取得 (set_user)
  • 権限確認 (check_admin)

これらは便利だが、「全アクション共通」な処理 に限定して使うべきです。

「何でもbefore_action」はアンチパターン

典型的な悪例:

ruby
before_action :set_project
before_action :set_tasks
before_action :set_comments

→ show だけでしか使わないものを共通処理にしてしまうと、
他のアクションで無駄にSQLが走ってしまいます。
しかも、どの変数がどこで使われているかが分かりづらいです。

責務を分離してみよう(リファクタリングのステップ)

ステップ1:アクションごとに明示する

ruby
def show
  @project = Project.find(params[:id])
  @tasks = @project.tasks
end

→ 「showで使う変数はshowの中で完結させる」
→ 可読性が上がる&依存関係が見えます

ステップ2:共通処理が明確ならprivateメソッドで

ruby
before_action :set_project, only: [:show, :edit, :update]

private

def set_project
  @project = Project.find(params[:id])
end

→ 明確な共通点がある場合のみ採用(id依存など)

ステップ3:複雑なロジックはServiceクラスへ移動

ruby
class ProjectAuthorizationService
  def initialize(user, project)
    @user = user
    @project = project
  end

  def can_edit?
    @user.admin? || @project.owner == @user
  end
end

コントローラでは:

ruby
def update
  service = ProjectAuthorizationService.new(current_user, @project)
  unless service.can_edit?
    redirect_to root_path, alert: "権限がありません"
  end
end

→ コントローラがスッキリします。
→ 「チェックの中身」がService層に切り出されて再利用できます。

DRYも大事だがSRP(単一責任の原則)についても考えよう

Railsの哲学「DRY」は強力ですが、
「同じ見た目の処理」=「同じ責務」ではない ことに注意です。

「重複して見える処理」でも、文脈が違うなら別に書いた方がよいです。

それが結果的に 保守性・可読性 を上げます。

Controllerを小さく保つコツ(チェックリスト)

チェック項目 理想的な状態
コントローラの行数 100行未満が望ましい
before_actionの数 2〜3個まで
privateメソッド 各アクションが何を使っているかが明確
複雑な条件分岐 Service/Policyクラスへ移動
JSONレスポンス構築 Serializer / Jbuilderへ委譲

まとめ

before_actionは便利だけど「最小限」にしましょう。
before_actionの正しい使い方

  • 全アクション共通の軽い処理だけに使う
  • 複雑なロジックはServiceやPolicyクラスに切り出す
  • 「見た目の重複」よりも「責務の明確化」を優先する

Discussion