SpringBootで実装するメール送信機能とMailHogを使ったローカルテスト

# はじめに

この記事では、Spring Boot における基本的なメール送信機能の実装方法と、MailHog を使ってローカル環境でメールの内容や挙動を確認する方法を説明します。

この記事でカバーする内容

- Spring Boot によるメール送信機能の実装方法
- MailHog を使ったローカル環境でのメールの確認方法

記事中のソースコードは以下のリポジトリから参照できます。

https://github.com/hiroe-mkk/springboot-mailhog-tips

# 環境

- Spring Boot: 3.1.0
- Java: OpenJDK 17
- Gradle: 8.8
- Docker: 24.0.5
  - Docker Desktop: 4.33.1
  - Docker Compose: 2.20.2-desktop.1
- MailHog 1.0.1（Dockerコンテナとして実行）
- Windows 11 Pro

# Docker を使用した MailHog のセットアップ

## MailHog とは

MailHog は、開発者が安全にメール送信機能をテストできるローカル環境用の SMTP サーバーです。

実際の SMTP サーバーを使わずに送信されるメールの内容や挙動を確認できるため、メール送信機能を実装する際にテスト環境として非常に便利です。

## セットアップ手順

公式サイトからバイナリをダウンロードすることも可能ですが、今回は手軽さを優先して Docker を使ったセットアップ手順を紹介します。

### 1. Docker コンテナの起動

MailHog は Docker イメージとして提供されているため、以下のコマンドを実行するだけで簡単に起動できます。

```shell
$ docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog
```

各オプションの意味は以下の通りです。

- **`-d`**: コンテナをバックグラウンドで実行
- **`-p 1025:1025`**: MailHog の SMTP サーバーポート
- **`-p 8025:8025`**: MailHog の Web UI ポート

### 2. Web インターフェースの確認

MailHog の起動確認も兼ねて、Web UI を見てみましょう。ブラウザで http://localhost:8025 にアクセスします。

問題がなければ、次のような画面が表示されるはずです。

![mailhog1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671461/ac51aea4-571c-4eff-ecd1-ae05b8528eda.png)

送信されたメールはこの画面に一覧表示され、各メールの内容や詳細も確認することができます。

# Spring Boot を使ったメール送信機能の実装

Spring Boot を用いたメール送信機能の実装に移る前に、その概要を簡単に説明します。

## Spring Boot のメール送信機能について

Spring Framework では、メール送信を簡単に行うための便利なユーティリティが提供されています。このユーティリティのおかげで、メール送信機能の実装は非常に簡素化されています。

特に Spring Boot を使用することで、メール送信機能の実装はさらに容易になります。Spring Boot には `spring-boot-starter-mail` という便利なスターターが提供されているため、これをプロジェクトに追加するだけで必要なライブラリの導入と基本的な設定が自動的に行われます。
そのため、開発者はスムーズにメール送信機能の実装を開始することができます。

## 実装方法

以下に、Spring Boot を使用したメール送信機能の具体的な実装方法を示します。

### 1. 依存関係の追加

まず、Spring Boot プロジェクトに `spring-boot-starter-mail` 依存関係を追加します。

`build.gradle` の `dependencies` に以下を追加してください。

```gradle:build.gradle
dependencies {
    // omitted

    implementation 'org.springframework.boot:spring-boot-starter-mail'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}
```

ここでは、メール送信の確認用エンドポイントを作成するために `spring-boot-starter-web` も追加しています。

### 2. 依存関係の反映

依存関係を追加したら、プロジェクトに反映させるために以下のコマンドを実行します。

```shell
$ ./gradlew build --refresh-dependencies
```

### 3. プロパティの設定

つぎに、SMTP サーバー (今回は MailHog) の情報をプロパティとして設定します。
この値を設定することで、Spring Boot アプリケーションは MailHog を使用してメールを送信できるようになります。

`application.properties` に以下を追加してください。

```properties:application.properties
spring.mail.host=localhost
spring.mail.port=1025
```

ここで指定するホスト名とポート番号は、コンテナ起動時に指定した MailHog の設定値に合わせます。

### 4. JavaMailSender を使用したメール送信処理の実装

Spring Framework でアプリケーションにメール送信機能を組み込む際は、要件に応じて以下の 2 つの主要な方法を選択できます。

**1. MailSender: プレーンテキストメール送信**

   - `MailSender` は、`org.springframework.mail` パッケージに含まれる基本的なインターフェースで、主にシンプルなプレーンテキストメールの送信に使用されます。
   - `SimpleMailMessage` クラスを利用して、件名、本文、送信先などを簡単に設定できるため、英語の通知メールやアラートメールなど、簡易的な用途に適しています。

**2. JavaMailSender: HTML メールや添付ファイルの送信など**

   - `JavaMailSender` は、`MailSender` を拡張したインターフェースで、より複雑なメールの送信をサポートします。HTML メールや添付ファイルを含むメール、または日本語などのマルチバイト文字を扱う際に推奨されます。
   - `MimeMessage` クラスを用いて、メールの内容を柔軟に構築することが可能です。
   - 特に、`MimeMessageHelper` を使用すると、メールのフォーマットやエンコーディングが簡単に設定でき、直感的に HTML メールを作成できます。

今回はより柔軟性が高く、HTML メールやマルチバイト文字を含むメールを簡単に扱える `JavaMailSender` を使用した実装方法を説明します。

```java:EmailSenderClient.java
@Component
public class EmailSenderClient  {

    private final JavaMailSender javaMailSender;  // 1. JavaMailSender の準備

    @Autowired
    public EmailSenderClient(JavaMailSender javaMailSender) {
        this.javaMailSender = javaMailSender;
    }

    public void sendMail(String emailAddress) {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();  // 2. MimeMessage の作成

        try {
            // 3. MimeMessageHelper を使用して MIME メッセージを構成
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
            helper.setFrom("me@mail.com");
            helper.setTo(emailAddress);
            helper.setSubject("件名");
            helper.setText("<html><body><h3>テストメール</h3>本文</body></html>", true);  // 第二引数に true を指定することで、Content-Type が text/html になる

            javaMailSender.send(mimeMessage);  // 4. メールの送信
		} catch (MessagingException | MailException ex) {
			System.err.println(ex.getMessage());
        }
    }
}
```

**1. JavaMailSender の準備**

`JavaMailSender` インターフェースをインジェクトします。

メール送信の基盤となる `org.springframework.mail` パッケージには、基本的なメール送信操作を定義する `MailSender` インターフェースが含まれています。このインターフェースを利用することで、メール送信に関する多くの手間を省くことができます。

とくに Spring Boot では、`spring-boot-starter-mail` をプロジェクトに追加することで `JavaMailSender` が自動的に構成されるため、設定は必要ありません。

**2. MimeMessage の作成**

`JavaMailSender` の `createMimeMessage` メソッドを使用して `MimeMessage` オブジェクトを作成します。

`MimeMessage` は Jakarta Mail の標準クラスで、マルチバイト文字、HTML、添付ファイルを含む複雑なメール構造を直感的に扱うことができます。

**3. MimeMessageHelper を使用してメッセージを構成**

`MimeMessageHelper` クラスを使用して、`MimeMessage` の設定を行います。

`MimeMessageHelper` は、JavaMail API の冗長さを解消し、`MimeMessage` オブジェクトの作成を簡素化するために設計されたクラスです。
コンストラクタには `true` を指定することで、multipart メッセージを使用することを示しています。また、日本語を適切に扱うために、文字エンコーディングとして UTF-8 を指定します。

**4. メールの送信**

構築した `MimeMessage` を `JavaMailSender` の `send` メソッドで送信します。

このメソッドは、プロパティファイルで設定した SMTP サーバーを介して受信者にメールを届ける役割を果たします。

### 5. 使用例

`EmailSenderClient` クラスを使用して実際にメールを送信する例を示します。

```java:MailController.java
@RestController
@RequestMapping("/mail/send")
public class MailController {

    private final EmailSenderClient emailSenderClient;

    public MailController(EmailSenderClient emailSenderClient) {
        this.emailSenderClient = emailSenderClient;
    }

    @PostMapping
    public void sendMail(@RequestParam String from) {
        emailSenderClient.sendMail(from);
    }
}
```

このコントローラクラスは、`/mail/send` エンドポイントを介してメールを送信するためのエントリーポイントとして機能します。POST リクエストを受け取ると、`EmailSenderClient` を利用して指定されたメールアドレスにメールが送信されます。

### 6. 動作確認

最後にアプリケーションを起動させて、正しく動作するか確認しましょう。

以下のコマンドを実行するなどして、アプリケーションを起動させてください。

```shell
$ ./gradlew bootRun
```

アプリケーションが起動したら、ブラウザや Postman などのツールを使用して、以下のようにリクエストを送信します。

```
POST http://localhost:8080/mail/send?from=you@mail.com
```

このリクエストを送信すると、指定されたメールアドレスに対してメールが送信されます。

ブラウザで http://localhost:8025 にアクセスして MailHog の Web UI を開くと、次のように送信されたメールが一覧に追加されているはずです。

![mailhog2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671461/b1554f48-5098-603e-5fc9-8bbbfbaf163c.png)

メールを選択すると、メールの内容や詳細を確認できます。

![mailhog3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3671461/1c99d535-4fa6-fd47-15f9-93b460f7611f.png)

送信元 (From) にはリクエストに指定したメールアドレスが使用され、HTML フォーマットのメール本文も問題なく表示されていますね。

# さいごに

今回は Spring Boot と MailHog を使用して、ローカル環境でメールを送信する方法について説明しました。

実際の開発では、この基本的な実装に加えて、セキュリティ面でのメールヘッダ・インジェクション攻撃への対策、パフォーマンスを考慮した適切なタイムアウト値の設定、そしてシステムの信頼性を高めるための適切な例外処理の実装などに注意を払う必要があります。
これらの要素を十分に考慮しながら、Spring Boot の提供する利点を最大限に活用することで、効率的かつ堅牢なメール送信機能を実装することができるかと思います。

Spring Boot の強力な機能と MailHog の便利さにより、非常に簡単にメール送信機能が実装できることがお伝えできれば幸いです。

それでは、最後までお読みいただき、ありがとうございました。

# 参考

▼ [E-mail送信(SMTP) — Macchinetta Server Framework Development Guideline](https://macchinetta.github.io/server-guideline-thymeleaf/current/ja/ArchitectureInDetail/MessagingDetail/Email.html)
▼ [Sending Email :: Spring Boot](https://docs.spring.io/spring-boot/reference/io/email.html)
▼ [Email :: Spring Framework](https://docs.spring.io/spring-framework/reference/integration/email.html)
▼ [Guide to Spring Email | Baeldung](https://www.baeldung.com/spring-email)