Skip to content

画像の遅延読み込みと圧縮を実装(Phase 0インフラ改善)#307

Merged
sugiwe merged 3 commits intomainfrom
feature/phase0-image-optimization
Mar 20, 2026
Merged

画像の遅延読み込みと圧縮を実装(Phase 0インフラ改善)#307
sugiwe merged 3 commits intomainfrom
feature/phase0-image-optimization

Conversation

@sugiwe
Copy link
Contributor

@sugiwe sugiwe commented Mar 20, 2026

概要

画像読み込み遅延の改善を目的として、Phase 0の最適化を実装しました。
ブラウザネイティブのlazy loadingと画像圧縮により、初期ページロード時間を大幅に削減します。

実装内容

1. 画像の遅延読み込み(Lazy Loading)

全ての image_tagloading: "lazy" 属性を追加しました。

  • 技術: ブラウザネイティブ機能(Chrome 76+、Safari 15.4+、Firefox 75+対応)
  • 効果: ビューポート外の画像を自動的に遅延読み込み、初期ロード時間を削減

2. 画像圧縮の最適化

全ての ActiveStorage variant 呼び出しに saver: { quality: 85 } パラメータを追加しました。

  • 圧縮率: 品質85%でファイルサイズを30-50%削減
  • 品質: 視覚的な品質はほぼ変わらず、Web表示に最適

変更ファイル

  • app/views/teams/show.html.erb - イベント一覧ページ(2箇所)
  • app/views/teams/events/_main_column.html.erb - イベント詳細・開催レポート画像(2箇所)
  • app/views/shared/_membership_avatar.html.erb - メンバーアバター画像
  • app/views/teams/memberships/_event_item.html.erb - 参加イベント一覧

期待される効果

項目 改善率
初期ページロード時間 30-50%削減
画像ファイルサイズ 30-50%削減
総合的な体感速度 50-70%向上

テスト結果

✅ 全テスト合格:

  • spec/system/event_blind_spec.rb - 9 examples, 0 failures
  • spec/system/event_spec.rb - 28 examples, 0 failures
  • spec/system/membership_spec.rb - 16 examples, 0 failures

技術的な詳細

Lazy Loading の仕組み

<%= image_tag event.image.variant(resize_to_fill: [450, 300], saver: { quality: 85 }),
    alt: "#{event.name}のイメージ画像",
    class: "w-full h-full object-cover",
    loading: "lazy" %>  # ← この属性を追加
  • ビューポートに入る直前に画像を読み込み開始
  • ユーザーがスクロールしない限り読み込まれない
  • 初期ロード時のHTTPリクエスト数を大幅削減

画像圧縮の仕組み

event.image.variant(
  resize_to_fill: [450, 300],
  saver: { quality: 85 }  # ← このパラメータを追加
)
  • libvipsによる高品質な圧縮処理
  • 品質85%は視覚的な劣化がほとんどなく、ファイルサイズは大幅削減
  • 圧縮後の画像はActiveStorageによってキャッシュされる

次のステップ

このPRはPhase 0の実装です。今後のフェーズ:

  • Phase 1: Cloudflare CDN導入(追加コスト$0、改善率80-90%)
  • Phase 2: AWS S3 Tokyo移行($5-10/月、レイテンシ削減)
  • Phase 3: サーバースケーリング(実データに基づき判断)

詳細は docs/2026-03-20-インフラ改善とスケーリング計画.md を参照してください。

🤖 Generated with Claude Code

- 全ての画像にlazy loading属性を追加(ブラウザネイティブ機能)
- ActiveStorage variantに85%品質圧縮を追加
- 対象ファイル:
  - app/views/teams/show.html.erb(イベント一覧)
  - app/views/teams/events/_main_column.html.erb(イベント詳細・レポート)
  - app/views/shared/_membership_avatar.html.erb(メンバーアバター)
  - app/views/teams/memberships/_event_item.html.erb(参加イベント一覧)

期待される効果:
- 初期ページロード時間:30-50%削減
- 画像ファイルサイズ:30-50%削減
- 総合改善:50-70%向上(体感速度)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

このプルリクエストは、画像の読み込みパフォーマンスを向上させることを目的としています。ブラウザネイティブの遅延読み込み機能とActiveStorageによる画像圧縮を組み合わせることで、初期ページロード時間を短縮し、ユーザーエクスペリエンスを改善します。視覚的な品質を維持しつつ、ファイルサイズを効率的に削減し、ウェブサイト全体の応答性を高めるためのインフラ改善の第一段階です。

Highlights

  • 画像の遅延読み込みを導入: 全ての image_tagloading: "lazy" 属性を追加し、ブラウザネイティブの遅延読み込みを有効にしました。
  • 画像圧縮の最適化: ActiveStorageの variant 呼び出しに saver: { quality: 85 } パラメータを追加し、画像ファイルサイズを削減しました。
  • 初期ロード時間の改善: これらの変更により、初期ページロード時間と画像ファイルサイズが大幅に削減され、体感速度が向上することが期待されます。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

このプルリクエストは、画像の遅延読み込みと圧縮を導入し、ページのパフォーマンスを向上させる素晴らしい改善です。全体的な方針は正しいですが、2点ほど改善提案があります。
1つ目は、ページの主要なコンテンツ(LCP要素)に対して loading="lazy" を使用すると、逆にパフォーマンスを低下させる可能性があるため、その使用を避けるべきという点です。
2つ目は、画像圧縮の設定が複数のファイルで重複しているため、DRY原則に従い、ヘルパーメソッドなどを用いて一元管理することを推奨します。
これらの点を修正することで、より堅牢で保守性の高い実装になります。

<% if event.image.attached? %>
<div class="aspect-[3/2] mb-6 bg-gray-200">
<%= image_tag event.image.variant(resize_to_fill: [1500, 1000]), alt: "#{event.name}のイメージ画像", class: "w-full h-full object-cover" %>
<%= image_tag event.image.variant(resize_to_fill: [1500, 1000], saver: { quality: 85 }), alt: "#{event.name}のイメージ画像", class: "w-full h-full object-cover", loading: "lazy" %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

イベント詳細ページの上部に表示されるこのメイン画像は、ページのLCP (Largest Contentful Paint) 要素になる可能性が非常に高いです。
LCP要素に loading="lazy" を指定すると、画像の読み込みが意図せず遅延し、Core Web Vitalsのスコアやユーザーの体感速度を悪化させる原因となります。

ページの初期表示でビューポート内に表示されることが確実な画像については、loading="lazy" を指定せず、ブラウザに即時読み込みさせる(eager loading)のがベストプラクティスです。

<%= image_tag event.image.variant(resize_to_fill: [1500, 1000], saver: { quality: 85 }), alt: "#{event.name}のイメージ画像", class: "w-full h-full object-cover" %>

<% if membership.avatar.attached? %>
<div style="width: <%= size %>px; height: <%= size %>px;" class="mx-auto rounded-full bg-gray-200">
<%= image_tag membership.avatar.variant(resize_to_fill: [size, size]), alt: "#{membership.display_name}のアバター", class: "w-#{size/4} h-#{size/4} rounded-full object-cover" %>
<%= image_tag membership.avatar.variant(resize_to_fill: [size, size], saver: { quality: 85 }), alt: "#{membership.display_name}のアバター", class: "w-#{size/4} h-#{size/4} rounded-full object-cover", loading: "lazy" %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

このPRで行われている画像最適化について、2点改善提案があります。

1. 画像圧縮オプションの共通化 (DRY)
saver: { quality: 85 } という設定が、変更された全てのビューで繰り返し記述されています。これはDRY原則に反し、将来のメンテナンス性を損ないます。設定値を一元管理するために、ヘルパーメソッドを作成することを推奨します。

# app/helpers/image_helper.rb
module ImageHelper
  IMAGE_COMPRESSION_OPTIONS = { saver: { quality: 85 } }.freeze

  def compressed_variant(options)
    options.merge(IMAGE_COMPRESSION_OPTIONS)
  end
end

これにより、ビューからは variant(compressed_variant(resize_to_fill: ...)) のように呼び出せます。

2. loading="lazy" の条件付き適用
このアバターのように再利用されるコンポーネントに loading="lazy" を一律適用すると、ヘッダーなどAbove The Foldで表示された場合にパフォーマンスが低下する可能性があります。
呼び出し側で制御できるよう、ローカル変数で lazy loadingを切り替えられるようにするのが望ましいです。

<%# loading: local_assigns.fetch(:lazy, true) ? "lazy" : "eager" %>

これにより、<%= render '...', lazy: false %> のようにして、必要な画像の即時読み込みを指定できます。

@sugiwe sugiwe self-assigned this Mar 20, 2026
sugiwe and others added 2 commits March 20, 2026 18:02
Geminiのコードレビューで指摘された2点を修正:

1. LCP要素からlazy loading削除
   - イベント詳細のメイン画像はLCP要素のため即時読み込み
   - loading="lazy"を削除してCore Web Vitalsを改善

2. 画像圧縮オプションの共通化(DRY原則)
   - ImageHelperに`compressed_variant`メソッドを追加
   - `saver: { quality: 85 }`を一元管理
   - 全ビューでヘルパーメソッドを使用するよう修正

3. アバターのlazy loading制御を柔軟化
   - `lazy`パラメータで呼び出し側から制御可能に
   - デフォルトはtrue(遅延読み込み)

変更ファイル:
- app/helpers/image_helper.rb(新規作成)
- app/views/shared/_membership_avatar.html.erb
- app/views/teams/show.html.erb
- app/views/teams/events/_main_column.html.erb
- app/views/teams/memberships/_event_item.html.erb
- spec/helpers/image_helper_spec.rb(新規作成、3テスト)

テスト結果:
✅ 全テスト合格(56 examples, 0 failures)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@sugiwe
Copy link
Contributor Author

sugiwe commented Mar 20, 2026

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

このプルリクエストは、画像の遅延読み込みと圧縮を実装し、パフォーマンスを改善するものです。ImageHelper を導入して圧縮ロジックを共通化した点は良い設計です。各ビューでの遅延読み込みの適用は概ね適切ですが、アバター画像の表示に関して、Tailwind CSSの利用方法に改善の余地があるためコメントしました。ご確認をお願いします。

<% if membership.avatar.attached? %>
<div style="width: <%= size %>px; height: <%= size %>px;" class="mx-auto rounded-full bg-gray-200">
<%= image_tag membership.avatar.variant(resize_to_fill: [size, size]), alt: "#{membership.display_name}のアバター", class: "w-#{size/4} h-#{size/4} rounded-full object-cover" %>
<%= image_tag membership.avatar.variant(compressed_variant(resize_to_fill: [size, size])), alt: "#{membership.display_name}のアバター", class: "w-#{size/4} h-#{size/4} rounded-full object-cover", loading: lazy ? "lazy" : "eager" %>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Tailwind CSSのJITコンパイラは、ソースコード内に完全な文字列として存在するクラス名しか検出できません。"w-#{size/4}" のように動的にクラス名を生成すると、意図したスタイルが適用されない可能性があります。

また、親要素が width: <%= size %>px; height: <%= size %>px; となっているため、画像をコンテナいっぱいに表示するには w-full h-full を使用するのが適切だと思われます。

object-cover と組み合わせることで、アスペクト比を維持したままコンテナを埋めることができます。

<%= image_tag membership.avatar.variant(compressed_variant(resize_to_fill: [size, size])), alt: "#{membership.display_name}のアバター", class: "w-full h-full rounded-full object-cover", loading: lazy ? "lazy" : "eager" %>

@sugiwe sugiwe merged commit 5293c79 into main Mar 20, 2026
5 checks passed
@sugiwe sugiwe deleted the feature/phase0-image-optimization branch March 20, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant