【超簡単！】RailsアプリでOGPを動的に変更する方法

# はじめに
こんにちは、プログラミングスクールRUNTEQにて、未経験からエンジニア転職を目指して学習中の[すずゆー](https://twitter.com/suzuyu_0115)と申します。

今回はRailsでのWebアプリ開発において、OGPをページ毎に変化させる方法について説明していきます！
「SNSシェアする時に、投稿やページに応じて画像が変化するようなアプリを作りたい」「でもやり方がわからない...」
そんな方に初心者目線で分かりやすく伝わるように書かせていただきました。

今回は解説にあたって、私の作成したWebアプリをベースに説明させていただきます。

https://meshitelog.com

[GitHubはこっち](https://github.com/suzuyu0115/meshitelog)


また、上記のサービスについて詳しくまとめている記事もございますので、お暇な方は拝見していただけると大変嬉しいです✨

https://qiita.com/suzuyu0115/items/6ca6ce071d0379467459


# OGPとは
### OGPとは
> OGPとは「Open Graph Protocol」の略で、Webサイト内の記事がFacebookやTwitterなどのSNSで拡散・シェアされた際に、各ページのタイトルやサムネイル画像といった情報を表示させるための機能です。

SNSを使っている皆さんでしたら100万回くらいは目にしたことがあるかと思います。アレですね！
![ogp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493892/6f527376-b425-8751-6724-d71adfe1fcba.png)
このように、OGPはリンクを貼った時にタイトル、画像、概要を視覚的に分かりやすく表示するための規格のことです。
今回はこちらのOGP画像を動的に変更していこうという実装をしていきます。
かくいうQiitaでも、動的OGPの実装が施されています。

⇩通常のOGP

https://qiita.com/

⇩動的に変更されたOGP

https://qiita.com/suzuyu0115/items/6ca6ce071d0379467459

記事のリンクでは、通常のOGP画像とは異なり、その記事のタイトルがOGP画像として反映されています。
このように同じQiitaのサイトでも、ページによって異なるOGPが表示されているのが分かります。


# 実装方法
先述の通り、私の作成したwebサービスをベースに実装方法を解説します。

https://meshitelog.com

### 環境と準備
```
ruby "3.2.2"
rails ~> 7.0.5

# 画像アップロード機能に使用したgem
gem 'carrierwave'
gem 'fog-aws'
gem 'mini_magick'
```
Railsのバージョンは7系で開発しましたが、6系などでも問題なく動作するかと思います。
デフォルトのOGP画像がディレクトリに用意されており、画像投稿機能の付加されたCRUD機能が作成されている前提で解説します。

## 1. (下準備) gem meta-tags のインストール
`Gemfile`に以下を追記して、`bundle install`します。
```:Gemfile
gem 'meta-tags'
```
```
$ bundle install
```

## 2.(下準備) OGPの設定
下記のコマンドを実行すると`config/initializers/meta_tags.rb`というファイルが出来上がるため、こちらを編集することでmetaタグとOGP調整できます。
```
$ rails g meta_tags:install
```
`config/initializers/meta_tags.rb`は今回デフォルトのままで結構です。
```config/initializers/meta_tags.rb
# frozen_string_literal: true

# Use this setup block to configure all options available in MetaTags.
MetaTags.configure do |config|
  # How many characters should the title meta tag have at most. Default is 70.
  # Set to nil or 0 to remove limits.
  # config.title_limit = 70

  # When true, site title will be truncated instead of title. Default is false.
  # config.truncate_site_title_first = false

  # Maximum length of the page description. Default is 300.
  # Set to nil or 0 to remove limits.
  # config.description_limit = 300

  # Maximum length of the keywords meta tag. Default is 255.
  # config.keywords_limit = 255

  # Default separator for keywords meta tag (used when an Array passed with
  # the list of keywords). Default is ", ".
  # config.keywords_separator = ', '

  # When true, keywords will be converted to lowercase, otherwise they will
  # appear on the page as is. Default is true.
  # config.keywords_lowercase = true

  # When true, the output will not include new line characters between meta tags.
  # Default is false.
  # config.minify_output = false

  # When false, generated meta tags will be self-closing (<meta ... />) instead
  # of open (`<meta ...>`). Default is true.
  # config.open_meta_tags = true

  # List of additional meta tags that should use "property" attribute instead
  # of "name" attribute in <meta> tags.
  # config.property_tags.push(
  #   'x-hearthstone:deck',
  # )
end
```
続いてメタタグとOGPの設定です。
`application-helper.rb`に以下を追記します。
[オプションの詳細についてはこちら（github）](https://github.com/kpumuk/meta-tags#allowed-options-for-display_meta_tags-and-set_meta_tags-methods)
```app/helpers/application-helper.rb
module ApplicationHelper
 def show_meta_tags
    assign_meta_tags if display_meta_tags.blank?
    display_meta_tags
  end

  def default_meta_tags
    def default_meta_tags
    {
      site: 'サイト名',
      title: 'タイトル',
      reverse: true,
      separator: '|',   #Webサイト名とページタイトルを区切るために使用されるテキスト
      description: 'ページの説明',
      keywords: 'ページキーワード',   #キーワードを「,」区切りで設定する
      canonical: request.original_url,   #優先するurlを指定する
      noindex: ! Rails.env.production?,
      icon: [                    #favicon、apple用アイコンを指定する
        { href: image_url('favicon.ico') },
        { href: image_url('icon.jpg'), rel: 'apple-touch-icon', sizes: '180x180', type: 'image/jpg' },
      ],
      og: {
        site_name: 'サイト名',
        title: 'タイトル',
        description: 'ページの説明', 
        type: 'website',
        url: request.original_url,
        image: image_url('ogp.png'),
        locale: 'ja_JP',
      },
      twitter: {
        card: 'summary_large_image',
        site: '@ツイッターのアカウント名',
      }
      fb: {
        app_id: '自身のfacebookのapplication ID'
      }
    }
  end
  end
end
```
上記のshow_meta_tagsメソッドは、メタタグがまだ設定されていない場合にそれを設定し、最終的にそのメタタグ情報をHTML形式で返すメソッドです。Railsのビューでこのメソッドを呼び出すことで、ページごとのカスタムメタタグやデフォルトのメタタグ情報を簡単に表示することができます。
こちらをご自身のアプリに合わせて編集してください！
また、メタタグを出力するには`application.html.erb`の`<head>`内に`show_meta_tags`メソッドを追記します。
```app/views/layouts/application.html.erb
<head>
  <%= show_meta_tags %>
</head>
```


## 3.OGPを動的に変更する
お待たせしました！ここから動的OGPの実装に移ります。
私のPFの投稿詳細画面の飯画像(`food_image`)をデフォルトのOGPの代わりに表示させる実装を例に解説します。
下記は私のmeta-tagsのオプションになります。
```app/helpers/application-helper.rb
module ApplicationHelper
  def show_meta_tags
    assign_meta_tags if display_meta_tags.blank?
    display_meta_tags
  end

  def assign_meta_tags(options = {})
    defaults = t('meta_tags.defaults')
    options.reverse_merge!(defaults)
    site = options[:site]
    title = options[:title]
    description = options[:description]
    keywords = options[:keywords]
    image = options[:image].presence || image_url('placeholder.png')
    configs = {
      separator: '|',
      reverse: true,
      site:,
      title:,
      description:,
      keywords:,
      canonical: request.original_url,
      icon: {
        href: image_url('placeholder.png')
      },
      og: {
        type: 'website',
        title: title.presence || site,
        description:,
        url: request.original_url,
        image:,
        site_name: site
      },
      twitter: {
        site:,
        card: 'summary_large_image',
        image:
      }
    }
    set_meta_tags(configs)
  end
end
```

今回重要となってくるのが`image`の部分になるのでこちらにフォーカスを当てていきます。
`assign_meta_tags`メソッド内にある下記は、`:image`キーに値(画像)が存在する場合はそれの画像を表示、存在しない場合にはデフォルトの`placeholder.png`を表示する処理になります。

また、`:og`ハッシュ内や`:twitter`ハッシュ内にも同様に`image`キーがありますが、こちらに値を設定すると動的OGPが実現できなくなってしまうので、空欄にしておきます。
```app/helpers/application-helper.rb
image = options[:image].presence || image_url('placeholder.png')
```

続いて、ビューの先頭に下記のコードを追加します。
この記載により、`assign_meta_tags`メソッドに`title`と`image`の引数を渡しています。
```app/views/posts/show.html.erb
<% assign_meta_tags title: @post.title, image: @post.food_image.url %>
・・・
```
このケースでは、`:title`キーに投稿のタイトル、`:image`キーに飯画像(`food_image`)が格納されており、これらがOGPのタイトル、画像として表示されるようになります。

**⇩デフォルトのOGP**
![ogp1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493892/e8c12f07-b3a4-60d5-da87-cdfae2a8af85.png)

**⇩投稿詳細のOGP**
![ogp2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493892/2b0c6fcb-8dbc-1e1b-ed2d-415d33e3725e.png)

**~~Xの改悪でタイトルが分かりづらくなっていますが~~このように投稿詳細URLのOGPでは、画像が投稿の飯画像に変化しました！**


# 最後に
最後までお読みいただきありがとうございました！

長々と書かせていただきましたが、動的OGP自体は少ないコードで簡単に実装できることが分かっていただけたと思います！

動的OGPは覚えれば自由自在に画像を設定できて色んなアプリにも応用が利くので、ぜひご自身のアプリで実装されてみてはいかがでしょうか。
当方未経験エンジニアの執筆記事のため、説明の抜けや事実と相違する内容がございましたらご指摘いただけますと幸いです🙇‍♂️

OGPと仲良くなってワンランク上のwebアプリ開発を楽しみましょう！