昔懐かしのアクセスカウンターを作った話

この記事は[Civictech１年目 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/civictech_rookies) 12日目の記事です。

11日目は @t-mat さんの[シビックテック1年目の取組と課題](https://termat.hatenablog.com/entry/2020/12/11/%E3%82%B7%E3%83%93%E3%83%83%E3%82%AF%E3%83%86%E3%83%83%E3%82%AF1%E5%B9%B4%E7%9B%AE%E3%81%AE%E5%8F%96%E7%B5%84%E3%81%A8%E8%AA%B2%E9%A1%8C)でした。
私も今年入ってから初めてシビックテックというものを知ったので、この後t振り返ろうかなと思っています。

# TL;DR
「BADオープンデータ供養寺」建立の宮大工をしていた時に作ったアクセスカウンターが同できてるかをゆるゆる書くっていう記事です。

BADオープンデータ供養寺のソースコードは https://github.com/codeforjapan/bad-opendata-temple です！
いまだに面白いissueが残ってたりしますので皆さんもぜひ...!

BADオープンデータ供養寺自体は https://bad-data.rip にありますので興味のある方はぜひ。（.ripなんてドメイン初めて聞いた...）

# はじめに
## BADオープンデータ供養寺とは
同じく12日目の[Civictech テック好き](https://qiita.com/advent-calendar/2020/civictech_tech)のカレンダーに書いてらっしゃる @kaizumaki さんの [BADオープンデータ供養寺、匠たちの技](https://medium.com/@kaizumaki/bad-opendata-temple-miya-carpenters-e3047c29336a)から引用をさせていただきまして...

オープンデータ供養寺とは
> 住職（※1）の「ぶっ飛んだデザインにした方が良いのでは」というひとことに、腕に覚えありの宮大工（※2）達が結集し、懐かしのインターネッツネタをモダンコーディングで再現しようと力を合わせたのでした。

> ※1…言わずと知れた我らがリーダー、下山紗代子氏のこと。オープンデータの普及に東へ西へと駆け回っている。すっかり「住職」と呼ばれるのが板についている。
※2…私たちの間ではエンジニアのことを「宮大工」、デザイナーのことを「宮造りデザイナー」としている。そして彼らの名前は「〜〜匠」と呼ばれる。

書いてある通りではあるんですが、

- カーソルをローソクにする
- お経を流す
- お鈴を鳴らす
- 餅を投げる

だったり、

- [MARQUEEタグ](http://www.htmq.com/html/marquee.shtml)じゃないマーキー
- CGIじゃないアクセスカウンター

など、ローテク？昔懐かし？のようなハイテクノロジー（）の数々があります

私はこの `CGIじゃないアクセスカウンター`を作ったのでこれについてこのあとかこうかと思います（ほかのことについては引用元をぜひ...）

# CGIじゃないアクセスカウンターってどゆこと？？？？
タイトルにも書いてる通り、今回のアクセスカウンターにCGIを使っていません！
正真正銘Reactのコンポーネントです。
（このタイトルで読みに来る方は大体CGIが何かわかってると思うのでそこは書きません）

（なぜcgiじゃないのかというとそれをホストするばしょがないからですね）

アクセスカウンターなのでアクセス数を記録しないといけない。
しかもそれは共通させなきゃいけないのでどこかにDBを持つ必要がある。
じゃあどうしてるのか....
実はGoogle Spreadsheet + Google App Scriptでできています。

## React側
```tsx:accessCounter.tsx
import React, {
  ReactElement,
  useEffect,
  useState,
} from 'react';
import styled from 'styled-components';

const AccessCounterContent = styled.div`
  font-size: 30px;
`;

const NumberText = styled.span`
  color: #ebff00;
  background-color: #000000;
  padding: 2px 4px;
  margin: 0 2px;
  box-sizing: border-box;
`;

const AccessCounter: React.FC = (): ReactElement => {
  const [accessStr, setAccessStr] = useState('LOADING');
  useEffect(() => {
    fetch(
      'https://script.google.com/macros/s/えーぴーあいの/exec',
    )
      .then((response) => {
        return response.json();
      })
      .then((json) => {
        setAccessStr(
          String(json.your_access).padStart(4, '0'),
        );
        return json;
      })
      .catch((_) => {
        setAccessStr('ERROR');
      });
  }, []);

  return (
    <AccessCounterContent>
      あなたは
      {[...accessStr].map((str, index) => {
        return (
          <NumberText key={str + String(index)}>
            {str}
          </NumberText>
        );
      })}
      人目の参拝者です
    </AccessCounterContent>
  );
};

export default AccessCounter;
```

 分けて解説をすると..

```tsx
const AccessCounterContent = styled.div`
  font-size: 30px;
`;

const NumberText = styled.span`
  color: #ebff00;
  background-color: #000000;
  padding: 2px 4px;
  margin: 0 2px;
  box-sizing: border-box;
`;
``` 
ここでデザインをかいていて...

``` tsx
const AccessCounter: React.FC = (): ReactElement => {
  const [accessStr, setAccessStr] = useState('LOADING');
  useEffect(() => {
    fetch(
      'https://script.google.com/macros/s/えーぴーあいのid/exec',
    )
      .then((response) => {
        return response.json();
      })
      .then((json) => {
        setAccessStr(
          String(json.your_access).padStart(4, '0'),
        );
        return json;
      })
      .catch((_) => {
        setAccessStr('ERROR');
      });
  }, []);
```
React FCを使っているので、useStateでStateを作成（初期値にLOADINGと文字列を入れておきます）
useEffectで`componentDidMounted`でこの後作るAPIにアクセスしてアクセス数を読み取ります。
ちなみに

```tsx
String(json.your_access).padStart(4, '0'),
```
これは23とかで帰ってきた数字を0023とかにするものです

```tsx
  return (
    <AccessCounterContent>
      あなたは
      {[...accessStr].map((str, index) => {
        return (
          <NumberText key={str + String(index)}>
            {str}
          </NumberText>
        );
      })}
      人目の参拝者です
    </AccessCounterContent>
  );
```
これは、表示を
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275457/9d0c5547-b7b9-57b1-67e9-d476fad8978e.png)
こうしたかったので、
`[...accessStr].map((str, index) => {`で一文字ずつに分割してmapで回して、

```tsx 
          <NumberText key={str + String(index)}>
            {str}
          </NumberText>
```

で一文字ずつ分割したコンポーネントとしています。

これが、Reactコンポーネントの動きです。

## App Scriptのスクリプトくん
```js
function doGet(e) {
  var sheet = SpreadsheetApp.getActive().getSheetByName('シート1');
  var accessNumber = Number(sheet.getRange("A1").getValues()) + 1;
  sheet.getRange("A1").setValue(accessNumber);
  var result = {
    "your_access": accessNumber
  }
  var out = ContentService.createTextOutput();

    //Mime TypeをJSONに設定
  out.setMimeType(ContentService.MimeType.JSON);

    //JSONテキストをセットする
  out.setContent(JSON.stringify(result));
  return out;
}
```
簡単な内容ですのでほぼ解説しませんが....
スプシはつないでいるので

```js
  var sheet = SpreadsheetApp.getActive().getSheetByName('シート1');
  var accessNumber = Number(sheet.getRange("A1").getValues()) + 1;
  sheet.getRange("A1").setValue(accessNumber);
```
でA1に格納している数値を取得して

```js
  var result = {
    "your_access": accessNumber
  }
  var out = ContentService.createTextOutput();

    //Mime TypeをJSONに設定
  out.setMimeType(ContentService.MimeType.JSON);

    //JSONテキストをセットする
  out.setContent(JSON.stringify(result));
  return out;
```

でgetへの返り値としてます

あとはこれを`ウェブアプリケーションとして導入`をすればアクセスカウンターのAPIの出来上がりです！

# おわりに
結構前からいんたーねっとにいる学生ではあるのでアクセスカウンターのcgiは見たことはあるし、使ったこともあるんですが実際に作ったのは初めてでした...
そしてそれ以上にcgiじゃないアクセスカウンターを作ることになるとは.....

こんな面白いものがいろいろそろったBADオープンデータ供養寺をぜひご覧ください！（https://bad-data.rip）