Webアプリケーション開発におけるフロントエンド、バックエンド分離環境


# 変更履歴
* 2022/05/06 : [本番環境へのデプロイ](#本番環境へのデプロイ)で、yarn generteでの警告通り、target: 'static'に設定するように加筆しました。併せて、yarn generateの標準出力も変更しています。

# 目的
* PoC業務で、数年ぶりにWebアプリケーション開発を行うことになって、開発環境、特にフロントエンド開発環境の進展、変貌に驚いた。
* 動的コンテンツはJSP, Ajaxで止まっており、テンプレートにjavascript, JSPタグを必至に埋め込んでいた自分にとって、これは纏めないと速攻忘れそうだ。

# 開発環境の構成

* フロントエンドとバックエンドはREST APIで分離されている。
* フロントエンドはローカル環境（Windows10, Mac)で開発可能
* フロントエンド開発においてバックエンドはスタブで代用可能


![test_frontend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282300/eb21dd89-1203-4634-39f5-79548d8f3872.png)



# この開発環境の利点
* フロントエンド、バックエンドの開発環境、開発体制を分離できる。(REST APIのみ取り決め)
    * フロントエンド開発はjavascriptダミーデータによりREST API取り決め以前でも開始可能。
* 開発環境での充分なテスト後に、フロントエンド実行環境をサーバにデプロイすることができる。

# フロントエンド開発環境
* 前提
    * フロントエンド開発環境 (Windows10)

## javascript実行環境
* [node.js](https://nodejs.org/ja/)
    * javascript実行環境

    ```
    PS C:\work\git\post19\acp_portal> node -v
    v14.15.1
    ```

* [yarn](https://classic.yarnpkg.com/en/)
    * npm互換のパッケージマネージャ。npmでもいいが、nuxtの自動セットアップと依存バージョンの固定(yarn.lock)を期待。

    ```
    PS C:\work\git\post19\acp_portal\sample_cloud> yarn -v
    1.22.10
    ```
## javascriptフレームワーク、コンポーネントライブラリ
* [vue](https://jp.vuejs.org/index.html)
    * ユーザーインターフェイスを構築するためのjavascriptフレームワーク
    * yarn create nuxt-app で自動的にインストールされる。

* [nuxt](https://ja.nuxtjs.org/)
    * vueフレームワーク。vueファイルの配置がそのままルーティングになるためかなり楽。
    * yarn create nuxt-app で自動的にインストールされる。

* [vuetify](https://vuetifyjs.com/ja/)
    * VueのUIコンポーネントライブラリ。多くのコンポーネント、レイアウトツールが含まれていて、vuetifyコンポーネントを組み合わせていくだけで多くの画面を構成できる。これを利用するためにVueを使うといっても過言ではない。
    * yarn create nuxt-app で自動的にインストールされる。

* フロントエンドアプリケーション作成例
    * yarn create nuxt-appによりnuxtフレームワークを利用したアプリケーション(sample-app)を作成する。
    * 選択の要点
        * Programming language: JavaScript/TypeScriptのどちらかの選択。上位互換のTypeScriptを選択。
        * Package manager: Yarn
        * UI framework: Vuetity.jsを選択すると自動的にインストールされる。
        * Nuxt.js modules: AxiosモジュールによりREST APIアクセスを行う。
        * Rendering mode: Singe Page Appを選択

        ```
        PS C:\work\git\frontend> yarn create nuxt-app sample-app
        yarn create v1.22.10
        [1/4] Resolving packages...
        [2/4] Fetching packages...
        [3/4] Linking dependencies...
        [4/4] Building fresh packages...

        success Installed "create-nuxt-app@3.6.0" with binaries:
            - create-nuxt-app

        create-nuxt-app v3.6.0
        ✨  Generating Nuxt.js project in sample-app
        ? Project name: sample-app
        ? Programming language: TypeScript 
        ? Package manager: Yarn
        ? UI framework: Vuetify.js
        ? Nuxt.js modules: Axios - Promise based HTTP client
        ? Linting tools: ESLint
        ? Testing framework: None
        ? Rendering mode: Single Page App    
        ? Deployment target: Server (Node.js hosting)
        ? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)
        ? Continuous integration: None
        ? Version control system: Git
        yarn run v1.22.10
        $ eslint --ext ".js,.vue" --ignore-path .gitignore . --fix
        Done in 21.63s.

        To get started:

                cd sample-app
                yarn dev

        To build & start for production:

                cd sample-app
                yarn build
                yarn start


        For TypeScript users.

        See : https://typescript.nuxtjs.org/cookbook/components/
        Done in 258.47s.
        ```

* yarn.lockにアプリケーション依存ライブラリのバージョンが固定される。

    ```
    Mode                 LastWriteTime         Length Name
    ----                 -------------         ------ ----
    -a----        2021/05/10     15:24         402210 yarn.lock
        ・・・
        vue "^2.6.12"
        ・・・
        vuetify "^2.4.2"
        ・・・
        nuxt@^2.15.3:
            version "2.15.5"
    ```


* フロントエンドアプリケーションの起動
    * yarm devで起動

        ```
        PS C:\work\git\frontend\sample-app> yarn dev
        yarn run v1.22.10
        $ nuxt

        ╭───────────────────────────────────────╮
        │                                       │
        │   Nuxt @ v2.15.5                      │
        │                                       │
        │   ▸ Environment: development          │
        │   ▸ Rendering:   client-side          │
        │   ▸ Target:      server               │
        │                                       │
        │   Listening: http://localhost:3000/   │
        │                                       │
        ╰───────────────────────────────────────╯

        i Preparing project for development                                                                                                                   15:45:47
        i Initial build may take a while                                                                                                                      15:45:47
        i Discovered Components: .nuxt/components/readme.md                                                                                                   15:45:47
        √ Builder initialized                                                                                                                                 15:45:47
        √ Nuxt files generated                                                                                                                                15:45:48

        √ Client
        Compiled successfully in 18.58s

        i Waiting for file changes                                                                                                                            15:46:15
        i Memory usage: 237 MB (RSS: 428 MB)                                                                                                                  15:46:15
        i Listening on: http://localhost:3000/                                                                                                                15:46:15  
        No issues found.                                                                                                                                      15:46:15
        ```

    * ブラウザからlocalhost:3000にアクセス![yarn_dev.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282300/b4de0dd5-c0a5-1f98-e410-2dc05d641155.png)



* フロントエンドアプリケーションの開発例
    * この状態からバックエンド環境から独立してフロントエンドアプリケーション開発が可能です。
    * 試しにトップページ(pages/index.vue)にダッシュボードぽいグラフコンポーネントを配置します。
        1. Vuetifyの[Dashboard card](https://vuetifyjs.com/ja/components/sparklines/#dashboard-card)を開き、ソースを表示。(<>を押下)
        2. TEMPLATEの`<v-card>～</v-card>`をトップページ(pages/index.vue)に貼り付け
        3. SCRIPTの内容をトップページ(pages/index.vue)の`<script>～</script>`にマージ
    * ブラウザからlocalhost:3000にアクセス
        * ダッシュボードぽいグラフが追加されました。![yarn_dev2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282300/ab49f87c-840e-91fb-14f4-dff6f21a4525.png)



* バックエンドとのREST APIによるデータ通信
    * Javascript・AxiosモジュールによってREST APIクライアントを実装します。
    * 例(GET)だけ示します。

        ```
        <script>
        import axios from "axios"

        export default {
            data() {
                let today = new Date();
                let tomorrow = new Date();
                tomorrow.setDate(today.getDate() + 1);
                return {
                    from_ts: today.toISOString().substr(0, 10),
                    to_ts: tomorrow.toISOString().substr(0, 10),
                    devices:[],
                };
            },
            created: function() {
                let sample_api = "https://backend/sample_app/api/deviceinfo/"
                    + "?from_ts=" + this.from_ts
                    + "&to_ts=" + this.to_ts
                axios.get(sample_api).then(
                    function(response) {
                        this.devices = response.data
                    }.bind(this)
                )
            },
        ```

# バックエンド開発環境
* 前提
    * バックエンド開発環境 (ubunutu@AWS)

* バックエンドは基本的な構成はデータベース、Webアプリケーションフレームワークで構成され、フロントエンドとのREST APIを提供する。
    * [django](https://www.djangoproject.com/)
        * djangoのREST API Framework
    * HTTPサーバ (Apache 2.4)
        * djangoとの連携をmod_wsgiで行う。
    * データベース (mariaDB)
    * RESTful環境
        * djangorestframework, django-filter, django-cors-headersをpipによりinstallする。
        * /home/ubuntu/acp_cloud/acp_cloud/acp_cloud/settings.py
        
            ```
            INSTALLED_APPS = [
                ...
                'rest_framework',
                'django_filters',
                'corsheaders',
            ]

            MIDDLEWARE = [
                ...
                'corsheaders.middleware.CorsMiddleware',
            ]

            REST_FRAMEWORK = {
                'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
            }
                ...
            CORS_ORIGIN_ALLOW_ALL = True
            ```

* バックエンド実装については省略します。

## 本番環境へのデプロイ
* サーバ環境にデプロイするためにフロントエンドアプリケーションをバンドルします。
* nuxt generateにより、webpack(nuxtにより導入済）がバンドルJSを作成します。
* すべてのルートを HTML ファイルとして生成し、dist/ ディレクトリに静的にエクスポートします

1. nuxt.config.jsに以下の一行を追加し、一時的にtargetをstaticにします。 開発段階では動的更新を有効にしたいため、targetは既定のServer (Node.js hosting）としています。

  　　```
  　　target: 'static',
  　　```

2. フロントエンド開発環境でnuxt generateを実行し、バンドルJSの生成を行う。

    ```
    PS C:\work\git\frontend\sample-app> yarn generate
    yarn run v1.22.10
    $ nuxt generate
    i Production build                                                                                                             18:42:57
    i Bundling only for client side                                                                                                18:42:57
    i Target: static                                                                                                               18:42:57  
    i Using components loader to optimize imports                                                                                  18:42:57
    i Discovered Components: node_modules/.cache/nuxt/components/readme.md                                                         18:42:57
    √ Builder initialized                                                                                                          18:42:57  
    √ Nuxt files generated                                                                                                         18:42:57

    √ Client
    Compiled successfully in 44.31s


    Hash: aef188a760f9eb955915
    Version: webpack 4.46.0
    Time: 44309ms
    Built at: 2022/05/06 18:43:50
                            Asset       Size   Chunks                                Chunk Names
    ../server/client.manifest.json   18.3 KiB           [emitted]
                        0405a50.js   3.95 KiB        1  [emitted] [immutable]         app
                        0f324fb.js   2.35 KiB        8  [emitted] [immutable]         runtime
                        67e3741.js  666 bytes        7  [emitted] [immutable]         pages/inspire
                        6baf37b.js   22.3 KiB  6, 3, 5  [emitted] [immutable]         pages/index
                        78812e5.js    536 KiB        9  [emitted] [immutable]  [big]  vendors/app
                        8fd98c9.js  948 bytes        5  [emitted] [immutable]         components/vuetify-logo
                        9cae516.js    219 KiB        2  [emitted] [immutable]         commons/app
                        LICENSES  407 bytes           [emitted]
                        d3d990d.js   6.63 KiB        4  [emitted] [immutable]         components/tutorial
                        e540769.js   1.85 KiB        3  [emitted] [immutable]         components/nuxt-logo
                        f1e1d61.js   12.5 KiB        0  [emitted] [immutable]         vendors/pages/index/pages/inspire
    + 1 hidden asset
    Entrypoint app = 0f324fb.js 9cae516.js 78812e5.js 0405a50.js

    WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
    This can impact web performance.
    Assets:
    78812e5.js (536 KiB)
    i Generating output directory: dist/                                                                                           18:43:51  
    i Generating pages                                                                                                             18:43:51  
    √ Generated route "/inspire"                                                                                                   18:43:51  
    √ Generated route "/"                                                                                                          18:43:51  
    √ Client-side fallback created: 200.html                                                                                       18:43:51  
    Done in 67.21s.
    ```


3. 1.で作成されたdistフォルダをWebサーバのドキュメントルート(ex. /var/www/html)に配置する。
4. Webサーバを再起動し、上記ドキュメントルートにアクセスする。(ex. https://backend:443/dist)


* Webサーバにデプロイされた環境は以下のようになる。
    * フロントエンド開発環境でgenerateされたdistフォルダをWebサーバにデプロイする。
    * アクセスURLはWebサーバのデプロイ先となる。
    * フロントエンド/バックエンド間のREST APIはそのままブラウザ/Webサーバ間となるため、CORS設定には注意が必要。
![deployed_frontend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282300/553907cd-abbf-7a50-9d85-0e6728109c3f.png)