Loading
BLOG 開発者ブログ

2024年9月19日

ReactとAWSでサーバレスアプリを構築してみた

Reactアプリの作成からデプロイまで、一連の流れをまとめました。

はじめに

こんにちは、クラウドソリューショングループのkitajima.eです。

この記事では、「イチから」Reactアプリを構築できるようにするために必要な情報を整理しつつ、環境構築から、実際に作成したReactアプリをS3にデプロイするところまでをやってみます。

今回作成するReactアプリから、API Gatewayエンドポイントを使用したLambda関数の呼び出しを行っており、APIが異なるドメインからリクエストを受け入れる場合などに必要なCORS設定に触れることで、より実践的な内容の学習に役立ちます。

AWS LambdaとDynamoDBについては、前記事の LambdaとDynamoDBを使ってみた で紹介しています。
今回の記事で使用しているAPIの作成などに関して触れているので学習にお役立てください。

目次

Reactとは

ReactはJavaScriptのライブラリで、主にWebアプリケーションのUIを構築するために使用します。
サーバ側で処理をせず、ユーザ側で表示内容の処理を行うためサーバ負荷が抑えられ、アクセスしてからの読み込み速度が速い特徴があります。
コンポーネント指向のためUIを部品に分けて開発を行うことで再利用性が高く、開発効率が向上するメリットがあります。

 

API Gatewayとは

API Gatewayは、APIを作成・公開・保守・モニタリングするために使用します。
AWS Lambdaと連携して、サーバレスでAPIを構築することができる特徴があります。
複雑なインフラ管理を必要とせず、APIの設定が簡単で非常に便利なツールです。

 

CORSとは

CORSは、とあるオリジン(ドメイン)で動作しているWebアプリケーションが異なるオリジンにあるリソースにアクセスできるようにする仕組みです。
ブラウザはセキュリティ上の理由から、異なるオリジン間のリクエストを制限しており、その制限を緩和するための仕組みとしてCORSが存在します。
レスポンスヘッダーで、どのオリジンからのリクエストを許可するかを指定することができ、不正なクロスオリジンリクエストを防いでセキュリティを強化することができます。

 

S3とは

Amazon S3はAmazonのストレージサービスです。
Webサーバのように静的なWebサイトの公開や、動画・画像などの格納先として幅広く利用することが可能です。

ここでは作成したReactアプリを格納してWebサイトの公開を行ない、バケットの作成手順に慣れてもらえればと思います。

 

開発環境構築

構築手順作業の簡単な流れとしては、以下です。

  1. Node.jsのバージョン確認
  2. プロジェクトの作成

    Node.jsのバージョン確認

    Node.jsのインストール手順やバージョン確認については、前記事の LambdaとDynamoDBを使ってみた で紹介している内容と同じになります。
    ここではNode.jsのバージョンが以下であることとします。

    $ node -v
    v20.17.0
    
    $ npm -v
    10.8.2

    プロジェクトの作成

    npmコマンドでReactプロジェクトを作成することができます。
    プロジェクトフォルダを作成するディレクトリへ移動してから、以下コマンドを実行してください。

    $ npm init react-app sample

    プロジェクトが作成できたら、プロジェクト配下へ移動し、以下コマンドでプロジェクトを実行してみましょう。

    $ npm start

    ブラウザに以下画面が表示されたらOKです!

    環境構築手順が完了しました!

    Reactアプリを作ってみる

    まずはAPIとデータ送受信をできるようにするため、
    ターミナルで以下コマンドを実行してください。

    $ npm install axios --save

    これによりReactアプリからHTTP通信を簡単に行うことができます。

    それでは実際にReactアプリの実装を行ってみましょう。

     

    Reactアプリ実装

    ここでは前記事で作成したDynamoDBデータ登録APIを利用したフォームの作成を行います。

    import React, { useState } from "react";
    import axios from "axios";
    import "./Form.css";
    
    function Form() {
      const [user_id, setUser_id] = useState("");
      const [task_id, setTask_id] = useState("");
      const [date, setDate] = useState("");
      const [message, setMessage] = useState("");
      const [quantity, setQuantity] = useState("");
    
      // データ登録
      const handleWrite = async () => {
        try {
          const response = await axios.post("https://your-api-gateway-endpoint", {
            user_id: user_id,
            task_id: task_id,
            date: date,
            message: message,
            quantity: quantity,
          });
          console.log("Write response:", response.message);
        } catch (error) {
          console.error("Error writing data to API", error);
        }
      };
      return (
        <div className="Form">
          <header className="header">
            <h1>DB登録</h1>
          </header>
          <div className="Contents">
            <label>
              user_id:
              <input
                type="text"
                value={user_id}
                onChange={(e) => setUser_id(e.target.value)}
                placeholder="ユーザーIDを入力"
              />
            </label>
            <br />
            <label>
              task_id:
              <input
                type="text"
                value={task_id}
                onChange={(e) => setTask_id(e.target.value)}
                placeholder="タスクIDを入力"
              />
            </label>
            <br />
            <label>
              date:
              <input
                type="text"
                value={date}
                onChange={(e) => setDate(e.target.value)}
                placeholder="日付を入力(YYYYMMDD)"
              />
            </label>
            <br />
            <label>
              message:
              <input
                type="text"
                value={message}
                onChange={(e) => setMessage(e.target.value)}
                placeholder="メッセージを入力"
              />
            </label>
            <br />
            <label>
              quantity:
              <input
                type="text"
                value={quantity}
                onChange={(e) => setQuantity(e.target.value)}
                placeholder="数量を入力"
              />
            </label>
            <br />
            <button onClick={handleWrite}>登録</button>
          </div>
        </div>
      );
    }
    
    export default Form;
    import React from "react";
    import ReactDOM from "react-dom/client";
    import "./index.css";
    import Form from "./Form";
    import reportWebVitals from "./reportWebVitals";
    
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <React.StrictMode>
        <Form />
      </React.StrictMode>
    );
    
    reportWebVitals();

    これでフロントの実装が完成しました!

     

    デプロイしてみる

    それでは実際にデプロイしてみましょう!
    以下のコマンドを実行してビルドを行います。

    $ npm run build

    完了したら、AWSマネジメントコンソールのナビゲーションバー上の検索ボックスに「S3」と入力します。
    サービスの中からS3が見つかるので、選択してS3ダッシュボードを開きます。
    ナビゲーションペインから バケット を選択し、以下バケット作成手順を進めてください。

    S3バケット作成手順

    • 「汎用バケット」タブ画面右上から「バケットを作成」を選択
    • 「バケット名」をお好みに設定し、画面下部「バケットを作成」を押下
    • 作成したバケットにアクセスし、プロパティタブから「静的ウェブサイトホスティング」の編集を選択
    • 静的ウェブサイトホスティングから「有効にする」を選択し、変更を保存
    • アクセス許可タブから「ブロックパブリックアクセス(バケット設定)」の編集を選択
    • ブロックパブリックアクセス(バケット設定)から「パブリックアクセスをすべてブロック」のチェックを外し、変更を保存
    • アクセス許可タブから「バケットポリシー」の編集を選択
    • 下記バケットポリシーをコピーし、バケットポリシーエディターに貼り付け
      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Sid": "PublicReadGetObject",
                  "Effect": "Allow",
                  "Principal": "*",
                  "Action": [
                      "s3:GetObject"
                  ],
                  "Resource": [
                      "arn:aws:s3:::Bucket-Name/*"
                  ]
              }
          ]
      }
    • Bucket-Nameをバケット名に合わせて変更し、変更の保存

    バケットが作成できました!

    上記手順で作成したバケットを選択すると、対象のバケットにアクセスすることができます。
    オブジェクトタブ画面右上「アップロード」を選択し、先ほど実施したReactアプリのビルドによって作成された以下のフォルダ配下のファイルを全てアップロードをします。

    sample
      └ build

    プロパティタブ下部の「静的ウェブサイトホスティング」にバケットウェブサイトエンドポイントが存在していると思います。
    ブラウザからエンドポイントへアクセスすると、作成したReactアプリの画面を確認することができます。

     

    作成したReactアプリをS3にデプロイできました!

    CORSの設定

    次に今回呼び出すAPIに対してAPI GatewayからCORSの設定を行います。

    AWSマネジメントコンソールのナビゲーションバー上の検索ボックスに「API Gateway」と入力します。
    サービスの中からAPI Gatewayが見つかるので、選択してAPI Gatewayダッシュボードを開きます。
    ナビゲーションペインからリソースを選択し、以下CORS設定有効化手順を進めてください。

    CORS設定有効化手順

    • 対象のリソースを選択
    • リソースの詳細から、「CORSを有効にする」を押下
    • Access-Control-Allow-Headers / Access-Control-Allow-Originを設定し、保存
    • OPTIONSメソッドが追加されていることを確認

    また、対象のLambda関数のレスポンスに以下CORSヘッダーを追加する必要があります。

    return formatJSONResponse({
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin": "*", // アクセスを許可するオリジンを指定
        "Access-Control-Allow-Methods": "POST,GET,OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
      },
      message: `${event.body.task_id}の書き込みに成功しました。`,
      event,
    });

    上記変更を保存し、APIを再デプロイします。

    これで、API GatewayでCORSが有効になり、ReactアプリからAPIを呼び出すことができるようになります。

    テストしてみる

    それでは実際にデプロイしたReactアプリの画面を操作し、DynamoDBにデータが登録されることを確認しましょう。

    まずはDynamoDBの状態を確認します。
    現在、PKが[ID:0001] / SKが次で始まる[ShoppingList:]とした場合に以下の1件がヒットする状態です。

    Reactアプリ画面上のフォームを以下のように入力し、登録ボタンを押下します。

     

    DynamoDBのテーブルを見に行きましょう。

    先ほどと同様に、PKが[ID:0001] / SKが次で始まる[ShoppingList:]とした場合に2件ヒットし、Reactアプリのフォームから登録した値が確認できます。

    これで、ReactアプリからdbRegister の機能でDynamoDBへデータの登録が正常に行われていることが確認できました!

     

    さいごに

    今回はReactを用いたフロント開発に触れ、S3へのReactアプリのデプロイやフロントの画面操作からLambdaのAPI呼び出しができるようになりました。

    Reactは大規模開発に向いているため、さまざまなコンポーネントを組み合わせて、より実践的なアプリ開発を行っていきましょう。

    AWSには今回登場したLambdaやDynamoDB、S3の他にもさまざまなサービスが存在します。
    今後の展望としてそれらと紐づけてより高度な実装ができればと思います。

     

    kitajimaeのブログ