Rails6 アプリからメールを送信する デプロイ環境編

# 目的

- Rails6で作成したアプリをデプロイした時に当該アプリからメールを送信する方法をまとめる。

# 前提条件

- 下記の方法、またはそれに準ずる方法でローカル環境にてテストメールが送信できる状態のRailsアプリが存在すること。
    - [Rails6 アプリからGmailのメールサーバからメールを送信する ローカル環境編](https://qiita.com/miriwo/items/06d7d69aff47fd02fbc1)
- メール送信を行いたいアプリがすでにherokuでデプロイされていること

# 作業期待値

- herokuでデプロイしているアプリからメールが送信されるようにする。
- メール送信内容は[Rails6 アプリからGmailのメールサーバからメールを送信する ローカル環境編](https://qiita.com/miriwo/items/06d7d69aff47fd02fbc1)と変わらずにクラウドサーバであるheroku側の設定を主に行う。
- herokuでのデプロイ方法の基本は下記の本を参考に構築を行なった。個人開発の公開時のノウハウが詰まった本なのでこれから勉強する人はバイブル的に持っておくことをおすすめする。本件のクリティカルな解決法は記載されていないが非常に素晴らしい本である。(アフィリエイトリンクでは無いので安心してください)
    - [個人開発のための Webサービス公開マニュアル](https://www.amazon.co.jp/%E5%80%8B%E4%BA%BA%E9%96%8B%E7%99%BA%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE-Web%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E5%85%AC%E9%96%8B%E3%83%9E%E3%83%8B%E3%83%A5%E3%82%A2%E3%83%AB-%E9%9B%A3%E6%B3%A2-%E8%81%96%E4%B8%80/dp/4798059315)

# 作業前にちょっと聞いてほしいこと

- 前回の[Rails6 アプリからGmailのメールサーバからメールを送信する ローカル環境編](https://qiita.com/miriwo/items/06d7d69aff47fd02fbc1)を実施された方なら簡単である。
- あまり難しいことを考えずに実施していただきたい。
- ローカル開発環境だとGmailのサーバを使用してメールを送信したが、heroku上のアプリだとSendGridというものからメールを送信する。(他にも方法があるが比較的簡単なので今回はこの方法を採用した。)

# 考え方

- ローカル開発環境のメールサーバ設定はheroku環境だとそのまま流用できないので専用のメールサーバ設定が必要となる。
- heroku上のアプリからメールを送るには専用の設定ファイルが必要と覚えておいてほしい。
- デプロイ中は前述した専用の設定ファイルから自動で設定が読まれる。
- 下記に前述の内容の簡易図を記載する。

![スクリーンショット 2020-01-21 0.49.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/c5503c4d-f0ec-dde3-0c3a-a3d3eb28135d.png)

# 作業概要

1. SendGridの有効化とAPI鍵作成
1. デプロイアプリとAPI鍵の紐付け
1. メール確認

# 作業詳細

1. SendGridの有効化とAPI鍵作成
    1. 下記コマンドを実行してSendGridを有効化する。

        ```terminal
        $ heroku addons:create sendgrid:starter
        ```

    1. herokuにログインする。
        - [heroku Apps](https://dashboard.heroku.com/apps)
    1. デプロイしているアプリ名をクリックする。
    1. 「Add ons」にある「SendGrid」をクリックする。
    ![スクリーンショット 2020-01-20 23.15.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8c477fba-836a-512d-4d61-00bea655ae46.png)

    1. 下記画面になったら確認用のメールアドレスを記入しメールを送るボタンをクリックする。(本メールアドレスはアプリからの送受信には全く関係ない。あくまでSendGridの確認用のアドレスである。)
    ![スクリーンショット 2020-01-20 23.16.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/f9e315cb-831e-c312-50ce-ba003601df0a.png)

    1. 下記の画面になったことを確認し、先に入力したメールアドレスの受信ボックスを確認する。
    ![スクリーンショット 2020-01-20 23.16.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/efb7f3f0-af93-63b8-4f01-8dbbeefc7589.png)

    1. 受信ボックスに下記のようなメールが届いていることを確認して、「Confirm Email Address」をクリックする。
    ![スクリーンショット 2020-01-20 23.16.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8921e350-9bee-5100-911d-95cbdfed6c45.png)

    1. 前述の作業を実施後下記の画面に遷移する。
    ![スクリーンショット 2020-01-20 23.17.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/289b5fb5-80f5-c587-ae41-15f398a4fb7e.png)

    1. 画面左下のバーの「Settings」→「API Keys」をクリックする。
    ![スクリーンショット 2020-01-20 23.18.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/f146885e-9064-7557-93c3-b2373da936e1.png)

    1. 下記の画面になったことを確認し、画面右上の「Create API Key」をクリックする。
    ![スクリーンショット 2020-01-20 23.18.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/3993c197-343a-8457-e642-e3f1d1e9cade.png)

    1. 「API Key Name」に認識しやすい任意のAPI鍵の名前を入力、(自分がわかるならなんでもOK)「API Key Permissions」は「Full Accesss」を選択し内容を確認後「Create & View」をクリックする。
    ![スクリーンショット 2020-01-20 23.19.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/987c9a57-16a4-aae4-f970-4849c47408d7.png)

    1. 表示された鍵をスクリーンショットやテキストコピーでメモしておく
    ![スクリーンショット 2020-01-20 23.20.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/1c0bcdbc-7c94-ed5c-1321-f86115429e98.png)

1. デプロイアプリとAPI鍵の紐付け
    1. 下記コマンドを実行してAPI鍵をherokuの変数に格納する。(本作業で自動的にメールサーバ用のパスワードとユーザ名が自動生成される)
    
        ```terminal
        $ heroku config:set SENDGRID_API_KEY=XXXXXXXXXXXXXXXXXXXX6NDlg.J1bMNO_jbeepuH8LOYFwkMKFcOKoQ5vZ8z9axkzMyWU
        ```

1. メールサーバの設定
    1. 下記に存在する送信先を指定するファイル`product.rb`をエディタで開く
        - アプリ名フォルダ/config/environments
            - product.rb
    1. ファイル`product.rb`を下記のように修正してSentGridからメールを送信できるようにする。(下記のように設定すると、先の作業で登録したAPI鍵からuser_nameとpasswordが自動生成されそれぞれ環境変数SENDGRID_USERNAMEとSENDGRID_PASSWORDに格納され、それを読んでメールを送るようになる。)

        ```ruby
        Rails.application.configure do
          # Settings specified here will take precedence over those in config/application.rb.
        
          # Code is not reloaded between requests.
          config.cache_classes = true
        
          # Eager load code on boot. This eager loads most of Rails and
          # your application in memory, allowing both threaded web servers
          # and those relying on copy on write to perform better.
          # Rake tasks automatically ignore this option for performance.
          config.eager_load = true
        
          # Full error reports are disabled and caching is turned on.
          config.consider_all_requests_local       = false
          config.action_controller.perform_caching = true
        
          # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
          # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
          # config.require_master_key = true
        
          # Disable serving static files from the `/public` folder by default since
          # Apache or NGINX already handles this.
          config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
        
          # Compress CSS using a preprocessor.
          # config.assets.css_compressor = :sass
        
          # Do not fallback to assets pipeline if a precompiled asset is missed.
          config.assets.compile = false
        
          # Enable serving of images, stylesheets, and JavaScripts from an asset server.
          # config.action_controller.asset_host = 'http://assets.example.com'
        
          # Specifies the header that your server uses for sending files.
          # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
          # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
        
          # Store uploaded files on the local file system (see config/storage.yml for options).
          config.active_storage.service = :local
        
          # Mount Action Cable outside main process or domain.
          # config.action_cable.mount_path = nil
          # config.action_cable.url = 'wss://example.com/cable'
          # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
        
          # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
          # config.force_ssl = true
        
          # Use the lowest log level to ensure availability of diagnostic information
          # when problems arise.
          config.log_level = :debug
        
          # Prepend all log lines with the following tags.
          config.log_tags = [ :request_id ]
        
          # Use a different cache store in production.
          # config.cache_store = :mem_cache_store
        
          # Use a real queuing backend for Active Job (and separate queues per environment).
          # config.active_job.queue_adapter     = :resque
          # config.active_job.queue_name_prefix = "eveyDayStudy_production"
          config.action_mailer.perform_caching = false
        
          # Ignore bad email addresses and do not raise email delivery errors.
          # Set this to true and configure the email server for immediate delivery to raise delivery errors.
          # config.action_mailer.raise_delivery_errors = false
        
          # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
          # the I18n.default_locale when a translation cannot be found).
          config.i18n.fallbacks = true
        
          # Send deprecation notices to registered listeners.
          config.active_support.deprecation = :notify
        
          # Use default logging formatter so that PID and timestamp are not suppressed.
          config.log_formatter = ::Logger::Formatter.new
        
          # Use a different logger for distributed setups.
          # require 'syslog/logger'
          # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
        
          if ENV["RAILS_LOG_TO_STDOUT"].present?
            logger           = ActiveSupport::Logger.new(STDOUT)
            logger.formatter = config.log_formatter
            config.logger    = ActiveSupport::TaggedLogging.new(logger)
          end
        
          # Do not dump schema after migrations.
          config.active_record.dump_schema_after_migration = false
        
          # Inserts middleware to perform automatic connection switching.
          # The `database_selector` hash is used to pass options to the DatabaseSelector
          # middleware. The `delay` is used to determine how long to wait after a write
          # to send a subsequent read to the primary.
          #
          # The `database_resolver` class is used by the middleware to determine which
          # database is appropriate to use based on the time delay.
          #
          # The `database_resolver_context` class is used by the middleware to set
          # timestamps for the last write to the primary. The resolver uses the context
          # class timestamps to determine how long to wait before reading from the
          # replica.
          #
          # By default Rails will store a last write timestamp in the session. The
          # DatabaseSelector middleware is designed as such you can define your own
          # strategy for connection switching and pass that into the middleware through
          # these configuration options.
          # config.active_record.database_selector = { delay: 2.seconds }
          # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
          # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
        end
        ```
        
        ↓修正
        
        ```ruby
        Rails.application.configure do
          # Settings specified here will take precedence over those in config/application.rb.
        
          # Code is not reloaded between requests.
          config.cache_classes = true
        
          # Eager load code on boot. This eager loads most of Rails and
          # your application in memory, allowing both threaded web servers
          # and those relying on copy on write to perform better.
          # Rake tasks automatically ignore this option for performance.
          config.eager_load = true
        
          # Full error reports are disabled and caching is turned on.
          config.consider_all_requests_local       = false
          config.action_controller.perform_caching = true
        
          # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
          # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
          # config.require_master_key = true
        
          # Disable serving static files from the `/public` folder by default since
          # Apache or NGINX already handles this.
          config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
        
          # Compress CSS using a preprocessor.
          # config.assets.css_compressor = :sass
        
          # Do not fallback to assets pipeline if a precompiled asset is missed.
          config.assets.compile = false
        
          # Enable serving of images, stylesheets, and JavaScripts from an asset server.
          # config.action_controller.asset_host = 'http://assets.example.com'
        
          # Specifies the header that your server uses for sending files.
          # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
          # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
        
          # Store uploaded files on the local file system (see config/storage.yml for options).
          config.active_storage.service = :local
        
          # Mount Action Cable outside main process or domain.
          # config.action_cable.mount_path = nil
          # config.action_cable.url = 'wss://example.com/cable'
          # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
        
          # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
          # config.force_ssl = true
        
          # Use the lowest log level to ensure availability of diagnostic information
          # when problems arise.
          config.log_level = :debug
        
          # Prepend all log lines with the following tags.
          config.log_tags = [ :request_id ]
        
          # Use a different cache store in production.
          # config.cache_store = :mem_cache_store
        
          # Use a real queuing backend for Active Job (and separate queues per environment).
          # config.active_job.queue_adapter     = :resque
          # config.active_job.queue_name_prefix = "eveyDayStudy_production"
        
          config.action_mailer.perform_caching = false
          config.action_mailer.delivery_method = :smtp
          config.action_mailer.perform_deliveries = true
          config.action_mailer.smtp_settings = {
            :enable_starttls_auto => true,
            :address => 'smtp.sendgrid.net',
            :port => 587,
            :domain => 'herokuapp.com',
            :authentication => :plain,
            :user_name => ENV['SENDGRID_USERNAME'],
            :password => ENV['SENDGRID_PASSWORD'],
          }
        
          # Ignore bad email addresses and do not raise email delivery errors.
          # Set this to true and configure the email server for immediate delivery to raise delivery errors.
          # config.action_mailer.raise_delivery_errors = false
        
          # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
          # the I18n.default_locale when a translation cannot be found).
          config.i18n.fallbacks = true
        
          # Send deprecation notices to registered listeners.
          config.active_support.deprecation = :notify
        
          # Use default logging formatter so that PID and timestamp are not suppressed.
          config.log_formatter = ::Logger::Formatter.new
        
          # Use a different logger for distributed setups.
          # require 'syslog/logger'
          # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
        
          if ENV["RAILS_LOG_TO_STDOUT"].present?
            logger           = ActiveSupport::Logger.new(STDOUT)
            logger.formatter = config.log_formatter
            config.logger    = ActiveSupport::TaggedLogging.new(logger)
          end
        
          # Do not dump schema after migrations.
          config.active_record.dump_schema_after_migration = false
        
          # Inserts middleware to perform automatic connection switching.
          # The `database_selector` hash is used to pass options to the DatabaseSelector
          # middleware. The `delay` is used to determine how long to wait after a write
          # to send a subsequent read to the primary.
          #
          # The `database_resolver` class is used by the middleware to determine which
          # database is appropriate to use based on the time delay.
          #
          # The `database_resolver_context` class is used by the middleware to set
          # timestamps for the last write to the primary. The resolver uses the context
          # class timestamps to determine how long to wait before reading from the
          # replica.
          #
          # By default Rails will store a last write timestamp in the session. The
          # DatabaseSelector middleware is designed as such you can define your own
          # strategy for connection switching and pass that into the middleware through
          # these configuration options.
          # config.active_record.database_selector = { delay: 2.seconds }
          # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
          # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
        end
        ```

1. メール確認
    1. 下記のメール送信トリガーコードが記載されているコントローラが実行されるような操作を行う
        - `NoticeMailer.greeting.deliver_now`
    1. [Rails6 アプリからGmailのメールサーバからメールを送信する ローカル環境編](https://qiita.com/miriwo/items/06d7d69aff47fd02fbc1)の対応で筆者の環境では`http://localhost:3000/posts/index/:id`にアクセスした時にメールが送信されるようにトリガーコードを設置している。
    1. 筆者の場合herokuのパスに置き換え`https://study-record.herokuapp.com/posts/index/:id`にアクセスしエラーが出ないことを確認する。
    1. テストメール送信先アドレスを確認し下記のようなメールを受信していることを確認する。
    ![スクリーンショット 2020-01-21 1.37.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/2ae2a7ad-8977-91b7-fea6-68d3791cd3ee.png)