ITと筋トレの二刀流

未だゼロ刀流

筋トレログ2019 (1/26〜2/1)

f:id:tatsuyashi:20190217003959p:plain:w300

2019年1月26日(土)〜2月1日(金)の筋トレログを報告します。

場所 レーニング内容
2019年1月26日(土) 自宅 腕立て伏せ:3セット
スタンディングカーフレイズ:6セット
スクワット:3セット
リバースプッシュアップ:3セット
2019年1月27日(日) ジム ベンチプレス:10kg×2セット
スミスマシン(インクライン):0kg×4セット
ダンベルカール:3.0kg×3セット
ラットプルダウン:15kg×4セット
サイドレイズ:2.0kg×2セット
2019年1月28日(月) - 休養
2019年1月29日(火) 自宅 スクワット:3セット
スタンディングカーフレイズ:3セット
2019年1月30日(水) ジム ベンチプレス:10kg×6セット
スミスマシン(インクライン):0kg×3セット
ダンベルカール:3.0kg×3セット
ディップス:3セット
サイドレイズ:3.0kg×3セット、2.0kg×2セット
2019年1月31日(木) - 休養
2019年2月1日(金) - 休養

私の筋トレログでは回数は書いていません。そもそも「回数」に囚われずにトレーニングするというポリシーにしているので、数えてないというのが正直なところです。

ですが、体感ではそこまで少なからず多からずだと思うので、だいたい10〜15回ぐらいなのかなと思います。(そのぐらいで限界を迎えている気がする)

あまりにも回数をこなせるようになったり、負荷を感じなくなったタイミングで重量アップになるという感じですね。今のところ重量はほとんど変わってませんが(泣)

2019年に取り組んでいる筋トレはこちら↓

tatsuyashi.hatenablog.com

ReactNative+ExpoでAndroid端末へプッシュ通知を送る その①〜Expo プッシュトークンの取得〜

f:id:tatsuyashi:20190214235559p:plain:w300

Expoを使用して開発するReactNativeアプリから各端末へプッシュ通知を送る仕組みを入れたいと思います。

私の端末はAndroidなのでまずはAndroid端末へ送れるようにしたのですが、途中試行錯誤したのでやり方を載せたいと思います。

Android向けの説明になりますが、iOSの方も途中までは参考にしていただけます。 iOSだともう少し手間なくできるかも?です。(未検証)

バージョン

[アプリケーション]

・create-react-native-app:2.0.2

・Expo CLI:2.4.0

・React Native SDK:31.0.0

後は今回の記事に関係しないので割愛。

[端末]

Xperia XZ1 (Android 9 Pie)

準備

プッシュ通知の確認は実機ベースが必須なので、GooglePlayよりExpoアプリをインストールしておいてください。

play.google.com

開発手順

プライベートなリポジトリで開発しているためソースは断片的にしか公開できませんので、簡単に開発手順をまとめます。

Expo CLIを使ってプロジェクト作れる方は飛ばしてもらってもOKです。

create-react-native-app インストール

npm install -g create-react-native-app

私と同じバージョンで始める場合は、

npm install -g create-react-native-app@2.0.2

Expo CLI インストール

npm install -g expo-cli

私と同じバージョンで始める場合は、

npm install -g expo-cli@2.4.0

React Nativeプロジェクト作成

create-react-native-app [your-project-name]

これでプロジェクトが作成されます。

アプリ起動

expo start

npm start ではなくexpoコマンドで起動します。

起動すると、localhost:19002でブラウザが自動で起動します。

↓こんな感じ

f:id:tatsuyashi:20190215003026p:plain:w500

ここに表示されるQRコードスマホ端末でExpoアプリを使ってスキャンすると、スマホ上にアプリがロードされます。(ここまでがアプリの初期セットアップ)

Push通知の手順

公式ドキュメントの内容をまとめると、

  1. Expoからプッシュトークンを取得する
  2. プッシュトークンを自分のサーバで保管する(ユーザ情報と紐付けておく)
  3. プッシュ通知を行うアクションが発生した際に、自分のサーバからExpoのプッシュ通知用のAPIをコールする(この際にプッシュトークンを使う)
  4. Expoが各端末へプッシュ通知を送る(OSのハンドリングはExpo側で行う)
  5. フォアグラウンドにあると通知が表示されないので、表示するようにハンドリングする(iOS用?)

読む限り、1でプッシュトークンが取得できれば、後はAPIを呼び出すだけのような気がします。

プッシュトークンの取得

先ほどの公式サイトにあるサンプルコードを真似てみます。

下記のコード(関数)をApp.jsに追加します。念のためtry-catchします。

import { Permissions, Notifications } from 'expo';

〜〜

async function registerForPushNotificationsAsync() {
  try {
    const { status: existingStatus } = await Permissions.getAsync(
      Permissions.NOTIFICATIONS
    );
    let finalStatus = existingStatus;

    // only ask if permissions have not already been determined, because
    // iOS won't necessarily prompt the user a second time.
    if (existingStatus !== 'granted') {
      // Android remote notification permissions are granted during the app
      // install, so this will only ask on iOS
      const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
      finalStatus = status;
    }

    // Stop here if the user did not grant permissions
    if (finalStatus !== 'granted') {
      return;
    }

    // Get the token that uniquely identifies this device
    let token = await Notifications.getExpoPushTokenAsync();

    console.log(token);
  } catch (e) {
    console.error(e);
  }
}

App.jsの初期処理で呼び出すようにします。

export default class App extends React.Component {
  
  componentDidMount() {
    registerForPushNotificationsAsync();
  }
  
  render() {
    ...
  }
}

ここまでは下記のブログを参考にさせていただいたので、これで動くと思っていました。

qiita.com

ですが!!

これでアプリを起動すると、、

Error: Couldn't get GCM token for device

なにやらエラーになりました。

どうもGCM(Google Cloud Messaging)が廃止され、FCM(Firebase Clound Messaging)へ移行しろっていう流れがあるらしいので、それに関連したエラーっぽいです。

Error: Couldn't get GCM token for device の対応

色々調べたのですが、公式ドキュメントに下記の記載があるのを見落としていました。

f:id:tatsuyashi:20190215005944p:plain:w500

要約すると、

「FCMのサーバーキーをExpoに送ってくれないとExpoからプッシュ通知できなくなったんだ。こっちのページにFirebaseプロジェクトの作り方を書いたからそれを見てFCMサーバーキーの取得してExpoに送ってくれ。」

みたいなことを言ってます。

こちらに手順が書いてあるので、その通り進めていくことにします。

Firebaseプロジェクトの作成

Firebaseプロジェクトを作成していない方はFirebaseコンソールからプロジェクトを作成します。

https://console.firebase.google.com/

[+]プロジェクトを追加 を押します。 f:id:tatsuyashi:20190215010834p:plain:w400

プロジェクト名を入力、地域/ロケーションを日本/asia-northeast1に変更してプロジェクトを作成します。

f:id:tatsuyashi:20190215011047p:plain:w400

androidパッケージの決定

※これは公式ドキュメントには書かれていません。

この後の手順でandroidパッケージが必要になります。

これはアプリの開発元やアプリ名に依存しますので、各自で決定お願いします。

com.[your company].[your app name]

みたいな感じでしょうか。

決定しましたら、app.jsonに下記の記載を追加します。

  "android": {
    "package": "[your android package]"
  }

既に "android"がある場合は、その中にpackageを追記します。

  "android": {
    "hoge": "fuga",
→"package": "[your android package]" 
  }

FirebaseへAndroidアプリの追加

Firebaseコンソール画面で[Android アプリに Firebase を追加] をクリックするとアプリの登録画面が開きます。

f:id:tatsuyashi:20190215012625p:plain:w400

ここに先ほど決定したandroidパッケージを入力して、[アプリを登録]します。

google-services.jsonのダウンロードと配置

アプリ登録が終わるとgoogle-services.json をダウンロードできるようになるのでダウンロードします。

f:id:tatsuyashi:20190215012949p:plain:w400

ダウンロードしたgoogle-services.jsonはプロジェクト直下に配置します。

/
 App.js
 app.json
 google-services.json

app.jsonに設定を追記します。

  "android": {
    ....,
    "googleServicesFile": "./google-services.json"
  }

あともう少しです。

FCMサーバーキーの取得

Firebaseコンソールで[Project Overview の右の歯車] → [プロジェクトの設定] を押します。

f:id:tatsuyashi:20190215013817p:plain:w400

[クラウドメッセージング]タブを開くとサーバーキーが表示されているので、コピーします。

f:id:tatsuyashi:20190215014029p:plain:w500

ExpoへFCMサーバーキーの登録

最後にExpoへコピーしたサーバーキーを登録します。expoコマンドを使います。

expo push:android:upload --api-key <your-token-here>

途中でExpoのアカウント登録を要求されるので登録します。 登録後メールでverifyするとアカウント作成されます。

以上で必要な手順は終わりです。

プッシュ通知のテスト

ここまで来ると先ほどアプリ起動で出ていたエラーは出なくなり、以下のようなプッシュトークンがログに出力されると思います。

ExponentPushToken[xxxxxxx]

これをExponentから全てコピーします。

プッシュ通知は本来であればExpoのAPIを使用するのですが、下記のダッシュボード画面からプッシュ通知を送ることができます。

expo.io

プッシュトークンとタイトルと本文を入力し [Send a notification]をクリックします。

f:id:tatsuyashi:20190215014923p:plain:w500

すると、

f:id:tatsuyashi:20190215015215p:plain:w400

スマホ側にプッシュ通知が届きました!

まとめ

いかがだったでしょうか?
基本的にはExpoが提供するサンプルコードでプッシュトークンは取得できるのですが、以前は起こらなかったGCMのエラーが出るようになっているので今回記載したようにFirebaseプロジェクトを作成する必要が出てきています。

この後はAWS Lambda経由で実際にExpoのAPIをコールしてみたいと思いますので、また実現できたら書こうと思います。

筋トレログ2019 (1/19〜1/25)

f:id:tatsuyashi:20190211003338p:plain:w300

2019年1月19日(土)〜1月25日(金)の筋トレログを報告します。

今週から重量を載せるようにします。
軽量ウェイトでやってます!って言ってるのに100kgとかやってたらおかしいですもんね。

あ、過去2回の分もリライトします。

場所 レーニング内容
2019年1月19日(土) ジム ベンチプレス:10kg×2セット、20kg×1セット
スミスマシン(インクライン):0kg×3セット
ディップス:3セット
ラットプルダウン:15kg×4セット
サイドレイズ:1.5kg×3セット
2019年1月20日(日) 自宅 フロントレイズ:2.0kg×3セット
ダンベルカール:3.0kg×6セット
ワンレッグスクワット:6セット
プランク:1分×3セット
2019年1月21日(月) 自宅 リバースクランチ:3セット
2019年1月22日(火) 自宅 腕立て伏せ:5セット
スタンディングカーフレイズ:6セット
2019年1月23日(水) ジム ダンベルカール:3.0kg×6セット
ラットプルダウン:15kg×4セット
サイドレイズ:2.0kg×4セット
フロントレイズ:4.0kg×2セット
2019年1月24日(木) - 休養
2019年1月25日(金) - 休養

バーベル種目(ベンチプレス、スミスマシン)はバーベルの重さ抜きで重りのみの重さを記載しています。

レーニングは継続できていますが、書き起こしてみるとちょっと少ないかなという印象です。 さらに追い込んでいかないといけないですね!

2019年に取り組んでいる筋トレはこちら↓

tatsuyashi.hatenablog.com

筋トレログ2019 (1/12〜1/18)

f:id:tatsuyashi:20190206012645p:plain:w300

2019年1月12日(土)〜1月18日(金)の筋トレログを報告します。

2019年2月11日追記:重量を記載しました

場所 レーニング内容
2019年1月12日(土) ジム スミスマシン(インクライン):0kg×3セット
ディップス:3セット
ラットプルダウン:10kg×4セット
サイドレイズ:1.5kg×3セット
フロントレイズ:2.0kg×3セット
ダンベルカール:3.0kg×3セット
リバースクランチ:3セット
2019年1月13日(日) 自宅 スタンディングカーフレイズ:3セット
プランク:2セット
2019年1月14日(月) 自宅 サイドレイズ:1.5kg×3セット
フロントレイズ:2.0kg×3セット
ダンベルカール:3.0kg×3セット
ワンレッグスクワット:6セット
2019年1月15日(火) 自宅 スタンディングカーフレイズ:3セット
2019年1月16日(水) 自宅 腕立て伏せ:3セット
サイドレイズ:1.5kg×3セット
フロントレイズ:2.0kg×3セット
サイドプッシュアップ:6セット
2019年1月17日(木) ジム ラットプルダウン:10kg×3セット
ダンベルカール:3kg×4セット
2019年1月18日(金) - 休養

ジムトレと自宅トレでほぼ全身を満遍なくトレーニングできるようになりました。
背中のトレーニングだけはまだ自宅トレの良いメニューがないのでジムオンリーです。

おすすめの背中自宅トレがあれば教えてください!

2019年に取り組んでいる筋トレはこちら↓

tatsuyashi.hatenablog.com

筋トレログ2019 (1/5〜1/11)

f:id:tatsuyashi:20190205011248p:plain:w300

前回のブログで、2019年から新たなトレーニングを始めたと書きました。

なので2018年と同じように筋トレをしたログを残していこうと思います。

筋トレログ ver2になります。

ver2は週単位の報告とします。

初回は2019年1月5日(土)〜1月11日(金)の筋トレログになります。

2019年2月11日追記:重量を記載しました

場所 レーニング内容
2019年1月5日(土) 自宅 クランチ:2セット
リバースクランチ:2セット
スクワット:3セット
2019年1月6日(日) ジム ベンチプレス:5.0kg×3セット
スミスマシン(インクライン):0kg×3セット
ディップス:3セット
2019年1月7日(月) 自宅 サイドレイズ:1.5kg×3セット
スタンディングカーフレイズ:3セット
2019年1月8日(火) 自宅 プランク:1セット
サイドプランク:2セット
2019年1月9日(水) 自宅 腕立て伏せ:3セット
フロントレイズ:2.0kg×3セット
スクワット:3セット
2019年1月10日(木) - 休養
2019年1月11日(金) - 休養

7日中5日トレーニングを実施しました。
寒いので自宅トレが多くなり、自重系のトレーニングが多いですね。

自宅トレを導入すると、暑さ・寒さ・悪天候を理由にジムトレを休んでもトレーニングが継続できるのが非常に大きいと感じました。

部位を分けてトレーニングしているので、ほとんど毎日トレーニングしていても問題ありませんね。

これからも毎週分アップしていくのでよろしくお願いします!

2019年に取り組んでいる筋トレはこちら↓

tatsuyashi.hatenablog.com

軽量ウェイトと自重だけで体を変える!

f:id:tatsuyashi:20190131235658p:plain:w200

2019年

これまでと違った取り組みで本気で体を変えてみたいと思います。

それは

軽量ウェイトと自重だけで体を変える

です!

タイトルだけ見ると「甘い!」って言われるかもしれませんが、できるだろうという算段はあります。

実際の成果や経過については定期的にブログにアップしていきますが、今回はどういう取り組みを行うかについて書きたいと思います。

今回の取り組み内容と成功する根拠

まず今回の取り組みにおいて大きな軸となるのは「トレーニング」と「食事」となります。

レーニン

「軽量ウェイトと自重」

その名の通り、

を行います。

「軽いウェイト」とはどの程度かと言いますと、だいたい下表ぐらいと思ってください。

種目 重さ
ラットプルダウン 10〜20kg
ベンチプレス 10〜20kg (バー除く)
ダンベルカール 3〜4kg

2018年に行っていたトレーニングに比べると半分以下になっていると思います。

筋トレログ その14 - Tatsuyashi's Blog

筋トレログ その13 - Tatsuyashi's Blog

ただ、2018年までと明らかに違うのは各トレーニングの「」です。

これまでのトレーニングにおいて、今思うとイケてない部分がありました。

  • どのトレーニングがどの部位に効くか把握しきれていない
  • 「重さ」にチャレンジしている
  • フォームが完全に我流

上記のこともあり、思うような成果は出ていませんでした。

ところが、2018年末頃にジムを変えて、初めてパーソナルトレーニングを受けてみて衝撃を受けました。

ターゲットの部位を意識し、正しいフォームでトレーニングすることで、軽いウェイトでも今までにない位に筋肉に効く感覚があったのです。

逆の言い方をすると、
軽いウェイトでも効くぐらいに筋肉ができていないとも言えます。

ですので「軽量ウェイト」っていうのは、自分にとって、筋肉に効かせられる最低限の重量のことなんですね。

今回トレーニングとして意識することは、

  • 正しいフォームで行う
  • ターゲットの部位を意識する
  • フォームを崩さない程度のウェイトを扱う

の3点となります。

また、1セットの回数については意識しません。

回数を設定すると、回数をこなすためにフォームを崩したり逆に追い込みが足らなくなる恐れがあるので、「もう力入らん!」っていうぐらいまではやるようにします。

「軽量ウェイト」って書くと楽をするように見えますが全くそうではないですよw

ギリギリまで追い込むのには変わりありません。 追い込めないぐらいになってきて初めて、重量アップを検討することになります。

自重トレ

また、自重トレについても積極的に導入します。

理由としては、家でできるトレーニングを増やしたいからです。

ジムに加えて家でトレーニングをすることでレーニングの頻度を増やすことを狙います。

ちなみに私の家には1.5kgのダンベルしかないので(w)、家トレするなら必然的に自重トレがメインになりますね。

自重トレも同じようにトレーナーに予めフォームを教えてもらった上で、ターゲットを意識してトレーニングします。

当たり前ですが、自重は全然軽量ではないのでジムトレよりきつく感じます。

食事

そして食事です。

まず、体を変えるために大事なことは3つあります。

レーニングをして、適切な食事により筋肉を育て、休息を行い回復させる。

これは体が変わるシンプルな仕組みです。

3つのバランスが非常に大事になるのですが、中でも特に大事なのが「食事」です。

重要度でいうと、食事:トレーニングは7:3(または8:2)と言われています。

体が変わらないっていう人は「食事」を疎かにしている可能性があります。

私は素人なので専門的なことは言えませんが、食事に関して意識するのは

  • たんぱく質をできるだけ絶やさないように摂取する
  • 栄養を摂るタイミングに注意する

の2点です。

たんぱく質に関しては肉などの食事やプロテインのようなサプリメントで摂取します。
また、たんぱく質は吸収が早く枯渇すると筋肉が分解されてしまうので体内のたんぱく質をキープする必要があります。

そのため、朝食時のプロテインや休みの日の昼の間食としてプロテインを採ったりすることでたんぱく質を枯渇しないような工夫をしていきます。

また、こうやってトレーニングをしていると色んな栄養素を摂りたくなるので、ブロッコリーなどの野菜も多く摂るようにします。

(レシピ的なものもいずれはブログにあげるかもしれません)

スケジュール

ここまでで今回取り組もうとしている内容について書きました。

簡単にまとめるなら、

「正しいフォームで部位に効かせるために軽量ウェイトでトレーニングをし、トレーニング後やその後の栄養補給もぬかりなく行う」ことで体を変えていこうと考えています。

そもそも「体を変える」ってどういうことかと言いますと、

  • しっかり筋肉がついている(そこそこモリモリ)
  • 余計な脂肪がついていない

f:id:tatsuyashi:20190201013617p:plain:w300

この写真は相当モリモリですが、まぁこういうカッコイイ体を目指すということです。

「見た目」がどう変わるかを重視するので、体重などの数値に囚われないようにします。(一応記録はします)

スケジュールについては「筋肉を付ける(増量)」と「脂肪を落とす(減量)」は両立するのが難しいらしいので、時期を分けるように考えています。

  • 1〜4月 増量
  • 5〜7月 減量
  • 8〜10月 増量
  • 11〜12月 減量

8月以降は仮ですがだいたい3〜4ヶ月周期で増・減しようかなと思います。

この記事を書いている1,2月は増量期なので、今は割とフリーに食べていますが、減量期には炭水化物の量を今より減らしてかつ筋肉を極力減らさないような方法をこれから考えていきます。

どのぐらいの頻度でどれだけのトレーニングをしたかっていうのは、またブログにアップしていきます。

2018年の筋トレログのように1日単位だとちょっと記事量産になりそうなので、1週間分の報告という形で書いていくようにします。

これからどういう風に体が変わっていくのか楽しみですね!

Angularのモックサーバーを簡単に実現する

f:id:tatsuyashi:20190127002850p:plain:w200

Angularで開発する際に、バックエンドのAPIがまだ実装されていない状態でAngularの実装を行うことは多いと思います。

その場合にAPIを一旦モックにして進めると思いますが、選択肢としては、

  • モック用のAPIサーバを用意する
  • in-memory-web-apiを使用する
  • serviceメソッドの戻りを固定値にする

などが考えられると思います。

今回の記事では私が実際に行っている方法で、1〜3とは異なるモックの仕組みを紹介したいと思います。

ソースコードは以下で公開してますので、記事を飛ばしたい方はこちらから確認してみてください。

GitHub - tatsuyashi/angular-mock-sample

キモとなるコミットはこちら↓
use mock on dev-server · tatsuyashi/angular-mock-sample@1889bd6 · GitHub

やりたかった事

私がモックの仕組みを作成するにあたり、以下の条件を満たすものを考えました。

  • 他のライブラリに依存しない
  • 実際にHTTP通信を発生させる
  • モック→実APIへの切り替えは設定ファイル以外触らない
  • production環境には一切乗らない(bundleさせない)

モックの仕組み

今回のモックの仕組みを簡単に図にすると以下のようになります。

f:id:tatsuyashi:20190127012006p:plain:w500

ポイントをまとめると、

  • 開発時に起動するdev server上でモックとなるJSONを公開する
  • JSONのパスは /mock/{HTTPメソッド}/{URL}.json とし、APIのメソッドとURLに対応したファイルパスで作成する
  • production時は実APIをそのまま呼び出す
  • 上記の仕組みをHttpInterceptorを使用して実現する

となります。

dev serverをそのままモック置き場として代用することで、別でAPIサーバを立てる必要もないですし、アプリケーションから実際にHTTP通信が行われるようになります。

それでは、次章から実際のコードを基にポイントを説明していきます。

ソースコードはこちら↓
GitHub - tatsuyashi/angular-mock-sample

サンプルアプリケーションの説明

今回のサンプルですが、GET・POSTのボタンからそれぞれGET・POSTのAPIを呼び出し、結果をJSON形式で画面に表示するシンプルなものになります。

f:id:tatsuyashi:20190127013958p:plain:w500

dev serverにモックを公開する

まずはdev server上にモックを公開する手順を説明します。

①モックファイルを作成する

最初にモックとなるファイルを作成して配置します。

APIのメソッドとURLに対応したパスにJSONファイルを作成します。

メソッド URL JSONファイルパス
GET api/guitars src/mock/GET/api/guitars.json
POST api/guitars src/mock/POST/api/guitars.json
src/mock/GET/api/guitars.json

[
  {"maker": "Gibson", "name": "Les Paul"},
  {"maker": "Fender", "name": "Stratocaster"},
  {"maker": "Fender", "name": "Telecaster"}
]
src/mock/POST/api/guitars.json

{"maker": "Gretsch", "name": "Silver Falcon"}

②angular.jsonで公開設定

angular.jsonに下記を追加します。

angular.json

      "architect": {
        "build": {
            (略)
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/mock"  ←追加
            ]

これでdev serverで/mockも公開されました。

次にproductionのときに/mockを公開しないように設定します。

angular.json

          "configurations": {
            "production": {
              "assets": [  ←追加
                "src/favicon.ico",  ←追加
                "src/assets" ←追加 (src/mockは書かない)
              ],  ←追加
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              (略)

productionの設定内でassetsを上書き、その際にsrc/mockを含めないようにします。

以上でモックファイルの公開とproductionのときに含めない設定が完了しました。

HTTPリクエストの向き先をモックに変える

モックファイルの公開はできたので、次はAPIのHTTPリクエストをモックに向けるような仕組みを入れていきます。

①環境設定ファイルに項目追加

environment.tsにモックを使用するかどうかの値を保持します。

src/environment/environment.ts (environment.prod.tsも同じ)

export const environment = {
  production: false,
  mock: true  ← environment.prod.tsはfalse
};

②モックに向けるHttpInterceptorの実装

モック使用環境において、APIのリクエストをモックに変える部分はAngularのHttpInterceptorの仕組みを使用します。

Angular 日本語ドキュメンテーション

今回は以下のようにモック用のHttpInterceptorを用意します。

src/app/http-interceptors/mock-http-interceptor.ts

@Injectable()
export class MockHttpInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    
    if (environment.production || !environment.mock) {
      return next.handle(req);
    }

    const mockRequest = this.makeMockRequest(req.method, req.url);
    const newReq = req.clone({ ...mockRequest });
    return next.handle(newReq);
  }

  /**
   * make mock method and url.
   * @param method http method
   * @param url request url
   */
  private makeMockRequest(method: string, url: string): { method: string, url: string} {
    // method
    const mockMethod = 'GET';
    // change url
    const mockUrl = `mock/${method}/${url}.json`;

    return {method: mockMethod, url: mockUrl};
  } 


}

解説

makeMockRequestメソッドでモック用のHTTPメソッドとURLのセットを作成しています。

private makeMockRequest(method: string, url: string): { method: string, url: string} {
    // method
    const mockMethod = 'GET';
    // change url
    const mockUrl = `mock/${method}/${url}.json`;

    return {method: mockMethod, url: mockUrl};
  } 

※HTTPメソッドがGET固定になっていますが、これは現在の課題となっていて、GET以外の場合はURLが合っていても404(Not Found)になってしまうため、どんなHTTPメソッドであってもGETに変換するようにしています。

作成したモックのリクエストを返すことで向き先がモックに変わります。

  intercept(req: HttpRequest<any>, next: HttpHandler):
   (略)

    const mockRequest = this.makeMockRequest(req.method, req.url);
    const newReq = req.clone({ ...mockRequest });
    return next.handle(newReq);
  }

production環境の場合や、モックを使用しない設定になっている場合はそのまま返します。

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    
    if (environment.production || !environment.mock) {
      return next.handle(req);
    }
    (略)

③モック用のHttpInterceptorを有効にする-1

作成したHttpInterceptorを有効にするために、まずHttpInterceptorProvidersを作成します。

src/app/http-interceptors/index.ts

const devHttpInterceptorProviders = environment.production ? [] :  [
  { provide: HTTP_INTERCEPTORS, useClass: MockHttpInterceptor, multi: true },
];

export const httpInterceptorProviders = [
  ...devHttpInterceptorProviders,
  { provide: HTTP_INTERCEPTORS, useClass: XxxInterceptor, multi: true },
];

解説

devHttpInterceptorProvidersという開発環境のみ発動するHttpInterceptorProviderをまとめます。
productionモード時は空配列が返ります。
これを行うことでng build --prodでビルドした際にMockHttpInterceptorがbundleされなくなります。

const devHttpInterceptorProviders = environment.production ? [] :  [
  { provide: HTTP_INTERCEPTORS, useClass: MockHttpInterceptor, multi: true },
];

devHttpInterceptorProvidersとどの環境でも発動するHttpInterceptorProviderをまとめてexportします。
(XxxInterceptorは特に中身はありませんが、何かしらのInterceptorを定義した場合を想定しています)

export const httpInterceptorProviders = [
  ...devHttpInterceptorProviders,
  { provide: HTTP_INTERCEPTORS, useClass: XxxInterceptor, multi: true },
];

③モック用のHttpInterceptorを有効にする-2

exportしたHttpInterceptorProvidersをModuleでproviderとして設定します。

src/app/app.module.ts

  providers: [
    httpInterceptorProviders,
  ],

以上でdev serverを使ったモックの完成となります。

まとめと課題

この仕組みで冒頭に述べたやりたかった事が全て実現できるようになりました。

  • 他のライブラリに依存しない
    →追加ライブラリなし
  • 実際にHTTP通信を発生させる
    →dev server(port 4200)にアクセスする
  • モック→実APIへの切り替えは設定ファイル以外触らない
    →environment.tsの変更のみ。serviceクラスも変更不要
  • production環境には一切乗らない(bundleさせない)
    →モックファイルおよびモックのためのHttpInterceptorクラスはproductionビルド時にはbundleされない

ただ、いくつか課題は残っています。

  • POST、PUTなどのGET以外のAPIはHTTPメソッドを無理矢理GETに変えている
    →POST、PUT、DELETEはそのままリクエストすると404になるので苦肉の策でGETに変えてリクエストしています。
    dev serverの設定を変えればできるのかもしれませんが、方法がわからないため課題となっています。

  • JSON以外のファイル形式に対応していない
    →9割以上のAPIJSONを返すと思うためJSONしか対応していませんでしたが、その他のファイル形式には対応していません。

特に1つ目の課題はトライしましたが全くうまくいかなかったのでわかる方がいれば・・・

最後に、
Angularの開発を行う際にそのプロジェクトに応じた共通基盤を作成すると思いますが、その1つしてモックは出てくると思いますので、 今回紹介した方法がお役に立てれば幸いです。