GoLangとMailgunで実装する自動メール送信機能

# はじめに
この記事は自分がGolangとMailgunでメール送信サービスの実装をしたのでその流れを忘備録として残せたらと思って書いてます。加えて受託案件で自動メール送信機能を作ることはよくあると思うのですが、次のセクションで理由を記述しますが結構その時に自分と同じように困る人もいるかもと思ってこの記事を書いています。

# SendGrid使えなかった
自分もそうだったのですが、メール送信サービスと聞くとSendGridを思い浮かべる人が多いと思います。なので今回のタスクがアサインされた際はSendGridを使おうと思っていました。しかし結論から言うとSendGridが使えませんでした。
なんとSendGridの利用規約に

>システムの開発や運用を受託している企業が契約した場合、その使用権をクライアントに譲渡している、もしくは再販していることになります。
>そのため、メール送信者の立場にあたるクライアント企業に直接契約いただく必要があります。
>※開発から運用保守まですべてを請け負っている場合であってもクライアントにて契約いただく必要があります

とありました。つまりまとめると
**他者に納品するプロダクトの中でSendGridを使いたい場合は納品先の会社や個人の方で契約をしてね**
ということです。困った。
納品後のコードの著作権がこちらに残る場合でも相手方が契約者にならないといけなさそうです。こうなると支払いが発生するので新たに契約をまかないといけなかったり、相手方への説明をしたりなど手間がかかってしまって面倒だ、という理由で今回はSendGridを諦めて代替サービスで実装することになりました。
受託で案件をやる場合、他の人はどうしてるんだろう...？

# AmazonSESとの比較
Mailgunを採用する前にAmazonSESとの比較検討をしました。それぞれのメリデメを簡単にまとめます。

| 特徴         | Amazon SES                          | Mailgun                              |
|--------------|-------------------------------------|--------------------------------------|
| **メリット** | ・コストを抑えられる<br>・AWSとの連携が容易  | ・GCPとの連携が可能<br>・初期設定が簡単 |
| **デメリット** | ・バウンス処理が面倒                 | ・メールアドレスのRFC準拠に厳しい    |

今回のプロジェクトでは特定のドメイン宛てにメールを送信することが主な目的であり、Mailgunの利用に関連するデメリットは影響が少ないと判断しました。また、このプロジェクトがGCPを使用しているため、Amazon SESを使用するメリットが相対的に低くなり、最終的にMailgunの採用を決定しました。次のセクションでは、実装にドメイン取得やMailgunの設定など実装の方に移っていきます。


# ドメインの取得
まずはメールを送信するためのドメインを取得します。お名前.comなど、どこでドメインを取得してもいいのですが、弊社では元々Google Domainsで取得していたのですが、それがSquarespaceに置き換わったので今回はSquarespaceでドメインを取得します。

日本語には全く対応していないので少し英語の理解が必要ですが、UIもわかりやすくておすすめです。
1. アカウントにログイン後、上部にDashboardかDomainsを選択できるタブがあるのでDomainsを選択します

2. ページが切り替わりそのページに**GET A DOMAIN**というボタンがあるのでそのボタンをクリックしたら取得したいドメイン名を入力するページに切り替わるのでそこで取得したいドメイン名を入力します

3. そして候補がいくつか入力後に提示されるので購入したいドメインを選択して後は購入まで行ってください。

余談ですがGCPのCloud DomainsでもSquarespaceでのドメイン取得ができそうで、加えてそちらの方の価格が安く設定してあったのですが今回はSuarespaceから直接購入しました。もしCloud Domainsでドメインを取得した方がいらしたら、購入後Squarespaceに反映されるのかなど教えてくださいましたら嬉しいです。購入後、SquarespaceのDomainsのタブの中で下記のように
![スクリーンショット 2024-03-09 16.40.32（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739285/ca251d87-267d-85b3-ec31-6007a6bbd1de.png)
取得したドメインが反映されていればドメインの取得成功です

# Mailgunの設定
次にMailgunでの設定に移ります。ログイン後、左にあるメニューリストの中にSendingというメニューがあります。その中にDomainsというアイテムがあるのでそれをクリックします。そうするとページが切り替わり、そのページの右上の方に**Add new domain**というボタンがあるのでそのボタンをクリックします。
![mailgun.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739285/e5e5754c-a09e-c594-0101-e621ad3971a2.png)
そうするとdomain名を入力するフィールドが出てくると思うのでそこに、取得したドメイン名を入力してください。
そして次に進むとDNS recordsが表示されると思うので、それを使ってDNSの設定をしていきます。
Squarespaceを例に説明していくとまずDomainsのタブから取得したドメインのレコードをクリックします。すると右側にモーダルが開くと思うのでその中の一番下の**MANAGE DOMAIN SETTINGS**ボタンをクリックします。
そして上の方の右側に**EDIT DNS**というのがあるのでそれをクリックしてDNSの編集画面を開いてください。
今回はCustom Recordsに追加していきます。
![DNSrecord.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739285/5f544633-5728-e7b3-484e-42340be5ba58.jpeg)
このような形になると思います。TypeがTXTはSPFとDKIMを設定します。DKIMのレコードのHostなのですが、
hogehoge._domainkey
のように設定を行なってください。
ちなみにDKIMとはメールにデジタル署名を加え、受信側でその署名を確認することで、メールの真正性を検証することでメールが改ざんされずに送信元から届いたことを証明することができます。
全て追加した後、mailgunの方の上部に**Veryfi DNS settings**というボタンがあるのでそれをクリックし、全てverifiedに変われば取得したドメインとmailgunの設定が完了です。
24~48時間かかる場合もあるとありましたが、自分の環境では設定後すぐにverifiedになりました。それでは次は実際にコードを書いていきます。

# Goでの実装
MailgunのGo言語用ライブラリを使用すると、メール送信機能の実装が驚くほど簡単になります。
[https://github.com/mailgun/mailgun-go](mailgunGoLibrary)
上記URLはMailgunから提供されているライブラリなのですが、これのREADMEを見てみると実装がものすごく簡単なことがわかります。下記コードは実際に私が実装した関数です。少し修正しています。


```golang
import (
	"context"
	"fmt"
	graphtype ""
	"os"

	"github.com/mailgun/mailgun-go/v4"
)

func (s AccountSvcImpl) SendEmail(
 ctx context.Context,
 input graphtype.SendEmailInput,
) (graphtype.InviteAccountResult, error) {
 // 環境変数からメール送信に必要な情報を取得
 emailDomain := os.Getenv("YOUR_DOMAIN")
 mailgunPrivateApiKey := os.Getenv("YOUR_MAILGUN_PRIVATE_API_KEY")

 sender := fmt.Sprintf("noreply@%s", emailDomain)
 subject := "件名"
 body := "本文"
 recipient := input.UserEmailAddress

 // mailgunClientのinstanceを作成
 mg := mailgun.NewMailgun(emailDomain, mailgunPrivateApiKey)
 m := mg.NewMessage(
  sender,
  subject,
  body,
  recipient,
 )
 // idなども受け取ることができます。
 _, _, err = mg.Send(ctx, m)
 if err != nil {
  return graphtype.SendEmailError{
   Code: "error_code",
  }, err
 }

 return graphtype.SendEmailSuccess{
  IsSuccess: true,
 }, nil
}
```
これだけで実装は終了です。この関数を実行すると、指定されたメールアドレスに実際にメールが送信されます。私たちのプロジェクトではGraphQLを使用しており、関数の戻り値やエラー処理はgraphtypeパッケージを通じて定義しています。
メールの送信が成功すると、Mailgunの方でログを確認することができます。
![mailsendsuccess.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3739285/8935d40b-30da-df48-7ccb-c7a849ace95b.jpeg)
このようにうAcceptedとDeliveredがセットでログに追加されます。最初自分はsenderとrecipientを逆にしていて、acceptedされているのになんで届かないんだという初歩的間違いをしてしまいましたのでみなさん気をつけてください...。

# 感想
記述していなかったのですがMailgunのPlanはFoundationを契約しています。月35ドル(5000円程度)なので少しかかってるなという印象です。しかし実装面はとても簡単でよかったです。

# まとめ
Mailgun簡単で使いやすくておすすめだよ。