実践WEBビーコン:メール開封率計測テクニックの全て
この記事は「レバテック開発部 Advent Calendar 2024」の 3 日目の記事です!
昨日の記事は、もりた さんの 年末に(私が)読みたい! 要件定義のおすすめ書籍ズ23冊 でした。
これはなに?
皆さん、こんにちは!最近、インドネシア旅行に行ったら現地の方より顔が濃くて、道を歩いていると現地語で話しかけられた@tomo_nxnです。
さて、そんな旅のエピソードはさておき、今回はメール配信システムの機能を使わずに、独自で Web ビーコンを使用してメール開封率を計測する仕組みを作ったので、その時の注意点や実際の実装方法などを紹介していきます!
前提
レバテック開発部では、メール配信にSendGridを使用しており、通知マイクロサービスとして独立したシステムを各システムが利用している状態です。SendGrid上でもメールの開封率は計測できるのですが、ユーザーの詳細までを知ることが難しいことと、担当プロダクトのIDと紐付けができないことがあるため、独自で実装しています。
対象読者
- 「メールの開封率?自分で作るしかない!」と奮闘中のエンジニア
- 「WEBビーコンって何者?」と好奇心が止まらないエンジニア
メール配信サービスを使用せずにどうやってメールの開封率を計測するのか?
WEBビーコン(Web Beacon)は、Webページやメールに埋め込まれる小さな透明画像(通常1×1ピクセル)やスクリプトのことです。これを用意することで、サーバー側でメールの開封を検知します。
その手順を簡単に説明すると、以下のような流れになります。
- UUIDの生成
- UUIDを埋め込んだトラッキング用画像URLをHTMLメールに設定
例:
<img src="https://hoge.com/tracking/?emailId=${uniqueEmailId}" alt="" style="display:none;" />
- リクエストの送信メールが開封されると画像が読み込まれ、設定したサーバーにリクエストが送信
- サーバー側での開封記録
サーバーはリクエストを受け取り、UUIDを照合。対応するUUIDが見つかれば「開封」として記録
ここからは、現在運用中のNestJSの実装例を基に、詳しく掘り下げていきます!
1. WEBビーコン(トラッキング画像)を作成
- 1×1ピクセルの透明画像を用意します(例:tracking.png)。
- フォーマットはGIFやPNGなど軽量な形式で。
- ファイルの実体を作っても良いですし、サーバー側でライブラリを使用してダイナミックに生成するでも良いかと思います。
2. メールのHTMLに埋め込む画像リンクを生成
計測したいメールテンプレート内に以下のような画像タグを埋め込みます。このリンクに、メールを送信するユーザーごとのユニークなIDを付けます。
<img src="https://hoge.com/tracking/?emailId=${uniqueEmailId}" alt="" style="display:none;" />
3. メール送信時にユニークIDを生成して保存
メール送信時にUNIQUE_EMAIL_IDを生成し、それをデータベースに保存します。
const uniqueEmailId = uuidV4();
// メール送信ログを保存
const notificationMailSentLog =
this.notificationMailSentLogRepository.create({
uniqueEmailId,
});
await this.notificationMailSentLogRepository.save(
notificationMailSentLog,
);
4. 画像へのリクエストを処理するエンドポイントを作成
@Controller('tracking')
export class TrackingController {
private imagePath = join(__dirname, './public/beacon.jpg');
constructor(private readonly trackingService: TrackingService) {}
@Get()
@Header('content-type', 'image/jpg')
@Header('Cache-Control', 'no-cache, no-store, must-revalidate')
@Header('Pragma', 'no-cache')
@Header('Expires', '0')
async trackEmailOpen(
@Query('emailId')
emailId: string,
@Res() res: Response,
) {
// トラッキング処理が失敗しても画像は返す
try {
await this.trackingService.handle({
emailId,
});
} catch (error) {
this.logger.warn({
error,
emailId,
});
}
// 画像を返す
const readStream = fs.createReadStream(this.imagePath);
readStream.pipe(res);
}
}
5. 開封情報を保存するサービスの作成
開封情報を記録するためのserviceを作成します。開封記録の保存先はデータベースです。
@Injectable()
export class TrackingService {
constructor(
@InjectRepository(NotificationMailSentLog)
private readonly notificationMailSentLogRepository: Repository<NotificationMailSentLog>,
@InjectRepository(NotificationUserReadLog)
private readonly notificationUserReadLogRepository: Repository<NotificationUserReadLog>,
) {}
async handle(
request: TrackingServiceRequest,
): Promise<TrackingServiceResponse> {
const emailId = request.emailId;
// メール送信ログを取得
const existingNotificationMailSentLog =
await this.notificationMailSentLogRepository.findOne({
where: {
emailId,
},
});
if (!existingNotificationMailSentLog) {
this.logger.error({
message: `指定された${emailId}は存在しません。`,
request,
});
throw new BadRequestException(
`指定された${emailId}は存在しません。`,
);
}
// ユーザーの開封を記録
const newMailRead = this.notificationUserReadLogRepository.create({
notificationMailSentLog: existingNotificationMailSentLog,
});
await this.notificationUserReadLogRepository.save(newMailRead);
return {
success: true,
};
}
}
以上の仕組みを実装することで、メールの開封時にサーバー側でログを記録できるようになります。
実際のプロジェクトでは、メールごとのkey名を併せて保存したり、中間テーブルを用意して分析に活用する形になります。
ハマりポイント
まとめ
本記事をお読みいただきありがとうございました。
今回、メール配信システムの機能を使わずに独自でWebビーコンを使用してメール開封率を計測する方法を紹介しました。これにより、ユーザーごとの詳細な開封情報を取得し、プロダクトの改善やユーザーエンゲージメントの向上に役立てることができます。
この記事が、皆様の参考になれば幸いです!
明日は daijinload さんが投稿します!
「レバテック開発部 Advent Calendar 2024」をぜひご購読ください!
Discussion