GoとReactでAPI通信

## 目的

前回、GoとReact環境をdockerで構築
https://qiita.com/zukka/items/5a6831bef702d8c910e0

次は、フロント側のReact側からGoのAPIを呼び出して出力されたテキストをフロントに表示することを目標に。


## GoでAPIを構築

### webフレームワークGinを使う

GinはGo言語ベースのWebアプリケーションフレームワーク
軽量かつ実行速度が速いということが特徴

https://gin-gonic.com/ja/

### インストール
go-sampleというモジュール名で初期する
go mod init は新しいGoモジュールを初期化するためのコマンド

```sh
docker-compose run --rm go sh -c 'go mod init go-sample'
```

WEBフレームワークのginを取得する

```sh
docker-compose run --rm go sh -c 'go get -u github.com/gin-gonic/gin'
```

### main.go作成

goフォルダ内にmain.goファイルを作成

:::note info
main.goファイルとは
goプログラムを実行させるために必要なパッケージ

### goでのパッケージとは
プログラムのグループ分けする機能
同じディレクトリにあるソースファイルの集合
モジュール性、カプセル化、分離されたコンパイル、再利用をサポートするもの

### パッケージのルール
1.パッケージ == ディレクトリ
　プログラムを実行させるためには「main」パッケージが必須
　mainパッケージのmain関数を、 プログラムのエントリポイントとして処理

2.パッケージは名前を持つ
**パッケージ名ルール**
* 1つのディレクトリでは、1つのパッケージ名しか利用できない
* mainは特別なパッケージ名
* パッケージ名はディレクトリ名に依存しない
* パッケージ名はユニークでなくて良い

3.公開制限
* 外部に公開していい場合は名前の頭文字を大文字
* 非公開にしたい場合は名前の頭文字を小文字

:::

### main.goのソースコード

```go
package main

import (
	"log"

	"github.com/gin-gonic/gin"
)

func main() {
	log.Println("start server...")
	r := gin.Default()
	r.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{
			"message": "Hello World!",
		})
	})
	log.Fatal(r.Run(":3001"))
}
```



### main.goを実行


docker-compose.ymlのgoセクションに次を追記

```yml
go:
    container_name: go-api
    build:
      context: .
      dockerfile: DockerfileGo
    volumes:
      - ./go:/api
    command: sh -c "go run main.go"　←　追記
    ports:
      - 3001:3001
    tty: true
```



### 確認
コンテナを起して、
`http://localhost:3001/`にアクセスして出力を確認

```
{"message":"Hello World!"}
```



## フロントでAPIを受け取る

### reactのコンテナにaxiosをインストール
reactでAPIを受け取るためにHTTP クライアントaxiosをインストール

```sh
docker-compose run --rm react-app sh -c 'npm install axios'
```

### API受信用のpageを作成

src/app/にapi-testフォルダを作成し、page.tsxを作成

:::note info
Reactコンポーネント内の状態を管理する（変数）したい時はuseState
:::

```react
"use client";

import axios from "axios";
import { useState } from "react"; // コンポーネントの中で変数を扱うためにuseStateが必要

export default function Home() {
  //変数messageを宣言、messageを更新する関数setMessage、初期値'test'をセット
  const [message, setMessage] = useState('test');
  
  const getData = async () => {
    try {
      const response = await axios.get("http://localhost:3001/hello");
      setMessage(response.data['message']); //関数setMessageを実行（変数messageを更新）
    } catch (error) {
      console.error('Error fetching data:', error);
    }
  };

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
        <button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
          onClick={getData}
        >
          getDate
        </button>
      </div>
      <p>{message}</p>
    </main>
  );
}

```

### CORSエラー
このまま実行するとAPIがCORSエラーとなる。

CORSモジュールをインストール

```sh
docker-compose run --rm  go sh -c 'go get -u github.com/gin-contrib/cors'
```

### main.go修正

main.goファイルを次のように修正

```go
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-contrib/cors"
	"time"
	"log"
)

func main() {
	log.Println("start server...")
	r := gin.Default()
	// ここからCorsの設定
	r.Use(cors.New(cors.Config{
		// 許可したいアクセス元
		AllowOrigins: []string{
			"http://localhost:3000",
			"http://localhost:3001",
		},
		// 許可したいHTTPメソッド
		AllowMethods: []string{
			"GET",
			"OPTIONS",
		},
		// 許可したいHTTPリクエストヘッダ
		AllowHeaders: []string{
			"Access-Control-Allow-Credentials",
			"Access-Control-Allow-Headers",
			"Content-Type",
			"Content-Length",
			"Accept-Encoding",
			"Authorization",
		},
		// cookieなどの情報の要否
		AllowCredentials: true,
		// preflightリクエストの結果をキャッシュする時間
		MaxAge: 24 * time.Hour,
	  }))
	r.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{
			"message": "Hello World!",
		})
	})
	log.Fatal(r.Run(":3001"))
}

```


### 動作確認

`http://localhost:3000/api-test`にアクセス
getDataボタンをクリックして、Hello World!が表示される。


![スクリーンショット 2024-02-14 15.43.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17764/96e10e27-cbcd-d5fb-152e-b6833b4c3018.png)