サーバーサイド不要説 ~ Angular&Firebaseを使ってがっつりサーバーレスなWEBサービスを開発・運用したノウハウ

  • 12
    Like
  • 0
    Comment

:one: はじめに

「Angular」と「Firebase」を使ってサーバーレスなWEBサービスの開発・運用したノウハウを紹介します。
この記事では詳細な技術的なことにはあまり触れず、「どんな技術を使用してWEBサービスを作ろうか」を検討している人向けに記載しています。
みなさんのサービス開発時の検討材料になれば幸いです。

Angularとは

Googleによって開発が進められているJavaScript(TypeScript)フレームワークです。SPA(シングルページアプリケーション)が容易に作成できます。

Firebaseとは

Googleによって開発が進められているWEBサービスやモバイルアプリに必要なサーバー処理を提供するmBaasサービスです。

開発したサービス

https://wehub.fun ちゃっかり紹介。誰でも記事を作成・共有できるサービスです。
技術的にはGoogleアカウントでのユーザー認証や、記事データの保持・共有などの処理をサーバーサイドプログラムを書かずに実現しています。

:two: 要件

以下の要件を満たせるものを目指しました。

No. 要件 実現方法
1 WEB上でアプリ並みの操作性を実現したい Angularでシングルページアプリケーション(SPA)を作成
Angular/Materialで各種コンポーネント部品を利用
2 PC/タブレット/スマホに対応したい angular/flex-layoutを使用してレスポンシブデザインに対応
3 フロントエンド開発に専念して、バックエンド開発をしたくない バックエンド開発の代わりにFirebaseを使ったmBaasサービスを利用
4 WEBサービスとモバイルアプリを1つのソースコードで管理したい PhoneGapやCordovaを使ってWEBアプリをAndroid/iOS向けにアプリを作成

:three: 環境

開発環境

開発に使用した各種ツールや環境です。メジャーで安定した開発が続けられるものを基準に採用しています。

No. 項目 Ver. 備考
1 Visual Studio Code 1.17.2 Microsoft製のテキストエディター
2 Node.js 6.11.3 JavaScript開発環境
3 npm 3.10.10 Node.jsのパッケージ(ライブラリ)管理マネージャー
4 TypeScript 2.5.3 Microsoft製のオープンソースのプログラミング言語
5 angular-cli 1.4 Google製のangularを爆速で開発するためのツール

※ 開発環境のOSはWindowsですが、macOSやLinuxでも同じものが使用できます。

要素技術(ライブラリ)

作成したWEBサービスに利用した要素技術(ライブラリ)の一覧です。こちらもメジャーで安定した開発が続けられるものを基準に採用しています。現時点でほぼ最新のバージョンを使用しています。

No. 項目 Ver. 備考
1 angular 4.4.4 Googleによって開発が進められているJavaScript(TypeScript)フレームワーク
2 angular/material 2.0.0-beta.12 Angular用のMaterialデザインのライブラリ
3 angular/flex-layout 2.0.0-beta.9 Angular用のレイアウトのライブラリ
4 Firebase 4.6.0 Googleによって開発が進められているWEBサービスやモバイルアプリに必要なサーバー処理を提供するmBaasサービスを利用するためのライブラリ
5 angularfire2 5.0.0-rc.3 FirebaseをAngularで簡単に利用するためのライブラリ
6 marked 0.3 Markdown文字列をHTMLに変換するライブラリ
7 moment 2.18 日時処理を便利にしてくれるライブラリ

※ betaやrcバージョンが入っていますので突然の仕様変更や不具合が存在する可能性があります。
※ 現在のWEB環境は正規版となっていても脆弱性の修正や仕様変更などはつきものなため、個人的にはbeta版であろうと開発が活発でオープンなものであれば気にしていません。

運用環境

No. 項目 備考
1 Google Firebase [Hosting] いわゆるWEBサーバー。HTML/CSS/JavaScriptなどの静的ファイルを設置
2 Google Firebase [FireStore] 記事データなど動的に作成されるデータ類を保持する
3 Google Firebase [AUTH] ユーザー認証に関する機能
メールアドレスやGoogleアカウントによる認証
4 Google Firebase [Funcsions] サーバー側で行いたい処理がある場合にJavaScriptの関数をサーバー側に設置可能
例:ユーザーからの支払い処理
5 Google Search Console 検索エンジンへの対応
6 Google Analytics アクセス解析

:four: プチノウハウ

検索エンジン対応1

検索エンジンはキーワードと対応するページを紐づけます。SPAではシングルページという名の通りページが1つとなるため検索エンジンとの相性はよくありません。
そこで、angularのrouter機能を使いURLと表示する内容を連動させることでこの問題を解決します。

app-routing.module.ts|ルーティング設定例
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './pages/home/home.component';
import { FeedbackComponent } from './pages/feedback/feedback.component';
import { WatchComponent } from './pages/watch/watch.component';
import { MySettingsComponent } from './pages/my-settings/my-settings.component';
import { MyArticlesComponent } from './pages/my-articles/my-articles.component';
import { PostComponent } from './pages/post/post.component';

const routes: Routes = [
    {
        path: '',
        component: HomeComponent,
    },
    {
        path: 'feedback',
        component: FeedbackComponent,
    },
    {
        path: 'watch/:id',
        component: WatchComponent,
    },
    {
        path: 'my/settings',
        component: MySettingsComponent,
    },
    {
        path: 'my/articles',
        component: MyArticlesComponent,
    },
    {
        path: 'my/post',
        component: PostComponent,
    },
    {
        path: 'my/edit/:id',
        component: PostComponent,
    },
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }

検索エンジン対応2

昔のクローラー(検索エンジン)はSPAなどのJavaScriptなどで動的にDOMが書き換わった文章を読めませんでしたが、少なくとも現在のGoogleのクローラーはある程度は動的にDOMが書き換わっても対応できるようです。

しかし、現在のクローラーでも完璧ではないようで最新のJavaScript規格に対応していないようです。そのためshimを入れて古いJavaScript規格でも対応できるようにしています。

index.htmlの一部
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.4.1/shim.min.js"></script>

検索エンジン対応3 - (未対応)

Angular-UniversalというSSR(サーバーサイドレンダリング)技術を使うと、サーバー上でDOMを生成してクライアントに完成したDOMを返す技術があります。
クライアント(クローラー)側で動的にDOMが書き換わらないため、検索エンジンにとっては最も好ましいといえます。

ただ、私の環境ではrxjsのObservable.fromPromiseでエラーが発生してしまう問題を解決できなかったため現在は未対応の状態です。

※ 「検索エンジン対応1,2」のみで、Googleのクローラーからはインデックスされていますので対応は十分と言えそうです。

Google Analytics 対応

コンテンツが動的に読み込まれてアドレスバーの URL が更新された時点で、トラッカーに保存されているデータも更新する必要があります。
https://developers.google.com/analytics/devguides/collection/analyticsjs/single-page-applications?hl=ja
https://qiita.com/Akira-Isegawa/items/845aee44d79197e9844f

TypeScript用の定義ファイルを追加

npm install --save-dev @types/google.analytics
index.html
    <!-- Google Analytics -->
    <script src="https://www.googletagmanager.com/gtag/js?id=UA-*********-1"></script>
</head>
app-routing.module.ts
import { } from 'google.analytics';

// Google Analytics の初期化コード
window[`dataLayer`] = window[`dataLayer`] || [];
function gtag(arg1, arg2) { window[`dataLayer`].push(arguments); }
gtag('js', new Date());
gtag('config', 'UA-*********-1');
app-routing.module.ts
export class AppRoutingModule {
    /**
     * DIコンストラクタ
     */
    public constructor(router: Router) {

        // ルーターイベントを取得
        router.events
            // NavigationEndイベントだけにする
            .filter(e => e instanceof NavigationEnd)

            // NavigationEnd型に変換
            .map(e => e as NavigationEnd)

            // イベント購読
            .subscribe(e => {
                // トラッカーを更新
                ga('set', 'page', e.url);

                // PV送信
                ga('send', 'pageview', e.url);
            });
    }
}

:five: 終わりに

記事自体のクオリティがかなり荒く申し訳ないですが何方かの参考になれば幸いです。
まずは何かアプトプットするということが大事かと思いましたのでご容赦を^^