Angular SPAからUniversalにしてみたら、パフォーマンススコアが13から51に！！

# 前提
Webアプリケーションを構築する場合、サービスの性質や要件を踏まえてSEO対策やモバイルデバイスへのパフォーマンス改善、ページ表示速度が重要であるかどうかをまずご検討ください。（これマジ。笑）
上記いずれかが重要な場合は最初からサーバーサイドレンダリング (SSR)で構築することをお勧めします。

# 概要
Angularで構築しているWebアプリケーションを元々はSPAで構築していましたが、サービス的にはSEOやパフォーマンス改善が重要になるため、[Angular Universal](https://angular.jp/guide/universal)を使ったサーバーサイドレンダリング (SSR)に対応しました。
その過程でいくつかつまづきポイントがあったので、注意点を含めてご紹介します。

ここはこうした方がいいよ、というアドバイスがあればコメントにて教えていただけると嬉しいです。

# 環境
- Node v16.10.0
- Angular CLI: 13.3.0
- Package Manager: npm 7.24.0
- OS: linux x64

# 最低限必要な手順

## サーバー側のモジュールインストール

```
ng add @nguniversal/express-engine
```

```sh
src/
  index.html                 アプリの Web ページ
  main.ts                    クライアントアプリのブートストラップ
  main.server.ts             * サーバーアプリのブートストラップ
  style.css                  アプリのスタイル
  app/ ...                   アプリケーションコード
    app.server.module.ts     * サーバーサイドアプリケーションモジュール
server.ts                    * Express Web サーバー
tsconfig.json                TypeScript 基本構成
tsconfig.app.json            TypeScript クライアント構成
tsconfig.server.json         * TypeScript サーバー構成
tsconfig.spec.json           TypeScript 仕様の構成
```

## ローカル環境での実行

```sh
npm run dev:ssr
```

http://localhost:4200 でアプリケーションにアクセスできます。


## 本番環境での実行
ビルドして、SSR用のサーバーを起動するイメージですね。

```sh
npm run build:ssr
npm run serve:ssr
```

http://localhost:4000 でアプリケーションにアクセスできます。

# 注意点
私が直面した経験ベースですが、ローカル環境とサーバー環境ごとの注意点をご紹介します。

## ローカル環境

### Browser Syncエラー

ローカル環境でDockerなどを利用してポートマッピングしている場合は注意が必要です。
`npm run dev:ssr`を実行すると、
`http://localhost:4200/browser-sync/socket.io/?EIO=4&transport=polling&t=N-w12hp`という形でpollingしているようです。
ポートが解放されていないと以下のようにエラーが出力されます。

![スクリーンショット 2022-03-24 12.43.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/ab496b40-822d-d561-b23f-ce54d925bfd2.png)

#### Docker環境
参考までに私のDocker環境をご紹介します。

##### 構成
```sh
.
┝ environment
│ └ development
│   ┝ docker-compose.yml
│   └ docker
│     └ node
│       └ Dockerfile
└ front # Angularプロジェクト
```

##### docker-compose.yml
`ports`の`'4200:4200'`がポイントです。

```docker-compose.yml
version: '3.9'

services:
  node:
    build: docker/node
    ports:
      - '80:4200'
      - '4200:4200'
    volumes:
      - './../../front:/projects:cached'
    tty: true
    command: bash -c "
      npm install;
      cp -f src/index.develop.html src/index.html;
      cp -f src/environments/environment.develop.ts src/environments/environment.ts;
      npm run dev:ssr;
      "
```

##### Dockerfile

```Dockerfile
FROM node:16.10.0

WORKDIR /projects

RUN npm cache clean -f
RUN npm install -g @angular/cli@13.3.0
```

### LocalStorageが使えない
`localStorage is not defined`となりますので、Cookieの利用を検討ください。
私は、localStorageもCookieも利用しない方向でとりあえず修正しました。

## サーバー環境(本番環境)

### Node Express server listening port変更
`npm run serve:ssr`を実行するとポート4000でアクセスを受け付けるようになります。
しかし、私のサーバー環境では、80番ポートでリクエストを受け取るようにしていたので設定する必要があります。

どうやら、server.tsで設定されているようです。

```ts:server.ts
function run(): void {
  const port = process.env['PORT'] || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}
```

取り急ぎの変更であれば、上記の`4000`を`80`に変更すれば良いのですが、`process.env['PORT']`で切り替わるようにしてみましょう。
設定方法はいくつかありますが、Dockerを使っている場合はDockerfileでも指定できます。

```Dockerfile
FROM node:16.10.0

ENV PORT=80
```

環境に合った方法で設定してみてください。

### Angular material
一番ハマったのですが、Angular materialはUniversalではそのままでは使えないようです。
ローカルではエラーが出ず、サーバー環境でのみエラーが出たので原因を調べるのがとても大変でした。笑

Universal用のパッケージ([universal-material](https://www.npmjs.com/package/@universal-material/angular))をインストールする必要があるようです。

#### 1. Universal Materialインストール

```sh
npm i @universal-material/angular
```

#### 2. Universal Material scssインポート

```scss:src/styles.scss
@import "node_modules/@universal-material/core/scss/universal-material.scss";
```

# SSR対応の効果
Chromeの拡張ツールである[Lighthouse](https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=ja)で検証します。

## Universal対応前
画像を残していなかったのですが、Performanceのスコアは13でした。

## Universal対応後
Performanceのスコアは51に！！


![スクリーンショット 2022-03-24 13.08.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/aeefcc33-05e8-4762-cd5f-cf2ddaab4eb2.png)

`※ Universal以外の対応は特にしていません。`