Next.js でフロントエンドアプリを作成し、AWS Lambda API を実行する方法

## はじめに
- この記事では、Next.jsを使ってフロントエンドアプリを作成し、AWS Lambdaを使ってバックエンドAPIを実行する方法を紹介します。読者は、フロントエンドからLambdaを利用することで、サーバーレスなAPIを簡単に呼び出す方法について学ぶことができます。
---

## 開発環境
開発環境の詳細を以下に示します。
- OS : Windows 11
- 言語 : Python 3.12.6
- フレームワーク : Next.js、AWS Lambda
- ライブラリ : React、Requests

## 実装
次に、実装の手順を詳しく記載します。

### 1. Next.js プロジェクトの作成
- まず、アプリを作成するためのフォルダを作成し、その階層でフロントエンドアプリを置くためのフォルダを作成します。
```
$ mkdir project
$ cd project
$ mkdir frontend
```
- 次に、frontendフォルダに移動し、Next.jsのプロジェクトを作成します。
```
$ cd frontend
npx create-next-app@latest .
```
下記のように質問されるので全てenterでインストールを完了します。
![スクリーンショット 2024-09-26 184407.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/c525b538-d379-b554-6f22-bf23d8f226a3.png)



- 次に、依存関係をインストールします。
```
$ npm install
```
- アプリの起動を行います
下記のコマンドを実行し、[localhost:3000](http://localhost:3000/)にアクセスしてみましょう。
```
$ npm run dev
```
このような画面が表示されれば準備はOKです。
![画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/a713cab7-c22d-115c-48f0-428fa0f30b55.png)

### 2. Next.js の基本設定とカスタマイズ
- 初期画面を編集してみます
localhost:3000にアクセスする際に描画されているのは、frontend/app/page.tsxというファイルの中身になります。
以下のようにコードを編集し、「Hello World」を表示するようにします。

```tsx:page.tsx
import Image from "next/image";
 
export default function Home() {
  return (
    <div className="flex items-center justify-center min-h-screen">
      <h1 className="text-4xl font-bold">Hello World</h1>
    </div>
  );
}
```
VScodeのコードの変更をCtrl+Sで保存をした後、localhost:3000をリロードして変更を確認してみてください。下記のような画面が表示されるはずです。

![スクリーンショット 2024-09-27 103957.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/91278ca7-9e50-d111-82f7-f9260a511220.png)


### 3. AWS Lambda を使ったバックエンドの構築
次に、AWSのLambdaで作成した関数(API)をフロントエンドで実行してみたいと思います。
Lambda関数の作成方法については[こちらの記事](https://qiita.com/naito_kc/items/785f7eaa7deecfea32ed)を確認してください。
作成したLambda関数は、クエリパラメータに名前を追加することでウェブサイトからも確認できる。というものでした。
今回は、フロントエンドからのAPIの呼び出し時に名前のクエリを渡してコードを実行するという実装を行っていきます。

- AWS Lambdaの関数のコード
```python:lambda_function.py
import json

def lambda_handler(event, context):
    # クエリパラメータから名前を取得
    name = event.get('queryStringParameters', {}).get('name', 'Lambda')
    
    # レスポンスメッセージを作成
    message = f'Hello from {name}!'
    
    # レスポンスを返す
    return {
        'statusCode': 200,
        'body': json.dumps({
            'message': message
        }),
        'headers': {
            'Content-Type': 'application/json'
        }
    }
```
### AWS Lambdaで作成した関数URLをもとに、バックエンドで実行できるファイルを作成する

- frontendフォルダと同じ階層にbackendフォルダを作成
```
$ cd project
$ mkdir backend
```
- backendフォルダの中に、`hello_lambda.py`ファイルを作成します。AWS Lambdaで作成した関数URLをもとに、APIを実行するためのpythonコードを作成します。
※コード内のURLはご自身のものに書き換えてください
```python:backend/hello_lambda.py
import requests

def get_response(name):
    url = f"https://o642rppuzz3snj2p2eqgv75esm0guytz.lambda-url.ap-northeast-1.on.aws/?name={name}"
    
    try:
        response = requests.get(url)
        response.raise_for_status()  # エラーがあれば例外を発生させる
        return response.json()
    except requests.RequestException as e:
        return f"エラーが発生しました: {e}"

# 使用例
name = "yamazaki"
result = get_response(name)
print(result["message"])
```
- PowerShellからこの`hello_lambda.py`ファイルを呼び出してみます
```
$ cd backend
$ python hello_lambda.py
```
下記のように「Hello from [name]!」となれば正しく呼び出せています。
![スクリーンショット 2024-09-27 100400.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/0ecd1461-14a6-1a6e-8233-759a5b9ebeff.png)
### フロントエンドから呼び出す
- frontend/page.tsxの編集

AWS Lambdaをもとに作成した下記のコードをフロントエンドから呼び出すために、page.tsxのコードを下記のように書き換えます。
```tsx:page.tsx
"use client";

import { useState, useEffect } from 'react';

export default function Home() {
  const [message, setMessage] = useState<string>('');
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const name = 'y-yamazaki';
        const url = `https://o642rppuzz3snj2p2eqgv75esm0guytz.lambda-url.ap-northeast-1.on.aws/?name=${encodeURIComponent(name)}`;
        const response = await fetch(url);
        const data = await response.json();
        setMessage(data.message);
      } catch (error) {
        console.error('Error fetching data:', error);
        setMessage('エラーが発生しました');
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  return (
    <div className="flex flex-col items-center justify-center min-h-screen">
      <h1 className="text-4xl font-bold mb-4">yamazaki
      </h1>
      {loading ? (
        <p className="text-xl">読み込み中...</p>
      ) : (
        <p className="text-xl">{message}</p>
      )}
    </div>
  );
}
```
### コードの解説
- `use client` : Next.jsの"use client"モードを有効化しています。このモードを有効化することでブラウザ側でJavaScriptが動作するように設定されています。
- `import { useState, useEffect } from 'react';` : useState と useEffect は React のフックです。これらを利用することで、関数コンポーネントで状態管理や副作用（API呼び出しなどの処理）を扱えるようにしています。
- `useState` : useState フックは、コンポーネント内で状態（state）を管理するために使用されます。ここでは、2つの状態を定義しています。
  - message: Lambda API から取得したメッセージを格納するための状態です。初期値は空文字 ''。
  - loading: データがロード中かどうかを示すブール値で、初期値は true です。非同期処理が完了したら false になります。
- `useEffect` : useEffect は、副作用を扱うためのフックです。ここでは、コンポーネントが初めてレンダリングされた時に、Lambda API からデータを取得する処理を実行しています。配列の依存関係（[]）が空なので、この処理はコンポーネントの初回レンダリング時にのみ実行されます。
- `fetchData` : 関数は非同期関数 (async) で、以下の処理を行います
  - URLの生成: name パラメータを含む API リクエスト用の URL を生成します。
  - fetch: JavaScriptの fetch メソッドを使って API からデータを取得し、レスポンスを JSON 形式で解析します。
  - エラーハンドリング: エラーが発生した場合は、catch ブロックでエラーメッセージをコンソールに表示し、message に「エラーが発生しました」と設定します。
  - finally: 処理が終了した後、loading を false に設定して、読み込み中の状態を終了します。

### Lambda関数のCORS設定
AWS Lambdaのデフォルトの設定では、CORS設定がオフになっており、このままでは任意ドメインからのアクセスは制御されています。
そのため、フロントエンドからの呼び出した際にエラー画面が表示されてしまうと思います。
下記の手順で設定を追加します。

- AWS コンソールにログインしてLambdaリソースにアクセスし、設定タブから関数URLを選択し、編集をクリック。
![スクリーンショット 2024-09-30 090204.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/6041ddb5-8c3a-a544-d9f8-7bdb37f8c20a.png)

- 「オリジン間リソース共有(CORS)を設定」にチェックを入れる
![スクリーンショット 2024-09-30 090223.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/8067ba66-51fe-b0d3-95a6-05576cd9de17.png)

これで設定は完了です。

### 実行結果の確認
PowerShellにてfrontendの階層に移動したのちアプリの起動を行いましょう。
```
$ cd frontend
$ npm run dev
```
[localhost:3000](http://localhost:3000/)にアクセスします。
下記のような画面が表示されたら、完成です！
![スクリーンショット 2024-09-27 112524.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885032/096da2f2-fdac-e03a-c4f3-1fe85efefa42.png)


## まとめ
今回は、Next.jsのプロジェクト作成を行い、localhost:3000に表示される初期画面の編集操作を学びました。また、AWS Lambdaにて作成したAPIをフロントエンドから実行する実装を行いました。AWS Lambdaを操作するのは初めてでしたが、思っていた以上に簡単にサーバーレスAPIを作成でき、その手軽さに驚きました。