ローコード（GAS）&サーバーレスでWebサービスを創る

# はじめに
この記事は [LIFULL Advent Calendar 2021](https://qiita.com/advent-calendar/2021/lifull) の14日目の記事です。
今回も、愛してやまないGASについて書きます。

# 今回作ったもの
社内限定で使えるフリマサービス（社内版ジモティー？）を作ってみました。

* 入稿画面：Googleフォーム
* データベース：スプレッドシート
* イメージサーバー：Googleドライブ
* Webサーバー：Googleサイト（＋GASが吐き出したパーツ）
* 言語：GAS（Google Apps Script）
* 認証：Googleアカウントそのもの

上記のような形でほとんどの機能をGoogleAppsに頼ることで、
少しのコードを書いただけで立派なWebサービスが出来上がります。:santa:

# Webサービスができるまでの流れ
以下の順番で説明します。
①入稿＆入稿管理
②サイト表示

# ①入稿＆入稿管理
サイトに表示させたい項目を用意します。
最初にも書きましたが、Googleフォームを使うことで、入稿機能を実装します（ノーコード）。
Googleフォームの利点は、アップロードするファイルの制御機能が備わっているため、
何もプログラムを書く必要はありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238981/e624e0eb-f3b3-05df-db62-49bffa36ce59.png)
なお、アップロード先のフォルダはGoogleドライブのどこに保存したいかを自分で指定します。

自由に設定いただければと思いますが、私はこんな感じで項目を用意しました。

* 出品者名
* 品物の名称
* 品物の画像
* 価格
* サイズ、品番、使用具合など
* 売却済み☑

ポイントが何点かあります。
それは、設定画面で以下を設定することをお勧めします。

* 「メールアドレスを収集する」をONにする
* 「回答のコピーを回答者に送信」にて「常に表示」を選択する
* 「回答の編集を許可する」をONにする
* 「〇〇と信頼できる組織のユーザーに限定する」をONにする ※組織外のアクセスを制御したい場合

これによって、出品者が誰であるかが明確になるため、
出品者不明な事態にならないですし、
『「回答のコピーを回答者に送信」にて「常に表示」を選択する』をすることで、
掲載した後、売れた、もしくはあげた場合に、受信メールからフォームを編集し、
フォームの項目に設けた「売却済み」にチェックを入れることで、
サイトからの掲載を出品者自身が落とせるようになります。
※他者による改ざんができないよう、管理用のスプレッドシートには保護をかけておくことをお勧めします。

# ②サイト表示
GoogleサイトとGASを連携してウェブサイトを構築する方法については、
２年前に私が投稿した「[Googleサイトの痒いところに手を届かせる方法](https://qiita.com/saitotakashi/items/9bde870bd7286b3274f4)」をご覧ください。

先程入稿したデータをスプレッドシートに吐き出すように設定し、
以下のコードでその情報を読み込みます。

売却済みのチェックが入っている場合、配列への格納をスキップ。
画像は、「 https://drive.google.com/open?id=xxx 」の形でスプレッドシートに入っています。
複数枚アップした場合はカンマで区切られています。
上記のパスのままだとサイト上で表示されないため、
一度画像のIDを抜き出し、GriveAppオブジェクトにしてダウンロード用URLを取得して格納します。
また、組織内限定で表示させたい場合はアクセストークンを付与します。

```javascript:コード.gs
function doGet() {
  var sh = SpreadsheetApp.openById("xxx");
  var ss = sh.getSheetByName("フォームの回答 1");
  var itemName, ownerName, itemPrice, comment, photos;

  var values = ss.getRange(2, 1, ss.getLastRow()-1, ss.getLastColumn()).getValues();
  var itemInfo = [];
  var photos, photoFile, photoFileId;
  for (var i=0; i<values.length; i++) {
    // 売却（or 受け渡し）が終了している場合はスキップして非表示にする
    if (values[i][6] != ''){ continue; }
    ownerName = values[i][2];
    itemName  = values[i][3];
    itemPrice = values[i][4];
    comment   = values[i][5];
    if (values[i][7]) {
      photos = values[i][7].split(", ");
      for (var j=0; j<photos.length; j++) {
        photoFileId = photos[j].replace("https://drive.google.com/open?id=", "");
        photoFile = DriveApp.getFileById(photoFileId);
        photos[j] = photoFile.getDownloadUrl() + '&access_token=' + ScriptApp.getOAuthToken();
      }
    } else {
      photos = [];
    }
    itemInfo.push({
      "ownerName": ownerName,
      "itemName" : itemName,
      "itemPrice": itemPrice,
      "comment"  : comment,
      "photos"   : photos
    });
  }

var t = HtmlService.createTemplateFromFile("index");
t.itemInfo = itemInfo;

return t.evaluate();
}
```

上記ファイルでスプレッドシートの情報を変数に格納したら、
下記のhtmlファイルにて出力します。
（NO IMAGEの場合はCSSで見せ方を整えていますが、割愛します）

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <?for(var i = 0; i < itemInfo.length; i++){?>
      <table>
      <tr>
      <td width="250">
        <b><?=itemInfo[i]['itemName']?></b><br /><br />
        出品者：<?=itemInfo[i]['ownerName']?><br />
        価格：<?=itemInfo[i]['itemPrice']?><br />
        コメント：<br /><?=itemInfo[i]['comment']?><br />
      </td>
      <td>
      <?if(itemInfo[i]['photos'].length > 0){?>
      <?for(var j = 0; j < itemInfo[i]['photos'].length; j++){?>
        <img src="<?=itemInfo[i]['photos'][j]?>" width="150" height="150" />
      <?}?>
      <?}else{?>
        <span class="noimage" title="No Image"></span>
      <?}?>
      </td>
      </tr>
      </table>
      <hr />
    <?}?>
  </body>
</html>
```

上記のファイルを作ったら、「公開」>「ウェブアプリケーションとして公開...」でデプロイします。
URLが生成されるので、それをコピーしてブラウザ表示します。
すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238981/1ff99373-59ce-95cf-a43f-4fc15357a6a2.png)
こんな感じのアウトプットが出来上がるはずです。
（２つ目の入稿では会社のロゴ画像を用いました）

後は、Googleサイトの管理画面上から「埋め込む」を選ぶと、URLを入力する欄が出てくるので、
ここで先ほどデプロイした際に生成されたURLを貼り付けます。
（生成されるURLを直接公開用に使わないのは、デプロイごとにURLが変わってしまうため）

なお、今回はなるべく機能をシンプルにして実装を簡素化したため、
買いたい or もらいたい人は、購入フォームを経由せず、直接出品者に連絡してもらうといったやり方を想定して作っています。

# 最後に
いかがでしたでしょうか。
今回真新しいものは何も使っておらず、ありものを組み合わせただけです。
ちょっとしたコードと工夫で実用的なサービスが作れますので、是非TRYしてみてください。