🍄

React✖️AxiosでAPIにリクエストを送信しHelloWorldを表示する

2023/12/28に公開

はじめに

こちらに移行しました🙇
https://kubodai-code.com/spring-react9/

バックエンドにSpringBoot3(REST API)フロントエンドにReactを使用してSNS風のWebアプリケーションを作っていきます。
勉強も兼ねて記録に残したいと思いますので誰かの役に立てば嬉しいです😊

今パートの目標

前回はリクエストを送ると「HelloWorld」というテキストをレスポンスとして返すHelloControllerの作成をしました。
今回はReactをAxiosを使用してAPIの作成したHelloControllerに向けてリクエストを送信し、取得した結果を画面に表示していきたいと思います。

環境について

react 18.2.0
Typescript 4.9.5
SpringBoot 3.2.0
java 17
git 2.43.0
macOS Sonoma 14.1.1
Docker Desktop 4.18.0

作業用ブランチを作成する

share-favplace-front % git fetch
share-favplace-front % git checkout -b develop
share-favplace-front % git checkout -b develop_20231225_requestAPI 

Axiosをインストールする

share-favplace-front % docker-compose run --rm front npm install axios

AxiosとはHTTPクライアントライブラリです。PromiseベースのAPIを提供しているので非同期処理が扱いやすく、JSONデータの自動変換も行ってくれるためレスポンスデータの処理がしやすくなっています。

https://axios-http.com/docs/intro

Axiosのconfigファイルを作成する

axiosのグローバル設定を行うための設定ファイルを作成します。

share-favplace-front % mkdir ./src/configs
share-favplace-front % touch ./src/configs/axios.ts

axios.tsを編集する

axios.ts
import axios from "axios";

let instance = axios.create({
  baseURL: process.env.REACT_APP_API_URL,
});

// 本番環境以外はリクエストとレスポンス時にデバッグ用ログを出力する
if (process.env.REACT_APP_ENV !== 'production') {
  instance.interceptors.request.use(
    (config) => {
      console.log(config);
      return config;
    },
    (error) => {
      console.log(error);
      return Promise.reject(error);
    }
  );
  
  instance.interceptors.response.use(
    (response) => {
      console.log(response);
      return response;
    },
    (error) => {
      console.log(error);
      return Promise.reject(error);
    }
  );
}

export default instance;

コードを説明していきます

let instance = axios.create({
  baseURL: process.env.REACT_APP_API_URL,
});

baseURL(APIのURL)を設定しています。
環境によって変更したいため、"REACT_APP_API_URL"という環境変数を参照するように記述します。環境変数は後で登録します。
開発環境では"http://loclahost:8080"がここに入ります。

REACT_APP_{変数名}という環境変数名で登録するとReactアプリからprocess.env.{環境変数名}で環境変数を取得することができます。

// 本番環境以外はリクエストとレスポンス時にデバッグ用ログを出力する
if (process.env.REACT_APP_ENV !== 'production') {
  instance.interceptors.request.use(
    (config) => {
      console.log(config);
      return config;
    },
    (error) => {
      console.log(error);
      return Promise.reject(error);
    }
  );
  
  instance.interceptors.response.use(
    (response) => {
      console.log(response);
      return response;
    },
    (error) => {
      console.log(error);
      return Promise.reject(error);
    }
  );
}

"REACT_APP_ENV"という環境変数を登録し、本番環境以外はデバッグログを出力するようにしています。

instance.interceptors.request.use()

リクエスト送信時に実行したい処理を記述します。

instance.interceptors.response.use()

レスポンス受け取り時に実行したい処理を記述します。
これを使用して共通エラーハンドリング処理を作成することなどもできます。

環境変数を登録する

axios.tsで参照する環境変数を登録します。

react-app-variables.envを作成する

share-favplace-front % mkdir environment
share-favplace-front % touch environment/react-app-variables.env

react-app-variables.envを編集する

react-app-variables.env
# react
REACT_APP_ENV=development
REACT_APP_API_URL=http://localhost:8080/

envファイルはgithubにあげる必要は無いので.gitignoreに登録しておきましょう

.gitignore
*.env

docker-compose.ymlを編集する

docker-compose.yml
services:
  front:
    build:
      context: .
      args:
        WORKDIR: ${WORKDIR}
    container_name: share-favplace-front
+   env_file:
+     - ./environment/react-app-variables.env
    command: npm run start
    volumes:
      - .:/${WORKDIR}
    ports:
      - "${FRONT_PORT}:${FRONT_PORT}"
    networks:
      - front

networks:
  front:
    external: false

環境変数ファイルを登録するように変更します。

APIにリクエストを送信する

App.tsxを編集する

App.tsxでボタンを押下するとリクエストを送信するように編集していきましょう

App.tsx
import { useState } from 'react';
import axios from './configs/axios';

export default function App() {
  const [text, setText] = useState('');

  const handleClick = () => {
    axios.get('/api/v1/hello')
      .then((res) => {
        setText(res.data)
      })
  };

  return (
    <div>
      <div className='flex justify-center mt-10'>
        <button type="button" className="text-white bg-gradient-to-r from-purple-500 to-pink-500 hover:bg-gradient-to-l focus:ring-4 focus:outline-none focus:ring-purple-200 dark:focus:ring-purple-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2" onClick={() => handleClick()}>Click!</button>
      </div>
      <div className="flex justify-center mt-5">
        <h1 className="text-3xl font-bold underline">
          { text }
        </h1>
      </div>
    </div>
  )
}

下記のような画面になります

少しコードの説明をします

<button ... onClick={() => handleClick()}>Click!</button>

ボタンのクリック操作時にhandleClick()というメソッドを実行するという意味になります

const handleClick = () => {
    axios.get('/api/v1/hello')
      .then((res) => {
        setText(res.data)
      })
};

axiosを使用して'/api/v1/hello'にリクエストを送信し、レスポンスをtext変数にセットします。
axiosの設定ファイルでbaseURLを設定しているのでhttp://localhost:8080/api/v1/hello にリクエストを送信します。

const [text, setText] = useState('');

useState('')により、変数の状態を管理します。
これによって、変数に値が代入された際に画面の表示を適切に変更することができます。
Reactのhooksについては以下動画がとても分かりやすいのでReactを触る前に一度視聴することをお勧めします。
https://www.youtube.com/watch?v=uuAdVs7sbAs&t=425s

<h1 className="text-3xl font-bold underline">
	{ text }
</h1>

ここにレスポンスで取得したtextを表示します。

リクエストを送信する

Click!ボタンを押してリクエストを送信してみましょう

しかし今のままでは画像の通り、エラーとなってしまいます
重要なのはこのエラーメッセージです。

Access to XMLHttpRequest at 'http://localhost:8080/api/v1/hello' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

上記より、CORSエラーとなっていることが分かります。

SpringBoot3のCORS設定

CORSとは

CORSとはCross-Origin Resource Sharingの略で、「オリジン間リソース共有」という意味です。
つまりあるオリジンで動いている Web アプリケーションに対して、別のオリジンのサーバーへのアクセスをオリジン間 HTTP リクエストによって許可できる仕組みのことです。
今回の場合はフロントエンドとバックエンドで異なるオリジンとなっているため、アクセスを許可する設定をしてあげる必要があります。
バックエンドで設定するのが一般的なため、SpringBootの機能を使って設定していきます。

Origin(オリジン)とは

protocol(プロトコル) + domain(ドメイン) + port number(ポート番号)のことです。
上記3つの項目が全て一致していないと異なるオリジンとなります。
今回の場合は開発環境の各オリジンは以下のようになるため、CORSの設定が必要となります。
※本番環境でも異なります。

フロントエンド

http://localhost:3000

バックエンド

http://localhost:8080

CORSの設定

CORS設定をしていきます。
SpringBootのCORS設定には複数の方法があります。
詳細についは以下の記事を参照してください。
https://b1san-blog.com/post/spring/spring-cors/
公式
https://spring.pleiades.io/guides/gs/rest-service-cors/
今回は設定用のクラスを作成し、設定していく方法を取りたいと思います。

configパッケージを作成する

以下階層にconfigパッケージを作成します。

- src
  └ main
    └ java
      └ {パッケージ名}
        └ config

WebMvcConfigクラスを作成する

先ほど作成したconfigパッケージにWebMvcConfig.javaを作成します。

- src
  └ main
    └ java
      └ {パッケージ名}
        └ config
	  └ WebMvcConfig.java

WebMvcConfig.javaを編集する

WebMvcConfig.java
package com.pandaman.sharefavplaceapi.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        final String origin = System.getenv("FRONT_URL");
        registry.addMapping("/**")
               .allowedOrigins(origin)
               .allowedMethods(CorsConfiguration.ALL);
    }
}

コードについて説明していきます

@Configuration

設定クラスであることを表します。このアノテーションを付与することでSpringBootが実行時に参照して設定をしてくれます。

@Override

実装しているWebMvcConfigurerインターフェースに定義されたメソッドをオーバーライドしていることを表します。

addCorsMappings(CorsRegistry registry)

このメソッドを実装し、引数のregistryに設定を追加していくことで設定していきます。

addMapping("/**")

CORS の対象となるパスを設定します。今回は全てのパスを設定しています。

allowedOrigins(String... origins)

許可するオリジン(Access-Control-Allow-Origin)を設定します。
今回はFRONT_URL(http://localhost:3000)を設定しています。

allowedMethods(String... methods)

許可するHttpリクエストメソッド(Access-Control-Allow-Methods)を設定します。
今回はCorsConfiguration.ALL(*)全てのリクエストメソッドを設定しています。
※HttpリクエストメソッドとはGetやPost、Delete等のことです。

以上で設定は完了です。SpringBootを再起動しておきましょう。

リクエストを送信する

それではもう一度Click!ボタンを押してリクエストを送信してみましょう

無事APIからHelloWorld!!!!を取得して画面に表示することができました。

githubにプッシュする

フロントエンド

share-favplace-front % git add -A .
share-favplace-front % git commit -m "axiosとAPIへのリクエスト送信処理追加" 
share-favplace-front % git push origin develop_20231225_requestAPI

バックエンド

share-favplace-api % git add -A .
share-favplace-api % git commit -m "Helloworldを返すAPIの実装追加"
share-favplace-api % git push origin develop_20231226_responseToFront

developブランチにマージする

githubにpushできたら、プルリクエストを作成し、developブランチに変更をマージしていきます。

プルリクエストを作成する


GitHubにPUSHした後にログインし、リポジトリのページに行くとアラートが出ているので「Compare&PullRequest」を押下しプルリクエストを作成します。


対象ブランチを「develop←develop_20231225_requestAPI」に変更し、差分を確認します。
差分に問題がなければ、「Create pull request」を押下し、プルリクエストを作成します。

developブランチにマージする


先ほど作成したプルリクエスト画面から「Merge pull request」を押下し、変更をマージします。

上記をフロントエンドバックエンドともに行ったら完了です。

おまけ

なぜわざわざプルリクエストを作成するのか

個人開発では特に意味のない無駄な作業に思えるプルリクエストですが、
実務ではプルリクエストを作成して別の人にコードレビューをしてもらい、問題がなければマージするというような流れをとるのが一般的だと思います。
なので、実務に沿った開発を個人開発でもしている方が、チーム開発でもGitHubを扱えるというアピールにもなるかなと思い、僕はそのようにしています。

まとめ

以上でaxiosを使用して、API側にリクエストを送信し、HelloWorld!!!!を表示するところまでできました!
ここまでできればあとは自分で作りたいアプリに合わせて機能を追加していくだけですね😆
次回はtailwindcssのテンプレートを使用して簡単にトップページを作成してみたいと思います。

Discussion