21
14

More than 1 year has passed since last update.

【React×Go】フロント(React)とバック(Go)で連携する

Last updated at Posted at 2022-10-19

はじめに

よく「フロントエンドは○○で、バックエンドは××で」みたいな記述を見かけますが、フロントエンドとバックエンドの連携というのが調べてもパッと出てこなく、学習初期によく疑問に思っていたのでまとめました。

前提知識

ReactとGoの基本知識があることが前提。

Go

  • APIサーバーを立ててjsonデータを返すことができる

React

  • useState, useEffect
  • axios

構成

フロントエンド:React
バックエンド:Go

フロントとバックの連携という1点に集中したいので、DBは使わず、テストデータを直書きしています。

1. Goでjsonデータを返す

1-1. 適当な作業ディレクトリを作成します。

~/Desktop$ mkdir dev
~/Desktop$ cd dev
~/Desktop/dev$ mkdir go-app // goアプリ用
~/Desktop/dev$ cd go-app
~/Desktop/dev/go-app$ code . // vs codeで開く場合

以下の作業はgo-app内で実施します。

1-2. main.goファイルの作成

go-app内にmain.goファイルを作成し、パッケージ名を宣言します。

~/Desktop/dev/go-app$ touch main.go
main.go
package main

1-3. テストデータの作成

jsonで返すテストデータを作成します。
(本来はここはDBから取得するものとなります。)

main.go
package main

type fruit struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	Icon string `json:"icon"`
}

var fruits = []fruit{
	{ID: 1, Name: "apple", Icon: "🍎"},
	{ID: 2, Name: "banana", Icon: "🍌"},
	{ID: 3, Name: "grape", Icon: "🍇"},
}

1-4. jsonデータを返す関数の作成

作成したテストデータを返す関数(ここではgetFruitsとします)を作成します。
あとでCORSエラーに対応するため、コードを1行追加します。

main.go
package main

import (
	"encoding/json"
)

type fruit struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	Icon string `json:"icon"`
}

var fruits = []fruit{
	{ID: 1, Name: "apple", Icon: "🍎"},
	{ID: 2, Name: "banana", Icon: "🍌"},
	{ID: 3, Name: "grape", Icon: "🍇"},
}

func getFruits(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
    // ここにCORS対応コードを追加します。
	json.NewEncoder(w).Encode(fruits)
}

1-5. 作成した関数の呼び出し

作成したgetFruits関数をmain関数内で呼び出します。

main.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type fruit struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	Icon string `json:"icon"`
}

var fruits = []fruit{
	{ID: 1, Name: "apple", Icon: "🍎"},
	{ID: 2, Name: "banana", Icon: "🍌"},
	{ID: 3, Name: "grape", Icon: "🍇"},
}

// ※Goではコードの記述順序は関係ないので、上に書いても下に書いても構いません。
func main() {
	http.HandleFunc("/", getFruits)
	fmt.Println("Starting server at port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func getFruits(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
    // ここにCORS対応コードを追加します。
	json.NewEncoder(w).Encode(fruits)
}

1-6. サーバー起動&jsonデータ取得

サーバーを起動します。

~/Desktop/dev/go-app$ go run main.go
Starting server at port 8080

この状態でhttp://localhost:8080にリクエストを送ると、jsonデータが返ってきます。

・curlの場合

~/Desktop& curl http://localhost:8080
[{"id":1,"name":"apple","icon":"🍎"},{"id":2,"name":"banana","icon":"🍌"},{"id":3,"name":"grape","icon":"🍇"}]

・Webブラウザの場合

image.png

以上で一旦完了です。
一旦Ctrl + Cでサーバーを止めておきます。

2.Reactでリクエストを送信する

2-1. Reactプロジェクトの作成&起動

作業ディレクトリでReactアプリを作成します。

~/Desktop/dev$ npx create-react-app react-app --template typescript

作成したReactアプリのディレクトリに移動してアプリを起動します。

~/Desktop/dev$ cd react-app
~/Desktop/dev/react-app$ npm start

http://localhost:3000にアクセスし、Reactアプリが起動していることを確認します。

2-2. データ型の作成&表示

App.tsxを開きます。
return文の中身を外側のdivを残して一旦削除します。
一番上のimport logo, import cssも使わないので削除します。

App.tsx
import React from 'react';
import './App.css';

function App() {
  return (
    <div>

    </div>
  );
}

export default App;

今回受け取るデータの型を定義します。
合わせてテストデータを作成し、ブラウザで正しく表示できることを確認します。

App.tsx
import React from 'react';

type Fruit = {
  id: number;
  name: string;
  icon: string;
}

var fruits = [
  { id: 1, name: "apple", icon: "🍎" },
  { id: 2, name: "banana", icon: "🍌" },
  { id: 3, name: "grape", icon: "🍇" },
]

function App() {
  return (
    <div>
      {fruits.map(fruit => (
        <p key={fruit.id}>
          <span>{fruit.name}</span>
          <span>{fruit.icon}</span>
        </p>
      )
      )
      }
    </div>
  );
}

export default App;

ブラウザに正しく表示できました。

image.png

正しく表示できることが確認できたので、テストデータは消しておきます。
(一時的にエラーとなります)

App.tsx
import React from 'react';

type Fruit = {
  id: number;
  name: string;
  icon: string;
}

function App() {
  return (
    <div>
      {fruits.map(fruit => (
        <p key={fruit.id}>
          <span>{fruit.name}</span>
          <span>{fruit.icon}</span>
        </p>
      )
      )
      }
    </div>
  );
}

export default App;

2-3. データを保持するstateの作成

useStateで非同期で取得したデータを保持するstateを作成します。

App.tsx
import React, { useState } from 'react';

type Fruit = {
  id: number;
  name: string;
  icon: string;
}

function App() {
  const [fruits, setFruits] = useState<Fruit[]>([{ id: 0, name: "", icon: "" }])

  return (
    <div>
      {fruits.map(fruit => (
        <p key={fruit.id}>
          <span>{fruit.name}</span>
          <span>{fruit.icon}</span>
        </p>
      )
      )
      }
    </div>
  );
}

export default App;

2-4. axiosで非同期リクエストを送り、jsonデータを取得する

まずはaxiosをインストールします。

~/Desktop/dev/react-app$ npm install axios

package.jsonにaxiosが追加されていることが確認できます。

次にuseEffect, axiosを使用してサーバーにリクエストを送信します。

App.tsx
import React, { useState, useEffect } from 'react';
import axios from "axios";

type Fruit = {
  id: number;
  name: string;
  icon: string;
}

function App() {
  const [fruits, setFruits] = useState<Fruit[]>([{ id: 0, name: "", icon: "" }])

  useEffect(() => {
    (
      async () => {
        const data = await axios.get("http://localhost:8080")
        console.log(data.data)
        console.log(data.data[0])
        setFruits(data.data)
      }
    )()
  }, [])

  return (
    <div>
      {fruits.map(fruit => (
        <p key={fruit.id}>
          <span>{fruit.name}</span>
          <span>{fruit.icon}</span>
        </p>
      )
      )
      }
    </div>
  );
}

export default App;

1で構築したサーバーにリクエストを送ります。
サーバーを起動します。

~/Desktop/dev/go-app$ go run main.go
Starting server at port 8080

ブラウザでhttp://localhost:3000にアクセスすると真っ白い画面が表示され何も表示されていません。
開発者ツールを開きます。
するとCORSエラーが発生しているのが確認できます。

image.png

これはサーバー(http://localhost:8080)に異なるorigin(http://localhost:3000)からアクセスしていることが原因です。

CORSに関してはこちらの動画がわかりやすいので、こちらでご確認ください。

【CORS入門】もうCORSエラーに苦しむことはありません。Webエンジニア必見です。

2-5. CORSの解消

Ctrl+ C で一度goのサーバーを停止します。

CORS解消のため、レスポンスヘッダーのAccess-Control-Allow-Origiにリクエスト元の http://localhost:3000を指定します。

main.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type fruit struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
	Icon string `json:"icon"`
}

var fruits = []fruit{
	{ID: 1, Name: "apple", Icon: "🍎"},
	{ID: 2, Name: "banana", Icon: "🍌"},
	{ID: 3, Name: "grape", Icon: "🍇"},
}

func main() {
	http.HandleFunc("/", getFruits)
	fmt.Println("Starting server at port 8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

func getFruits(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") // 追加
	json.NewEncoder(w).Encode(fruits)
}

再度Goのサーバーを起動します。

~/Desktop/dev/go-app$ go run main.go
Starting server at port 8080

http://localhost:3000にアクセスし、ブラウザをリフレッシュすると、データが取得できていることが確認できます。

image.png

以上でフロントエンドとバックエンドの通信が実現できました。
なお、Goにはフロント作成用のテンプレートがありますが、実務ではあまり使用されず、ReactやVueといったフロントエンドフレームワークと組み合わせて使用されるのが一般的なようです。

以上です。

21
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
14