AngularでWebpackのWebAPIのモックサービスを使う

SPA開発（例えばVuejsなど）ではWebAPIのレスポンスを[WebpackのDevServer](https://webpack.js.org/configuration/dev-server/)でモックサービスとして稼働させることで開発効率を上げることは一般的な方法だと思っていたのだが、Angularだと（In-memory Web APIが主流なのか？）付属のwebpackだけではこれが実現できなかったので、備忘録として記事にまとめることにした。

## 想定環境
```
$ ng --version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 12.1.2
Node: 14.15.4
Package Manager: npm 7.20.0
OS: darwin x64

Angular: 
... 

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1201.2 (cli-only)
@angular-devkit/core         12.1.2 (cli-only)
@angular-devkit/schematics   12.1.2 (cli-only)
@schematics/angular          12.1.2 (cli-only)
```

## プロジェクトを作成
`ng`で作成する。特に設定変更せずデフォルトのまま。

```
$ ng new sample
// デフォルトのままエンター連打
$ tree ./sample
./sample
├── README.md
├── angular.json
├── karma.conf.js
├── node_modules
     // 省略
├── package-lock.json
├── package.json
├── src
│   ├── app
│   ├── assets
│   ├── environments
│   ├── favicon.ico
│   ├── index.html
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.css
│   └── test.ts
├── tree.txt
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
```

## `custom-webpack`を追加
Angularにもともと付属しているWebpackでは実現できないので、[custom-webpack](https://github.com/just-jeb/angular-builders/tree/master/packages/custom-webpack)を追加で導入し、`angular.json`を設定する。
ついでに、DevServerの設定(`extra-webpack.config.ts`)もTypeScriptで記述できるように`@types/webpack-dev-server`も追加する。

```
$ npm i -D @angular-builders/custom-webpack
$ npm i -D @types/webpack-dev-server
```

```diff:angular.json
       "prefix": "app",
       "architect": {
         "build": {
-          "builder": "@angular-devkit/build-angular:browser",
+          "builder": "@angular-builders/custom-webpack:browser",
           "options": {
+            "customWebpackConfig": {
+              "path": "./extra-webpack.config.ts"
+            },
             "outputPath": "dist/angular",
             "index": "src/index.html",
             "main": "src/main.ts",

           }
         },
         "serve": {
-          "builder": "@angular-devkit/build-angular:dev-server",
+          "builder": "@angular-builders/custom-webpack:dev-server",
           "options": {
             "browserTarget": "angular:build"
           },
```


### extra-webpack.config.ts を作成
モックサービスとなるWebAPIのレスポンスを`extra-webpack.config.ts`に記述する。

```
$ tree ./sample
./sample
├── README.md
├── angular.json
├── extra-webpack.config.ts     ## ここに新規作成
├── karma.conf.js
├── node_modules
├── package-lock.json
├── package.json
├── src
│   ├── app
│   ├── assets
│   ├── environments
│   ├── favicon.ico
│   ├── index.html
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.css
│   └── test.ts
├── tree.txt
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
```

```ts:extra-webpack.config.ts
import { Configuration } from 'webpack';
import 'webpack-dev-server';

export default {
    devServer: {
        before: (app, server) => {
            app.get("/api/sample", (req, resp) => {
                // モック内容を定義
                resp.json({
                    resultCode: 0
                });
            });
        }
    }
} as Configuration;
```

## クライアントコード作成
WebAPU呼び出しのサンプルコードを作成する。

```diff:app.module.ts
+import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
+   HttpClientModule,
  ],
})
export class AppModule { }
```

```diff:app.conponent.ts
import { Component } from '@angular/core';
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'sample';

+  constructor(private http: HttpClient) {
+    http.get('/api/sample').toPromise()
+      .then(resp => {
+        alert(resp);
+      })
+      .catch((err: HttpErrorResponse) => {
+        console.error(err);
+      })
+  }
}
```

## 動作確認
アプリ起動してブラウザで開発者ツールを起動し、通信内容を確認すると`/api/sample`をgetしていることがわかるはず。

```ts
$ ng serve --open
// ブラウザが起動するので開発者ツールを起動してコンソールを確認
```

# まとめ
Angularでwebpackのdevserverをカスタマイズして使う方法をまとめてみたが、今回の内容は意外とググっても情報が少なかったように思う。
Angularの場合、公式ガイドラインにあるように[In-memory Web API](https://angular.jp/tutorial/toh-pt6)を使うことでも目的を達成することができるためだろうか。
あるいはdevserverのproxy機能を利用して、モックではなく本物のWebAPIへリバースプロキシする方法もある。プロキシする場合は`@angular-builders/custom-webpack`を使う必要はなく、Angular付属のwebpackのみで対応できるのでこっちのほうが手軽かもしれ合い。