メール配信をPostfixからSendgridに切り替えた話

# 背景

メール配信システムを以前は自分たちのサーバーにPostfixを立てて運用していたのですが、

- Postfixのメンテがしんどい
- 自分たちのサーバーから送信したメールが特定のメーラーで受け取れないという問題が発生しており、メールの送達率を上げたい
- メルマガの作成や送信などを、デザイナーやマーケティング担当者だけでできるようにしたい
- 送信したメールの開封率やクリック率を**手間を掛けずに**取りたい
- 配信停止リンクなど、この世界で星の数ほど実装されてきた機能をいちいち作りたくない

といった背景から、SendGridに移行することにしました。

# RailsでSendGridを使う

[ガイド](https://sendgrid.com/docs/Integrate/Frameworks/rubyonrails.html)に沿って導入するだけです。 

`config/environment.rb`に、以下を追記するだけでActionMailerがSendGrid経由でメールを送るようになります。

```ruby:config/environment.rb
ActionMailer::Base.smtp_settings = {
  :user_name => ENV['SENDGRID_USERNAME'],
  :password => ENV['SENDGRID_PASSWORD'],
  :domain => 'yourdomain.com',
  :address => 'smtp.sendgrid.net',
  :port => 587,
  :authentication => :plain,
  :enable_starttls_auto => true
}
```

# X-SMTPAPIを使って効率的にメールを送る

SendGridでは、メール送信時のSMTPリクエストに独自のヘッダ（X-SMTPAPI）を付けることで効率的なメール送信ができるようになっています。個人的によく使う事例をいくつか。

## 同じ内容のメールを、toを一人だけにした状態で複数のユーザーに送る

普通はこんな感じで一人ひとり処理すると思います。

```rb
class MyMailer < ActionMailer::Base
  def send_hello(user)
    mail to: user.email
  end
end

# 呼び出し例
users.each do |user|
  MyMailer.send_hello(user).deliver_now
end
```

X-SMTPAPIを使うとこうなります。

```rb
class MyMailer < ActionMailer::Base
  def send_hello(users)
    header = {
      to: users.map { |user| user.email }
    }
    headers['X-SMTPAPI'] = header.to_json
    mail to: 'willnotdeliver@example.com'
  end
end

# 呼び出し例
MyMailer.send_hello(users).deliver_now
```

複数ユーザーへの送信を一回のリクエストで処理できるようになります。複数の宛先の指定には時間がかかるため、一回のリクエストでは1000件程度までにしておくのがよいそうです（[参考リンク](https://sendgrid.kke.co.jp/blog/?p=1300)）

## 送信先ごとに、メールの内容を変える

上記の`to`パラメータとの組み合わせで、送信先ごとにメールの中身を変えることもできます。よくある例として、メールの先頭にユーザーの氏名を入れてみます。

まず、ActionMailerのテンプレートに、氏名を表す部分に独自のタグを入れます。

```send_hello.txt.slim
こんにちは、[name]さん！
```

`[name]`が、独自に定義したユーザー名を表すタグです。独自定義なので、`-name-`でも`<<name>>`でもなんでもよいです。

次に、X-SMTPAPIヘッダに、`[name]`を置換するデータを表す`sub`を追加します。

```rb
class MyMailer < ActionMailer::Base
  def send_hello(users)
    header = {
      to: users.map { |user| user.email },
      sub: {
        "[name]" => users.map { |user| user.full_name }
      }
    }
    headers['X-SMTPAPI'] = header.to_json
    mail to: 'willnotdeliver@example.com'
  end
end
```

これで、相変わらず一回のリクエストで複数のユーザーにちょっとずつ内容の違うメールを送ることができます。

# 開封率／クリック率計測

開封率／クリック率の計測は、SendGridで簡単に導入できます。基本的にすべてHTMLメールでのみ動作するので、事前にHTMLメールを用意しておきます。

SendGridのダッシュボードでSETTING > Trackingと進むと、以下のトラッキングの有効／無効を切り替えできます。

- Click Tracking
    - クリック計測。HTMLメールに含まれるリンクを書き換えてsendgrid.net経由にすることで計測している
- Google Analytics
    - Google Analytics。HTMLメールに含まれるリンクに自動的にGAのキャンペーン情報を追加する
- Open Tracking
    - 開封計測。HTMLメールでのみ動作。
- Subscription Tracking
    - 配信停止計測。SendGridの配信停止リンクとセットで動作する。

## メールのカテゴリごとに開封率／クリック率を計測する

これもX-SMTPAPIを使うことでできます。`category`をX-SMTPAPIヘッダに追加するだけで、SendGridダッシュボード上でカテゴリごとの開封率／クリック率が見れるようになります。

```rb
class MyMailer < ActionMailer::Base
  def send_hello(users)
    header = {
      category: 'Category1',
      to: users.map { |user| user.email }
    }
    headers['X-SMTPAPI'] = header.to_json
    mail to: 'willnotdeliver@example.com'
  end
end
```

# 配信停止機能

結論から言うと、SendGridの配信停止機能は使いませんでした（自前で作りました）

弊社のサービスの場合、

- ひとつのSendGridアカウントで複数のアプリケーションが動作している
    - サロン予約サービスPopcornの、ユーザー向けページと、サロン向けページがある
- Transaction emailとMarketing emailで配信停止リストを同期させたい
- 配信停止リンクを出したいメール（メルマガ系）と、出したくないメール（予約確認など重要なメール）がある
- 配信停止ページをカスタマイズしたい

といういくつかの要件があり、Sendgridの配信停止機能ではこれら全てを同時に満たすにはかゆいところに手が届かなかったためです。

# まとめ

一部使いづらい機能もありますが、PostfixからSendGridへの移行によって弊社のエンジニアは総じてHappyになっています。ぜひ興味のある方、メール配信で消耗している方はSendGridお試しください。