Angular6 で俺式 KOAN スタック Webアプリを構築する、ほぼ全手順（2）

## 概要
Angular6で「俺式 KOAN スタック」で Web アプリ構築するための個人的な備忘録記事。
概要については「[こちら](https://qiita.com/tomy0610/items/9e948154f9b8f56f28cf)」を参照。
事前に「[ほぼ全手順（1）](https://qiita.com/tomy0610/items/a1d28655da9f07068f8c)」が完了していることが前提。

今回の内容は「Web アプリの daemon（デーモン）化」「環境変数を操作し、アプリ実行モードによって色んな設定値を呼び分けられるようにする」

## Web アプリの daemon（デーモン）化

現状のままだと、アプリ起動コマンド実行後は、以下のような状態になる。

```bash
...
> demo-app@0.0.0 serve:node /path/to/webapp/demo-app
> node dist/web-server.js

======================================================================
MEAN stack Template App. アプリ名をここに表示
======================================================================
=========================================================== ver.0.0.0
App is listening on http://localhost:3000
```

この状態だと、「コマンド実行後、裏でアプリを動かしっぱなしにする」ということが出来ない。
なので、アプリを daemon（デーモン）化して、バックグラウンドでも起動したアプリが止まらないようにする。

### 1．daemon 化モジュール "forever" をインストール

```bash
npm install forever
```

### 2．アプリ起動コマンドを追加

forever 実行するスクリプトを、以下のとおり追記。
（呼出コマンド、アプリ起動コマンド、起動済み一覧表示コマンド、停止コマンド）

```json-doc:package.json
{
  "name": "demo-app",
  "version": "0.0.0",
  "scripts": {
    "start": "npm run clean && npm run build && npm run serve:node",
    "forever": "npm run clean && npm run build && npm run serve:forevr", // 追加
    "list": "forever list", // 追加
    "clean": "rm -rf dist",
    "build": "npm run build:client && npm run build:server",
    "build:client": "ng build",
    "build:server": "tsc --project server --outDir dist --allowjs true",
    "serve:node": "node dist/web-server.js",
    "serve:forevr": "forever start dist/web-server.js", // 追加
    "stop": "forever stop dist/web-server.js", // 追加
    ...
  },
  ...
```

### 3．確認

#### 起動確認

試しに実行してみる。

```bash
npm run forever
```

以下のようになれば、daemon 化成功。

```bash
...
> demo-app@0.0.0 serve:forevr /path/to/webapp/demo-app
> forever start dist/web-server.js

warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: dist/web-server.js # アプリ起動完了

my-mac:demo-app-dir user$ # コマンド完了後、返ってくる
```

これで、アプリ起動しても他のコマンド打てるし、ターミナル落としても起動アプリが勝手に止まったりしなくなる。

#### 起動中のアプリを確認

```bash
npm run list
```

実行すると、以下のように、実行中のスクリプトやコマンド履歴、IDなどが出る。

```bash
> demo-app@0.0.0 list /path/to/webapp/demo-app
> forever list

info:    Forever processes running
data:        uid  command             script             forever pid   id logfile                        uptime
# 起動中のアプリ ▼
data:    [0] BQIE /usr/local/bin/node dist/web-server.js 13168   13169    /Users/tommy/.forever/BQIE.log 0:0:9:43.442 
```

#### 停止確認

以下のコマンドで daemon 化されたアプリを停止できる。

```bash
npm run stop
```

もう一度 `npm run list` で確認すると、以下のように起動中一覧からアプリが消える。

```bash
> demo-app@0.0.0 list /path/to/webapp/demo-app
> forever list

info:    No forever processes running # 起動中のアプリが存在しない
```

## 環境変数を操作し、設定を呼び分けられるようにする

### 1．node-config を導入する

起動時に NODE_ENV (環境変数) を指定するだけで config ファイルを自動で呼び分けてくれる便利なモジュール、node-config をインストール。と、同時に、型定義ファイルもインストール。

```bash
npm install config @types/config
```

### 2．環境変数毎の設定ファイルを作成

node-config の仕様どおりに、ルートディレクトリ直下に config というディレクトリを用意して、環境変数に対応した設定ファイルを作る。
設定ファイルの形式は json などに対応しているが、今回はコメントも記載できて書きやすい yaml (ヤムル) ファイルで書く。

##### 今回作成する環境値

- ローカル環境（daemon 化せずに node アプリ起動する環境）
- 開発環境 (daemon 化してアプリ起動するが、開発用)
- 本番環境 (daemon 化してアプリ起動し、本番で動かす用)

#### config ファイル作成：ローカル環境

```yaml:config/default.yaml
---
  # ローカルマシン環境用 設定値

  mode: 'local'
  description: 'local モードでアプリ起動'
  port: 3000

```

#### config ファイル作成：開発環境

```yaml:config/development.yaml
---
  # 開発環境用 設定値

  mode: 'development'
  description: 'development モードでアプリ起動'
  port: 3000

```

#### config ファイル作成：本番環境

```yaml:config/production.yaml
---
  # 開発環境用 設定値

  mode: 'production'
  description: 'production モードでアプリ起動'
  port: 3000

```

### 3．web-server.ts を修正

node-config を使用して、web-server.ts を以下のとおり修正してみる。

- アプリ起動時に、設定ファイルからアプリのポート番号を設定する
- API 呼び出し時、環境変数を動的に返却するように設定してみる

```ts:server/web-server.ts
...

import * as Koa from 'koa';
import * as koaStatic from 'koa-static';
import * as KoaRouter from 'koa-router';
import * as bodyParser from 'koa-bodyparser';
import * as config from 'config'; // 追加
import { join } from 'path';

const app = new Koa();
const PORT = process.env.PORT || config.get('port'); // 修正：3000 -> config のポート値を使用
const clientSrcPath = join(process.cwd(), 'dist/client');

...

const koaRouter = new KoaRouter();
koaRouter.get('/api/*', (ctx) => {
  let response = 'API が呼ばれました'; // 追加
  response += `process.env.NODE_ENV: ${process.env.NODE_ENV}`; // 追加
  response += `設定ファイル: ${config}`; // 追加

  ctx.status = 200;
  ctx.body = response; // 修正
});

...
```

### 4．アプリ起動コマンドを修正

実行環境別にスクリプトを用意する。以下のとおりに追記・修正。

```json-doc:package.json
{
  "name": "demo-app",
  "version": "0.0.0",
  "scripts": {
    "local": "npm run clean && npm run build && npm run serve:local", // 修正： start -> local ローカル環境用
    "start:dev": "npm run clean && npm run build && npm run serve:dev", // 修正： forever -> start:dev 開発環境用
    "start:prod": "npm run clean && npm run build && npm run serve:prod", // 追加： 本番環境用
    "list": "forever list",
    "clean": "rm -rf dist",
    "build": "npm run build:client && npm run build:server",
    "build:client": "ng build",
    "build:server": "tsc --project server --outDir dist --allowjs true",
    "serve:local": "NODE_ENV=production node dist/web-server.js", // 修正： start -> local ローカル環境用
    "serve:dev": "NODE_ENV=production forever start --uid demo-app-dev dist/web-server.js", // 修正： start -> local 開発環境用
    "serve:prod": "NODE_ENV=production forever start --uid demo-app dist/web-server.js", // 追加： 本番環境用
    "stop:dev": "forever stop demo-app-dev", // 修正： UID を指定して 開発環境 アプリ停止
    "stop:prod": "forever stop demo-app", // 追加： UID を指定して 本番環境 アプリ停止
    ...
  },
  ...
```

「NODE_ENV=xxx」を指定しながらアプリ起動することで、対応する config ファイルが自動で呼び出され、同時に 環境変数 `process.env.NODE_ENV` に環境値が格納される。
また、 forever start にオプション `--uid` を指定することで、起動済みアプリ一覧表示時に何を起動したかわかるようになる。アプリ停止も UID で行える。

### 5．確認

#### ローカル環境モードで起動してみる

##### アプリ起動

```bash
npm run local
```

##### ブラウザから API アクセス

Web ブラウザから `http://localhost:3000/api/aaa` にアクセスすると以下のように出る

<img width="890" alt="API確認_local.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/25ca1df2-4412-0fe3-b9b2-347604f56000.png">

##### アプリ停止

`Ctrl`＋`C` で停止

#### 開発環境モードで起動してみる

##### アプリ起動

```bash
npm run start:dev
```

##### 起動アプリ一覧表示

```bash:起動中アプリ確認
> demo-app@0.0.0 list /path/to/webapp/demo-app
> forever list

info:    Forever processes running
data:        uid          command             script             forever pid   id logfile                                uptime
# 開発環境用アプリ ▼
data:    [0] demo-app-dev /usr/local/bin/node dist/web-server.js 14536   14537    /Users/tommy/.forever/demo-app-dev.log 0:0:0:46.339
```

UID の部分に `demo-app-dev` と表示される

##### ブラウザから API アクセス

Web ブラウザから `http://localhost:3000/api/aaa` にアクセスすると以下のように出る

<img width="890" alt="API確認_dev.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/0ada02b6-9aa9-451b-df0b-80ed661da9ee.png">

##### アプリ停止

```bash
npm run stop:dev
```
***
#### 本番環境モードで起動してみる

##### アプリ起動

```bash
npm run start:prod
```

##### 起動アプリ一覧表示

```bash:起動中アプリ確認
> demo-app@0.0.0 list /path/to/webapp/demo-app
> forever list

info:    Forever processes running
data:        uid          command             script             forever pid   id logfile                                uptime
# 本番環境用アプリ ▼
data:    [0] demo-app /usr/local/bin/node dist/web-server.js 14571   14572    /Users/tommy/.forever/demo-app.log 0:0:0:4.12
```

UID の部分に `demo-app` と表示される

##### ブラウザから API アクセス

Web ブラウザから `http://localhost:3000/api/aaa` にアクセスすると以下のように出る

<img width="890" alt="API確認_prod.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/a1aae7e2-a23e-9dcd-17c8-2c34af1c5f0a.png">

##### アプリ停止

```bash
npm run prod
```

#### 結果

同じソースコードでも、起動コマンドを変えることで、環境変数や設定値を、動的に変えられることが確認できた。
これによって、例えば、外部 API のアクセス先やログの出力先などを設定ファイル別に書くことで、いちいち NODE_ENV 変数を呼び出して if 文などで処理を分岐させるようなソースコードを書かなくてすむ。

## この後やること

画面のボタンから複数の API を呼べるように通信部を作り込む。

→ 「[Angular6 で俺式 KOAN スタック Webアプリを構築する、ほぼ全手順（3）](https://qiita.com/tomy0610/items/15241a615d107ec99613)」に執筆