「Next.js × Vercel でお問い合わせフォームを実装💌✨ スパム対策もバッチリ！」

## 1. Next.jsプロジェクトの作成

まずは、新規Next.jsプロジェクトを作成します。以下のコマンドを実行して、プロジェクトディレクトリを用意してください。

```bash
npx create-next-app contact-form-with-vercel
cd contact-form-with-vercel
```

---

## 2. フロントエンドフォームの作成

次に、ユーザーから名前、メールアドレス、メッセージを入力してもらうフォームを実装します。`pages/contact.js`（またはTypeScriptの場合は`.tsx`）に以下のコードを追加してください。フォームは送信時にPOSTリクエストをAPIルートへ送るようにしています。

```jsx
import { useState } from "react";

export default function Contact() {
  const [form, setForm] = useState({ name: "", email: "", message: "" });
  const [status, setStatus] = useState("");

  const handleChange = (e) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setStatus("送信中...");

    // ★★ ここでreCAPTCHAトークンを取得する処理を後ほど追加可能 ★★

    try {
      const response = await fetch("/api/contact", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(form),
      });

      if (response.ok) {
        setStatus("送信成功！");
        setForm({ name: "", email: "", message: "" });
      } else {
        setStatus("送信失敗。もう一度お試しください。");
      }
    } catch (error) {
      setStatus("エラーが発生しました。");
    }
  };

  return (
    <div>
      <h1>お問い合わせフォーム</h1>
      <form onSubmit={handleSubmit}>
        <label>
          お名前:
          <input
            type="text"
            name="name"
            value={form.name}
            onChange={handleChange}
            required
          />
        </label>
        <br />
        <label>
          メールアドレス:
          <input
            type="email"
            name="email"
            value={form.email}
            onChange={handleChange}
            required
          />
        </label>
        <br />
        <label>
          メッセージ:
          <textarea
            name="message"
            value={form.message}
            onChange={handleChange}
            required
          ></textarea>
        </label>
        <br />
        {/* ★★ Honeypotフィールド ★★ */}
        <input type="text" name="honeypot" style={{ display: "none" }} />
        <button type="submit">送信</button>
      </form>
      <p>{status}</p>
    </div>
  );
}
```

---

## 3. バックエンドAPIルートの作成

`pages/api/contact.js` にAPIルートを用意し、フォームから送信されたデータを処理します。ここでは、[Nodemailer](https://nodemailer.com/) を使用してメール送信を実装します。また、スパム対策として後述するreCAPTCHAやHoneypotの検証処理も組み込みます。

### APIルートのコード例

```javascript
import nodemailer from "nodemailer";
import fetch from "node-fetch";

export default async function handler(req, res) {
  if (req.method === "POST") {
    const { name, email, message, recaptchaToken, honeypot } = req.body;

    // Honeypotフィールドが埋まっていたらスパムとしてリジェクト
    if (honeypot) {
      return res.status(400).json({ error: "スパムが検出されました。" });
    }

    // reCAPTCHAが導入されている場合、トークンの検証を実行
    if (recaptchaToken) {
      const verificationResponse = await fetch(
        "https://www.google.com/recaptcha/api/siteverify",
        {
          method: "POST",
          headers: { "Content-Type": "application/x-www-form-urlencoded" },
          body: `secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${recaptchaToken}`,
        }
      );
      const verificationResult = await verificationResponse.json();

      if (!verificationResult.success) {
        return res.status(400).json({ error: "reCAPTCHA検証に失敗しました。" });
      }
    }

    // Nodemailerの設定
    const transporter = nodemailer.createTransport({
      host: process.env.SMTP_HOST,
      port: Number(process.env.SMTP_PORT),
      secure: process.env.SMTP_SECURE === "true",
      auth: {
        user: process.env.SMTP_USER,
        pass: process.env.SMTP_PASS,
      },
    });

    try {
      await transporter.sendMail({
        from: process.env.SMTP_USER,
        to: process.env.CONTACT_RECEIVER,
        subject: `お問い合わせ：${name}`,
        text: `名前: ${name}\nメールアドレス: ${email}\nメッセージ:\n${message}`,
      });

      res.status(200).json({ success: true });
    } catch (error) {
      console.error("メール送信エラー:", error);
      res.status(500).json({ error: "メール送信に失敗しました。" });
    }
  } else {
    res.status(405).json({ error: "メソッドが許可されていません。" });
  }
}
```

### 環境変数の設定

ルートディレクトリに `.env.local` ファイルを作成し、以下のようにSMTPサーバー情報やreCAPTCHAのシークレットキーを設定してください。

```env
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-smtp-user@example.com
SMTP_PASS=your-smtp-password
CONTACT_RECEIVER=receiver@example.com
RECAPTCHA_SECRET_KEY=your-recaptcha-secret-key
```

---

## 4. スパム対策の追加

ここでは、無料で利用できる2種類のスパム防止策を紹介します。

### 4.1 Google reCAPTCHA v3 の導入

Google reCAPTCHAは、スパム対策として非常に有効です。以下の手順で実装します。

1. **Google reCAPTCHAに登録**  
   Google reCAPTCHAのサイトで、サイトキーとシークレットキーを取得してください。

2. **フロントエンドでのトークン取得**  
   フォーム送信時に `grecaptcha.execute` を呼び出し、トークンを取得してAPIルートへ送信します。例えば、`handleSubmit` 内に以下の処理を追加します。

   ```jsx
   const handleSubmit = async (e) => {
     e.preventDefault();
     setStatus("送信中...");

     // reCAPTCHAトークンの取得（事前にGoogleのスクリプトを読み込む必要あり）
     const token = await grecaptcha.execute("YOUR_SITE_KEY", { action: "submit" });
     
     try {
       const response = await fetch("/api/contact", {
         method: "POST",
         headers: { "Content-Type": "application/json" },
         body: JSON.stringify({ ...form, recaptchaToken: token, honeypot: "" }),
       });

       if (response.ok) {
         setStatus("送信成功！");
         setForm({ name: "", email: "", message: "" });
       } else {
         setStatus("送信失敗。もう一度お試しください。");
       }
     } catch (error) {
       setStatus("エラーが発生しました。");
     }
   };
   ```

3. **API側でのトークン検証**  
   前述のAPIルートコード内で、`recaptchaToken`の検証を実施しています。検証に成功すれば、メール送信処理へ進む仕組みです。

### 4.2 Honeypotフィールドの導入

Honeypotは、通常ユーザーには見えない隠しフィールドを利用してボット対策を行うシンプルな方法です。  
- **フロントエンドには隠しフィールドを追加**（上記フォーム内に実装済み）  
- **APIルートでフィールドの値が空でない場合は、リクエストを却下**  

```javascript
if (honeypot) {
  return res.status(400).json({ error: "スパムが検出されました。" });
}
```

---

## 5. Vercelへのデプロイ

最後に、完成したプロジェクトをVercelにデプロイします。Vercel CLIやGitHub連携を利用してデプロイ可能です。CLIを用いる場合は、以下のコマンドを実行してください。

```bash
vercel deploy
```

---

## フォーム送信フローの図解

以下のシンプルなフローチャートは、フォーム送信からメール送信までの流れを示しています。

```mermaid
flowchart TD
    A[ユーザー: フォーム入力] --> B[ブラウザ: POSTリクエスト]
    B --> C[APIルート（/api/contact）]
    C --> D{Honeypotチェック}
    D -- スパムなら --> E[リクエスト拒否]
    D -- 正常なら --> F[reCAPTCHA検証]
    F -- 成功なら --> G[Nodemailerでメール送信]
    G --> H[確認レスポンス]
    F -- 失敗なら --> I[エラー返却]
```

---

## 結論

この記事では、Next.jsとVercelを利用して基本的なお問い合わせフォームを構築し、ノード環境でのメール送信（Nodemailer）とともに、スパム対策としてGoogle reCAPTCHA v3およびHoneypotを組み込む方法を解説しました。  
- **Next.jsのページとAPIルート**を組み合わせることで、シンプルながら柔軟性の高いフォーム実装が可能です。  
- **スパム対策**は、ユーザー体験を損なうことなく、確実にボットからの不正送信を防止します。  

この手順を参考に、実際のプロジェクトでお問い合わせフォームを実装してみてください。エンジニア初心者でも取り組みやすい内容になっていますので、ぜひ挑戦してみましょう！

---
## **💖 ご支援いただけませんか？**

![スクリーンショット 2025-01-31 7.51.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637864/f9d94b4d-dbd3-ab70-6b2b-dfc289cda562.png)


このブログでは、高品質な情報提供と学習活動を通じて、読者の皆さまのお役に立つことを目指しています。もしこの記事が役立ったと感じていただけましたら、ご支援いただけると幸いです！

---

### **暗号資産による寄付**
以下のウォレットアドレスをご利用ください。**重要：Ethereum (ETH)、BNB Chain (BNB)、Polygon (MATIC)、Avalanche (AVAX) は、全て以下の同一アドレスを使用しますが、送金ネットワークの選択を間違えると資金が失われます！** 送金時には、**絶対に**使用するネットワーク（例: ERC-20、BEP-20、Polygon、Avalanche C-Chain）を**必ず**正しく選択してください。

---

<img src="https://cryptologos.cc/logos/ethereum-eth-logo.png" alt="Ethereum Logo" width="20">

**Ethereum (ETH)**  （ネットワーク: ERC-20）

```
0x5CDA2F68f59F641B00aD172475c3d5fC10321174
```
<img src="https://cryptologos.cc/logos/binance-coin-bnb-logo.png" alt="BNB Logo" width="20">

**BNB Chain (BNB)**  （ネットワーク: BEP-20）
```
0x5CDA2F68f59F641B00aD172475c3d5fC10321174
```
<img src="https://cryptologos.cc/logos/polygon-matic-logo.png" alt="Polygon Logo" width="20">

**Polygon (MATIC)**   （ネットワーク: Polygon）

```
0x5CDA2F68f59F641B00aD172475c3d5fC10321174  
```
<img src="https://cryptologos.cc/logos/avalanche-avax-logo.png" alt="Avalanche Logo" width="20">

**Avalanche (AVAX)**    （ネットワーク: Avalanche C-Chain）

```
0x5CDA2F68f59F641B00aD172475c3d5fC10321174
```
<img src="https://cryptologos.cc/logos/solana-sol-logo.png" alt="Solana Logo" width="20">

**Solana (SOL)**  
```
EnPFbqDbF67rU9mAPvfgh4YYtncJNbFQ9NLQ5R6z5S2f
```

<img src="https://cryptologos.cc/logos/stellar-xlm-logo.png" alt="Stellar Logo" width="20">

**Stellar (XLM)**    メモ: 必要に応じて入力してください。

```
GCSMWCACKVEZ737GZAV4AJRFL52ZZKVQ7M3B3KYY64JJGOAO2GDYKABO 
```

<img src="https://cryptologos.cc/logos/xrp-xrp-logo.png" alt="Ripple Logo" width="20"> 

**Ripple (XRP)**    タグ: 必要に応じて入力してください。

```
r1s4EASr3zQRrfpDA3ptTahezBhGo2hhN
```

<img src="https://cryptologos.cc/logos/cardano-ada-logo.png" alt="Cardano Logo" width="20">

**Cardano (ADA)**  

```
addr1q8heq6ddw8rwlqa5hqlucnfk36arah9tzc8ajxvu83870h7lrre25wzq9yemex857we56cm0xu8tmxqvm8nykmtgsjdqavdpv7
```

<img src="https://cryptologos.cc/logos/dogecoin-doge-logo.png" alt="Dogecoin Logo" width="20">

**Dogecoin (DOGE)**  
```
DRFZ9JhAk3DTtu1tV85cawekWNrm1vKm3H
```

---

### **資金用途**
寄付金は以下の目的で活用させていただきます：

1. サーバー維持費やデザインツール購入  
2. 学習活動（オンラインコース受講・書籍購入）  
3. 読者向け無料コンテンツ制作  

ご協力いただいた皆さまには心より感謝申し上げます！ 🙏

---

### **補足情報**
- **Ethereum (ETH)、BNB Chain (BNB)、Polygon (MATIC)、Avalanche (AVAX)について**  
  上記4つのネットワークは**同じウォレットアドレス**（`0x5CDA2F68f59F641B00aD172475c3d5fC10321174`）を使用します。ただし、送金時には、**絶対に**使用するネットワーク（例: ERC-20、BEP-20、Polygon、Avalanche C-Chain）を**必ず**正しく選択してください。

- USDCやUSDTなどのステーブルコインも、対応するネットワーク経由であれば送金可能です。ただし、**送金先のネットワークと選択するネットワークが一致していることを必ず確認してください。**

- 初回送金時には少額でテスト送金することをおすすめします。

---