Firebase javascript でやる初めの一歩 Windows10

私はFirebaseをアプリへのPush通知では使ったことはあるけれども、Webの方では使ったことはないので、ちょっとやってみたという感じです。

環境はWindows10です。

早い話が、下記の動画を見ればすぐわかります(笑)。

全部英語ですが、まぁソースコード見てると大体わかりますし、どうしても英語聞きたいときは0.5倍ぐらいで聞くとなんとか聞けたりします。

わかりにくいところだけ補足します。

①apikeyとかなんなのかわからない。

const firebaseConfig = {
    apiKey:これ
};

ですよね~。

これは、Firebaseの方で先にアカウントを作っておいて、「アプリを登録」ということをする必要があります。

下記のFirebaseのサイトにアクセスして、プロジェクトを作ります。

https://console.firebase.google.com/u/0/?hl=ja

その後、作ったプロジェクトに「アプリを追加」します。

Firebaseの画面 左上の「プロジェクトの概要」をクリックするとこれになります。

アプリを</>というマークがJavascriptですので、Javascriptで登録します。

すると、このapiKeyとかprojectIdとかをコピペすればすむ画面が出てきます。

②ブラウザから動作させてみる

動画の通り、index.jsとindex.htmlを作ります。

import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.0.0/firebase-app.js';
import { getAnalytics } from 'https://www.gstatic.com/firebasejs/9.0.0/firebase-analytics.js';;

// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional

const firebaseConfig = {
    apiKey: "置き換えてね",
    authDomain: "置き換えてね",
    projectId: "置き換えてね",
    storageBucket: "置き換えてね",
    messagingSenderId: "置き換えてね",
    appId: "置き換えてね",
    measurementId: "置き換えてね"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
console.log(analytics);
console.log("にゃー");
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="module" src="index.js"></script>
</head>
<body>
ウキー
</body>
</html>

動画内では

serve

というコマンドがさらっと使われております。

これ、Node.jsのコマンドなんですね。事前に

$ npm install -g serve

とやって、serveをnpmでインストールしておきます。

(この動画の冒頭でもnpm使っているので、npmが入っていないということはないと思いますが、npmが入っていなければnpmを先に入れましょう。)

はい、それではindex.jsとindex.htmlがおいてあるディレクトリ(ここではC:\study\firebase)で

serve src

とやります。

http://localhost:5000/

へアクセスします。

ちゃんとブラウザのコンソールにAnalyticsのオブジェクトが表示されましたね。


プログラミングにおける循環参照 なぜいけないのか

循環参照という言葉はExcelで見たことがある人もいるかと思いますが、プログラミングの現場においても、起こることはあります。

普通に循環参照って検索すると、大体Excelの話が出てきますが、循環参照自体は下記のサイトの説明がわかりやすいと思います。

https://gimo.jp/glossary/details/circular_definition.html

今回は、弊社のプログラマーさんと話していて、このような構成になっていたところ

「なんでいけないんですか?」

と言われてパッと答えられなかったので書いておきます。

例えば、ここに構成が複雑なオブジェクト、Itemクラスがあります。

Itemオブジェクトはあまりに複雑なので、ユーザーもItemからコピーして作りたいという要望があったとします。

なので、ItemMasterというクラスが爆誕します。で、ItemMasterはItemを参照して、そのItemのコピーをバンバン作っていきます。

ちなみに、できたItemはユーザーが個別にプロパティをちょこちょこ編集できる仕様です。

クラスとプロパティを抜粋して書くと、次のようになります。

<?php


class ItemMaster{

    public $id;
    public $item_id;

}


class Item{

    public $id;
    public $item_master_id;


}

文章で書くと、

「10番のアイテムができがよいので、コピペして作りたい。そこで、10番をマスターとして、アイテムマスター(Id:1)を作る。アイテムマスターから、11番、12番、13番のアイテムを作る。」

ということです。

ここまでは問題がなさそうです。

実際の問題①

アイテムを更新することを考えてみましょう。

「10番のアイテムのプロパティにちょっと変更を加えた、アイテムマスター (Id:1) から作られた アイテムたちに都度変更を加えるのは大変だから、マスターが更新されたらマスターから作られたアイテムはマスターの変更を反映して変更する。つまり11番、12番、13番のアイテムに10番の変更が反映される。」

そのために、次のようにアップデートメソッドを実装します。

class ItemMaster{

    public $id;
    public $item_id;

    public function updateItem(){
        //このマスターから作られたItemを更新する
    }
    //以下続く
}


class Item{

    public $id;
    public $item_master_id;

    public function updateItemMaster(){
        //このアイテムから作られたItemMasterを更新する
    }
    //以下続く

}

ここで、もしItemのプロパティ $item_master_id と ItemMasterのプロパティ $item_id がお互い同じものを見ることになったとします。

つまり

Item $id=10, $item_master_id=1

ItemMaster $id=1, $item_id=10

文章にすると

「10番のアイテムは、アイテムマスター (Id:1) から作られた。」

「アイテムマスター (Id:1) は10番のアイテムから作られた。」

が両立します。すでに頭の中がぐちゃぐちゃになっちゃいますよね。とりあえず、次のような状態です。

「10番のアイテムができがよいので、コピペして作る。そこで、10番をマスターとして、アイテムマスター(Id:1)を作る。アイテムマスターから、11番、12番、13番のアイテムを作る。後日、誤操作で10番もアイテムマスター (Id:1)から作られたことになった。」

実際の問題①としては、では10番のアイテムを更新したとき、10番をもとに作られたアイテムマスター(Id:1)が更新され、そのアイテムマスターから作られたアイテム10番、11番、12番、13番のアイテムが更新されることになります

10番はそこで更新されているので、またアイテムマスター (Id:1) を更新します。 そのアイテムマスターから作られたアイテム10番、11番、12番、13番のアイテムが更新されます。10番が更新されたので…以下無限に続く。

という無限ループになってしまいます。

実際の問題②

実際の問題②として、ではアイテムマスターから生まれたアイテムをまたアイテムマスターにした時はどうでしょうか?実際の問題①を忘れて頂いて、初期状態になったとします。

「13番のアイテムもできがいいので、アイテムマスターにしよう。」

となった場合

「13番のアイテムをもとに、アイテムマスター(Id:2)を作る。アイテムマスター (Id:2) から、14番、15番のアイテムを作る。13番のアイテムは、アイテムマスター (Id:1)から作られており、 アイテムマスター (Id:1) はアイテム10番から作られている。」

こうなった場合、アイテム10番に更新をした場合、

「 アイテムマスター(Id:1)が更新される。 アイテムマスター(Id:1) から作られた11番、12番、13番が10番と同じ内容に更新される。13番が更新されたので、 アイテムマスター (Id:2) を更新する。 アイテムマスター (Id:2) から、14番、15番のアイテムを更新する。内容は、14番、15番は10番と同じ内容のアイテムになる。 」

ということになります。


この話は、絶対に循環参照がダメという話ではないです。やむを得ずこういう設計になってしまうこともあるでしょう(´ω`)。その場合、こういうリスクがありますよ。ということなので、上記の例でいえば

・絶対にアイテムマスターとアイテムは同じオブジェクトを参照しない

・アイテムマスターから作られたアイテムを、アイテムマスターにしない

などを気を付ければうまく使えるかもしれません。

複雑なオブジェクト イメージ
この世のものは全部複雑ですよねー。


存在チェックのSQL処理速度比較メモ

存在チェック(該当データがあるのか)の処理について忘れがちなのでメモします。
SQLの内容それぞれに対して、ローカル環境ですが、クエリの実行時間を調べてみました。
結論から言うと、SELECT EXISTS( SELECT * FROM ○○ WHERE ××)のような書き方が速いですね。


SELECT
                *
            FROM
                driver_status    
	        WHERE
                driver_id = 1678
            AND
                created
            BETWEEN 
                "2021-07-18 12:27:25" AND "2021-07-18 15:27:25"

まず、WHERE句を含んだSELECT文です。カラムは全てのカラムが対象です。
クエリの実行時間: 0.0054 秒

  SELECT
                id
            FROM
                driver_status    
	        WHERE
                driver_id = 1678
            AND
                created
            BETWEEN 
                "2021-07-18 12:27:25" AND "2021-07-18 15:27:25"

次に、 上と同じくWHERE句を含んだSELECT文ですが、こちらはカラムがidのみ対象です。
クエリの実行時間: 0.0044 秒

    SELECT
                *
            FROM
                driver_status    
	        WHERE
                driver_id = 1678
            AND
                created
            BETWEEN 
                "2021-07-18 12:27:25" AND "2021-07-18 15:27:25"
	    LIMIT 1

さっきのカラム全選択にLIMIT 1を付けたもの。
クエリの実行時間: 0.0009 秒

  SELECT
                id
            FROM
                driver_status    
	        WHERE
                driver_id = 1678
            AND
                created
            BETWEEN 
                "2021-07-18 12:27:25" AND "2021-07-18 15:27:25"
	    LIMIT 1

カラムidのみでLIMIT 1。
クエリの実行時間: 0.0007 秒

SELECT EXISTS (SELECT
                id
            FROM
                driver_status    
	        WHERE
                driver_id = 1678
            AND
                created
            BETWEEN 
                "2021-07-18 12:27:25" AND "2021-07-18 15:27:25")

SELECT EXISTS( SELECT ~ ) でカラムにidを指定。
クエリの実行時間: 0.0009 秒

SELECT EXISTS (SELECT
                *
            FROM
                driver_status    
	        WHERE
                driver_id = 1678
            AND
                created
            BETWEEN 
                "2021-07-18 12:27:25" AND "2021-07-18 15:27:25")

SELECT EXISTS( SELECT ~ ) でカラムは全て。カラムは選択しないで全て取ってきた方がSELECT EXISTS( SELECT~)だと速いらしいです。
クエリの実行時間: 0.0003 秒

リモートDBを使って開発環境を整える


1.動機

①セットアップ
最近、開発環境をセットアップすることが増えました。
Docker環境が整備されたことで、かなり楽にセットアップを行うことができました。しかし、DBの変更まで、完璧に同期することはかなり難しいです。

②PR確認
DB変更を伴う機能追加や修正の場合、レビュアーはローカルのDBに変更を加える必要があります。しかし、レビューが終わったら元に戻す必要があったりと、かなり時間と手間がかかってしまいます。


2.検討

どんなサービスを使うか?

【要件】
・MySQL5.7
・~20GBの容量

結果として、LightsailのDBインスタンスを利用する形にしました。

エントリーNo.1:RDS for MySQL
 
 Good
 ・本番環境に近い形で運用が可能
  ・最小構成で20GBから始められる
  ・社内ノウハウもある
 Bad
  ・高い!(使わない時間は停止すればコストダウン可能)

エントリーNo.2:Lightsail

 Good
  ・安い!
  ・リカバリーが簡単にできる
 Bad
  ・可用性が微妙
  ・最小構成が40GB~


3.導入

LightsailのDBインスタンスでは、下記のようにパブリックネットワークにすると危ないかもよ?と脅されます。


同じリージョンにあるインスタンスであれば、アクセスできます。
なので、踏み台用のインスタンスを用意します。

「俺は、ローカルからアクセスしたいんだよ!!!話が違うじゃないか!」

そんなあなたに、SSHポートフォワーディングを!!!

・SSHポートフォワーディングとは?
 ローカルから踏み台サーバーのネットワークにアクセスし、DBにアクセス可  能にできます。こんな感じです↓

・Point
 リモートDBに繋ぐ際は、ローカルのDockerから一度出て、SSH Clientのポートフォワーディングで踏み台サーバー経由でリモートDBに繋ぎます。


4.まとめ

少々面倒な方法になってしまいましたが、リモートDBがあることで、開発のスピードが格段に上がったよに感じます。
今後も、ロジック周りの環境が変化することはあると思うので、データと隔離するメリットはあると思います。