はじめに
よく「フロントエンドは○○で、バックエンドは××で」みたいな記述を見かけますが、フロントエンドとバックエンドの連携というのが調べてもパッと出てこなく、学習初期によく疑問に思っていたのでまとめました。
前提知識
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
package main
1-3. テストデータの作成
jsonで返すテストデータを作成します。
(本来はここはDBから取得するものとなります。)
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行追加します。
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関数内で呼び出します。
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ブラウザの場合
以上で一旦完了です。
一旦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も使わないので削除します。
import React from 'react';
import './App.css';
function App() {
return (
<div>
</div>
);
}
export default App;
今回受け取るデータの型を定義します。
合わせてテストデータを作成し、ブラウザで正しく表示できることを確認します。
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;
ブラウザに正しく表示できました。
正しく表示できることが確認できたので、テストデータは消しておきます。
(一時的にエラーとなります)
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を作成します。
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を使用してサーバーにリクエストを送信します。
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エラーが発生しているのが確認できます。
これはサーバー(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
を指定します。
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
にアクセスし、ブラウザをリフレッシュすると、データが取得できていることが確認できます。
以上でフロントエンドとバックエンドの通信が実現できました。
なお、Goにはフロント作成用のテンプレートがありますが、実務ではあまり使用されず、ReactやVueといったフロントエンドフレームワークと組み合わせて使用されるのが一般的なようです。
以上です。