独自APIを使った戦国武将紹介ページの作成

## はじめに
初めましての人もそうでない人もこんにちは！

今回はSheetDBを使って独自APIを作りそれを使った紹介サイトを作ってみようと思います！

今回は初めてJavaScriptを使った開発をしてみます！
いつもならTypeScriptを使って作成をしていますが今回は気分転換にJavaScriptを使った開発をしてみたいなーと思ったので開発をします！

ぜひ最後までご覧ください！

## そもそもSheetDBとは？
Googleスプレッドシートを使ってノーコードでREST API作成してくれるサービスです！
RESTとは？　APIとは？・・・
ここら辺は別の記事をご覧になった方がわかりやすくまとめてあるので別途調べてください！

この前のGeminiAPIから思っていましたがGoogleさんすごいな・・・

## 今回扱うデータの説明
| カラム名 | 説明 |
| :---: | :---: |
| id | 独自に示した武将ごとの識別番号 |
| names | 武将の名前 |
| country | 出身国 |
| age | 享年 |
| nickname | 別名 |

となっています！これらのデータを元に作ってみたいと思います！

そしてついでにGoogleMapの機能を使って武将が住んでいた地域を表示させたいと思います！

## 作ってみよう！
### CSVファイル作成
まずはGoogleスプレッドシートを開いてもらってこのように作ってみてください！
|       | **A** |     **B**     | **C**   | **D**  | **E**            |
|-------|-------|-----------|---------|--------|------------------|
| **1** | **id** | **names** | **country** | **age** | **Nickname**      |
| **2** | 1     | 織田信長    | 尾張     | 47     | 第六天魔王          |
| **3** | 2     | 上杉謙信    | 越後     | 48     | 越後の龍           |
| **4** | 3     | 武田信玄    | 甲斐     | 51     | 甲斐の虎           |
| **5** | 4     | 島津義久    | 薩摩     | 78     | 引きこもり戦国大名   |
| **6** | 5     | 大友宗麟    | 豊後国   | 57     | ドン・フランシスコ   |
| **7** | 6     | 龍造寺隆信   | 肥前国   | 55     | 肥満の大将         |
| **8** | 7     | 長宗我部元親 | 土佐     | 60     | 土佐の出来人        |
| **9** | 8     | 三好長慶    | 阿波国   | 42     | 副将軍             |
| **10** | 9     | 毛利元就    | 安芸     | 74     | 戦国の雄           |
| **11** | 10    | 徳川家康    | 三河国   | 73     | お館様             |
| **12** | 11    | 南部晴政    | 陸奥国   | 65     | null             |
| **13** | 12    | 伊達政宗    | 出羽国   | 68     | 独眼竜            |
| **14** | 13    | 最上義光    | 出羽国   | 68     | 虎将              |
| **15** | 14    | 北条早雲    | 小田原   | 63     | 後北条氏           |
| **16** | 15    | 今川義元    | 三河国   | 41     | 海道一の弓取り     |
| **17** | 16    | 小田氏治    | 常陸国   | 68     | 不死鳥            |
| **18** | 17    | 尼子経久    | 安芸     | 82     | null             |
| **19** | 18    | 朝倉義景    | 越前     | 39     | null             |
| **20** | 19    | 浅井長政    | 尾張国春日井郡 | 28     | null             |
| **21** | 20    | 豊臣秀吉    | 尾張     | 61     | ハゲネズミ         |

おそらく間違っている情報が含まれているとは思いますが今回はこれで進ませていただきます！
これを入力したら作成したスプレッドシートのURLをコピーして終了です！

### SheetDBにアクセス
https://sheetdb.io/

次にこちらのURLにアクセスしてください！

アクセスしていただいたら画面左にある
「Create Free API」をクリックしてください！

右上にある
「＋ Create new API」をクリックすると
「Google Spreadsheet URL」と表示された画面がでます！

その画面に先ほどコピーしたスプレッドシートのURLをペーストしてください！

おそらく中央に「https\://sheetdb.io/api/v1/<ユニークID>」が表示されたと思います！

そのURLにアクセスしてみるとJSON形式で表示されれば成功です！

## コーディングしよう！

皆さんお疲れ様です！あともう少しなので頑張りましょう！

今回もReactを使うので以下のコードをターミナルに入力してください！
```
npx create-react-app sengoku
cd sengoku
npm start
```

そうしたらsengokuフォルダの中のsrcフォルダの中にあるApp.jsファイルを開いてください！
そしたら以下のコードをコピペしてください！

```js:src/App.js
import React, { useEffect, useState } from "react";
import "./App.css";

const Rectangle = ({ data }) => {
  const isOdaNobunaga = data.id === "1";
  const isUesugiKenshin = data.id === "2";
  const isTakedaShingen = data.id === "3";
  const isShimazuYoshihisa = data.id === "4";
  const isOtomoSorin = data.id === "5";
  const isRyuzoujiTakanobu = data.id === "6";
  const isChosokabeMotonari = data.id === "7";
  const isMiyoshiNagayoshi = data.id === "8";
  const isMoriMotochika = data.id === "9";
  const isTokugawaIeyasu = data.id === "10";
  const isNanbuHarumasa = data.id === "11";
  const isDateMasamune = data.id === "12";
  const isMogamiYoshiaki = data.id === "13";
  const isHojoUjimasa = data.id === "14";
  const isImagawaYoshimoto = data.id === "15";
  const isOdaNobuhime = data.id === "16";
  const isAmagoTunehisa = data.id === "17";
  const isAsakuraYoshikage = data.id === "18";
  const isAsaiNagamasa = data.id === "19";
  const isToyotomiHideyoshi = data.id === "20";

  return (
    <div className="pokemonCardContainer">
      <div className="diamond">
        <h3>名前: {data.names}</h3>
        <h3>出生地: {data.country}</h3>
        <h3>享年: {data.age}</h3>
        <h3>二つ名: {data.Nickname}</h3>
      </div>
      {isOdaNobunaga && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 織田信長`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isUesugiKenshin && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 上杉謙信`}
            src={
              "<ユニーク>"
            }
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isTakedaShingen && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 武田信玄`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isShimazuYoshihisa && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 島津義久`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isOtomoSorin && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 大友宗麟`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isRyuzoujiTakanobu && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 龍造寺隆信`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isChosokabeMotonari && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 長宗我部元親`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isMiyoshiNagayoshi && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 三好長慶`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isMoriMotochika && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 毛利元就`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isTokugawaIeyasu && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 徳川家康`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isNanbuHarumasa && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 南部晴政`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isDateMasamune && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 伊達政宗`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isMogamiYoshiaki && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 最上義光`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isHojoUjimasa && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 北条早雲`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isImagawaYoshimoto && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 今川義元`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isOdaNobuhime && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 小田氏治`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isAmagoTunehisa && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 尼子経久`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isAsakuraYoshikage && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 朝倉義景`}
            src={`<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isAsaiNagamasa && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 浅井長政`}
            src={`https://www.google.com/maps/embed?pb=<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
      {isToyotomiHideyoshi && (
        <div className="mapContainer">
          <iframe
            title={`Google Maps - 豊臣秀吉`}
            src={`https://www.google.com/maps/embed?pb=<ユニーク>`}
            width="100%"
            height="250px"
            style={{ border: "0" }}
            allowFullScreen=""
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          ></iframe>
        </div>
      )}
    </div>
  );
};

const App = () => {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("https://sheetdb.io/api/v1/<ユニークID>");
        const jsonData = await response.json();
        setData(jsonData);
      } catch (error) {
        console.error("Error fetching data:", error);
      }
    };

    fetchData();
  }, []);

  return (
    <div className="centerContainer">
      <h1 className="umebana">戦国武将一覧</h1>
      <div className="a"></div>
      {data.map((item) => (
        <Rectangle key={item.id} data={item} />
      ))}
    </div>
  );
};

export default App;
```
これで、表示されるはずです！

<ユニーク>となっているところにはGoogleMapの共有リンクを貼り付けするところになっています！

次にApp.cssを使ってデザインしてみようと思います！


@mei331(Mei Koutsuki)様

https://qiita.com/mei331/items/d1d47f506ed76c57f38c

https://b-moon.net/css-japanese-pattern/

こちらの2つのサイトにあるデザインを参考にしました！

```src/App.css
.App {
  text-align: center;
  width: 100%;
  height: 100vh;
}

.centerContainer {
  text-align: center;
  background-size: 50px 50px;
  background-image: radial-gradient(closest-side, rgba(9, 150, 35, 0.583) 98%, rgba(54, 180, 88, 0.13) 100%),
                    radial-gradient(circle at top left, rgba(53, 255, 107, 0.488) 35%, rgba(142, 218, 61, 0) 35% 100%),
                    radial-gradient(circle at top right, rgba(53, 255, 107, 0.542) 35%, rgba(142, 218, 61, 0) 35% 100%),
                    radial-gradient(circle at bottom left, rgba(53, 255, 107, 0.443) 35%, rgba(142, 218, 61, 0) 35% 100%),
                    radial-gradient(circle at bottom right, rgba(53, 255, 107, 0.546) 35%, rgba(142, 218, 61, 0) 35% 100%);
  background-repeat: repeat;
}



.pokemonCardContainer {
  border: #0c0c0c 1px solid;
  padding: 2rem .5rem;
  background-color: white;
  width: calc(30% - 10px);
  display: inline-block;
  align-items: center;
  margin-right: 10px;
  box-shadow:#0c0c0c 3px 5px 0;
  font-family: 'Noto Serif JP', serif;
  position: relative;
  margin-bottom: 30px;
  height: 500px;
  overflow: hidden;
}

@media (max-width: 768px) {
  .pokemonCardContainer {
    width: 100%; 
    margin-right: 0;
  }
}

h1.umebana {
  -webkit-writing-mode: horizontal-tb;
  -ms-writing-mode: horizontal-tb;
  writing-mode: horizontal-tb;
  font-family: 'Noto Serif JP', serif;
  background-color: white;
  padding: 10px; 
  border-radius: 50%; /* 丸型にする */
  display: inline-block; /* 背景を内容に合わせて丸くするため */
  line-height: 300px; 
  height: 300px;
  width: 300px;
  text-align: center;
  vertical-align: middle; /* テキストを垂直方向に中央揃えにする */
  margin-left: -3%;
  margin-top: 3%;
}

.diamond{
  display: inline-block;
}


h1.umebana:before{
  content:' ';
  display:inline-block;
  width:40px;
  height:40px;
  background-image: 
  radial-gradient(circle, transparent 50%, #e6c619 50%, #e6c619 50.5%, transparent 50.5%),
  radial-gradient(circle, transparent 50%, #e6c619 50%, #e6c619 50.5%, transparent 50.5%),
  radial-gradient(circle, transparent 50%, #e6c619 50%, #e6c619 50.5%, transparent 50.5%),
  radial-gradient(circle, transparent 50%, #e6c619 50%, #e6c619 50.5%, transparent 50.5%),
  radial-gradient(circle, transparent 50%, #e6c619 50%, #e6c619 50.5%, transparent 50.5%);
  background-position: 50% 0, 5% 40%, 95% 40%, 23% 95%, 78% 95%;
  background-size: 20px 20px;
  background-repeat:no-repeat;
  margin-bottom:1px;
}

  .white-square {
    border: 1px solid black;
    margin: 5px;
    padding: 10px;
    background-color: white;
    display: inline-block;
  }
  
.btn button{
  padding: 13px 32px;
  background-color: #5257a3;
  border: none;
  box-shadow: 2px 8px 21px -2px #777;
  border-radius: 5px;
  color: white;
  cursor: pointer;
  transition: all 0.3s;
}

.btn button:hover{
  transform: translateY(5px);
  box-shadow: none;
}
/* iPhone用のスタイル設定 */
@media only screen and (max-width: 375px) {
  .pokemonCardContainer {
    width: 42%;
    margin: 6px 5px;
    height: 530px;
  }
  .mapContainer {
    position: absolute;
    bottom: 2px;
    width: 90%;
    border: #0c0c0c 1px solid;
  }
}
```

今回作成した画面が
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3631203/0987adb2-393f-1d20-0a7c-7786b874ca79.png)

このように表示されました！

## さいごに
今回の開発で一番大変だったのがCSVファイルにデータを作る作業が一番大変でした・・・
次点でGoogleMapから共有リンクをコード内にコピペすることですね・・・

大変ではありますが多くの方に見られるようなサイトを作ることができればとてもやりがいのある開発だと思います！

皆さんもぜひ使ってみてください！

最後まで読んでいただきありがとうございました！
またどこかの記事でお会いしましょう！