先日 2017年1月19日に開催されたワークショップ「Oracle Cloud Developers Meetup@北海道 - AIが入ったBotの作り方を学ぼう」に参加してまいりました。
Oracle Cloud Developers Meetup@北海道 - AIが入ったBotの作り方を学ぼう
https://oracleclouddevelopersh.doorkeeper.jp/events/55595
このワークショップで進める具体的な手順は、Qiita に説明があります。
このワークショップに参加したにあたって、自分が行った手順を参考までにここにさらします。
PC/OS環境と開発ツール類のバージョン
自分が使っている PC 機材/OS が Windows なので、OS は Windows になります。具体的な OS バージョン、及び、ワークショップ資料で使用が明示されているツール類の自分の環境におけるバージョンを以下に記しておきます。
- OS - Windows 10 Pro x64 ver.1607 Build 14393.693
- Git for Windows - ver.2.8.4.windows.1
-
Node.js - ver.6.9.4
- npm - ver.3.10.10
- Heroku CLI - ver.5.6.12-1708b34 (windows-amd64) go1.7.4
エディタ
普段は、エディタというか統合開発として Visual Studio を主に使っています。
しかし今回は気分で Visual Studio Code を使うことにしました (以降は "VSCode" と略記します)。
今回のワークショップ参加時点での、自分が使用した VSCode のバージョンは v.1.8.1 です。
ちなみに、自分は、
「行末のセミコロンをタイプしたらその行を自動整形」
「ソースファイルを保存したら、そのソースコード全体を自動整形」
という、VSCode が備えるソースコード自動整形の機能を有効にして使ってます。
詳しくは下記ブログ記事などを参照してください。
Visual Studio Code で「保存時に自動整形」「タイプ時に自動整形」を有効にする
http://devadjust.exblog.jp/23791887/
コマンドプロンプト、というか PowerShell
本稿では "コマンドプロンプトで~" と記している作業は、実際には PowerShell コンソールで実行していました。
理由は、PowerShell 用の追加モジュールである「posh-git」をインストールしておくことで、CLI での git の操作が便利になるからです。
posh-git の紹介については下記ブログ記事などを参照してください。
PowerShell で Git
http://devadjust.exblog.jp/17296786/
TypeScript を使用
今回のワークショップでは、プログラミング言語として JavaScript を使います。
しかし自分は普段、JavaScript を生で書くことは滅多にありません。
代わりに、だいたいが TypeScript で書いています。
ですので、今回のワークショップでも、TypeScript で書くことにしました。
TypeScript コンパイラをインストール
自分の環境では既に TypeScript コンパイラはインストール済みです。
もしまだインストールしていない場合は、管理者として実行で開いたコマンドプロンプトにて、
> npm install -g typescript
をいちど実行しておくことで、システムグルーバルに TypeScript コンパイラがインストールされ、TypeScript コンパイラ、すなわち、tsc コマンドが実行可能となります。
ちなみに、自分がワークショップ参加時に使用した TypeScript コンパイラのバージョンは v.2.1.5 でした。
インストールされている TypeScript コンパイラのバージョンは、コマンドプロンプトから「tsc -v」を実行するとコンソールに表示されます。
TypeScript 用の開発環境を構成する
TypeScript コンパイル構成ファイルの作成
さて、まずはワークショップの説明どおりに、コマンドプロンプトを開いて作業用のフォルダを作成し、その作業用フォルダにカレントディレクトリを移動します。
次に、
tsc --init
を実行します。
このコマンド実行により、TypeScript コンパイル時の構成が書かれたJSON形式の設定ファイル「tsconfig.json」が既定値の設定でカレントディレクトリに新規作成されます。
今回のワークショップでは、まずは既定の tsconfig.json のままとくに設定変更せずに進めます。
VSCode の "タスク" を作成
次に下記コマンドを実行して、カレントディレクトリを作業用フォルダとして、VSCode を起動します。
code .
ピリオド ( . ) は、カレントディレクトリを意味する記号ですね。
VSCode が起動したら、
「TypeScript のソースコード (拡張子 .ts) が保存されたら、自動で TypeScript コンパイラでコンパイル」
されるように、VSCode の "タスク" 機能の設定ファイルを作成します。
といっても、VSCode が既定の設定を用意済みなので、作業は簡単です。
- VSCode 上で Ctrl + Shit + P を押して、「コマンドパレット」入力欄を開く
- そのまま "task" とタイプする
- すると実行可能なコマンドの中から "task" を名前に含むタスクに絞り込まれる
- 絞り込まれた中に "Tasks: Configure Task Runner" という項目(タスク)があるので、カーソルキーの上下でこれを選択し、Enter を押す
- すると「どのようなタスク実行環境を作るのか?」という選択肢が表示される
- カーソルキーの上下を押して "TypeScript - Watch Mode" を選択して Enter を押す
以上で必要となる設定ファイル「.vscode/tasks.json」が新規作成され、VCode エディタ内に開かれます。
今回のワークショップの用途では、これ以上のカスタム設定は不要なので、VCode エディタ内に開かれている「.vscode/tasks.json」はそのまま閉じます。
これで準備はできたので、VSCode 上で Ctrl + Shift + B を押します。
すると VSCode は、先ほど生成した「.vscode/tasks.json」に従って "タスク" を実行します。
今回作成したタスクは "TypeScript - Watch Mode" というタスクなのですが、これは、「TypeScript コンパイラを監視モードで実行、そのまま待機」という処理を実行するタスクなのでした。
そのため、いちど VSCode 上で Ctrl + Shit + B を押してこのタスクを走らせ始めたら、
- 以降 VSCode を終了させるまで TypeScript コンパイラが常駐し、
- TypeScipt ソースコードの追加・変更が発生するたびに、コンパイルの実行が行われる
ようになります。
手始めに、VSCode 上から新規ファイル「index.ts」を作成してみます。
すると、(Ctrl + Shift + B によって常駐開始した TypeScript コンパイラによって) それをコンパイルしてできあがる「index.js」が現れるのがわかります。
続けて、「index.ts」に TypeScript コードを記述してから、Ctrl + S を押すなどして保存してみます。
すると、「index.ts」の変更が (常駐している) TypeScript コンパイラに検知されてコンパイルが実行され、「index.js」にコンパイル結果の JavaScript コードが書き込まれます。
以上で TypeScript でコーディングすると、同時に対応する JavaScript ファイルが自動でコンパイル・生成される開発環境が整いました。
TypeScript における "require"
続けて、ワークショップの手順 (参照: http://qiita.com/nkjm/items/27d0131003a4b7ef02b9 ) に従い、「npm init」で「packages.json」の初期生成を行い、さらに「npm install express --save」で「express」Node パッケージモジュールをこの作業用フォルダに追加したりします。
そして「index.ts」にて「express」モジュールを使用するためにインポートする構文を記述します。
Node.js 環境で実行される JavaScript コードにおける、「外部モジュールの読み込み」は、
var express = require("express");
と書きます。
これを TypeScript の構文では、
import express = require('express');
というように import 構文で書きます。
TypeScript 型定義ファイルのインストール
さてこれで、ワークショップに従って TypeScript でのコーディングを進めていけます。
しかしこのままでは、インポートした「express」モジュールを使用するコードを書いていても、VSCode によるコード補完・メンバー候補表示も発動しなければ、コーディング中の即時構文エラー表示もされません。
というのも、VSCoe ( 及びこれを支援する TypeScipt コンパイラ ) が、インポートした外部モジュール「express」について、どんなメソッドやプロパティを持つどんなクラスが収録されているか、情報を持ち合わせていないためです。
これではせっかくの TypeScript でコーディングも魅力半減です。
そこで、外部モジュール「express」に関して、"この「express」モジュールにはどんなメソッドやプロパティを持つどんなクラスがあるのか?" という "型定義" を記述した TypeScript ファイル、すなわち「TypeScript 型定義ファイル」(拡張子 .d.ts の場合が多い) を作業用フォルダにダウンロードしてくることにします。
ありがたいことに、多くの Node パッケージモジュールについて、対応する TypeScript 型定義ファイルが用意されています。
その TypeScript 型定義ファイルは、npm コマンドでダウンロードしてくることができます。
例えば「express」モジュールについての TypeScript 型定義ファイルを作業用フォルダにダウンロードしてくるには、下記コマンドを実行します。
> npm install @types/express --save-dev
これで、作業用フォルダ以下「node_modules/@types」フォルダ以下に、必要となる TypeScript 型定義ファイルがダウンロードされてきます。
VSCode とこれを背後で支えている TypeScript コンパイラは、「node_modules/@types」フォルダ以下に TypeScript 型定義ファイルが配置されるはず、という仕様で動作しています。
そのため、こうして npm コマンドで配置された TypeScript 型定義ファイルが認識され、「express」外部モジュールについても、コード補完やヒント表示、即時構文エラー表示などが機能し始めます。
このように、多くの場合、
> npm install @types/{Nodeパッケージモジュール名} --save-dev
を実行することで、その Node パッケージモジュールに対応する TypeScript 型定義ファイルがダウンロードされてきます。
もっとも、すべての Node パッケージモジュールについて、対応する TypeScript 型定義ファイルが用意されているとは限りません。
上記 npm コマンドが「そのような型定義は見つからない」というエラーで終わることもあります。
そのような場合は、私は、基本的にはあきらめて VSCode 及び TypeScript コンパイラの支援をあてにせずにコーディングを進めています。
その上で、適宜、インラインで型注釈を自分で自分のソースコード側に書き足したりしてしのいでいます。
なお、TypeScript 型定義のダウンロードにあたり、npm コマンドのオプションとして「--save-dev」を指定しています。
これは「実行時には不要だけど、ビルド時や開発時には必要なモジュールだよ」という内容で、packages.json に依存パッケージとして記述を追加するものです。
このように packages.json に設定が書き足されていれば、node_modules フォルダ以下を除いたソースコード一式があれば、「npm install」を実行することでいつでも、node_module フォルダ以下を復元できる次第です。
ローカル git リポジトリを作成してから heroku apps:create
ワークショップの手順 ( http://qiita.com/nkjm/items/27d0131003a4b7ef02b9 ) によると、
- 先に heroku CLI を使って「heroku apps:create <アプリ名>」で heroku 上に Web サイトを作成してから、
- その次にローカルの git リポジトリを作成して、
- その上で heroku 上のリモートリポジトリを「git remote add heroku ~」によってリモート名 heroku で参照できるよう追加
という手順になっています。
しかし自分は、
「既存のローカル git リポジトリのフォルダ上で、heroku apps:cretae を実行すれば、heroku コマンドが自動でリモート名 heroku でリモートリポジトリの登録もやってくれる」
ということがわかっていたので、
- 先にローカルの git リポジトリを作成して、
- それから「heroku apps:create」で heroku 上に Web サイト作成
の手順で実行しました。
これだけで以後、「git push heroku master」コマンドが実行できます。
つまり「git remote add heroku ~」の手順が省けます。
さらに「heroku apps:create」したときに用意されるリモートリポジトリの URL を書き留める手間もありません。
heroku 上にアプリ作成する際のアプリ名も未指定でお任せで
今回のワークショップで作成する LINE ボットのシステムは、そのまま本稼働させるつもりは毛頭ありませんでした。
そのため、ワークショップを通して自分が作成する Web エンドポイントの URL には、まったくこだわりありません。
そこで heroku コマンドで Web サイト作る際には、「heroku apps:create」とだけ指定して実行し、アプリ名は heroku が自動生成するに任せました。
アプリ名を明示しないで heroku コマンドで Web サイト生成を実行すると「https://wildwood-aqueous-12345.herokuapp.com
」のようなランダムな URL を自動で割り当ててくれます。
これなら、まだ使われていないアプリ名を考えるのに頭を悩ます必要がありません。
もっとも、上記のように自動生成されるランダムな URL は長くておぼえにくいという欠点はあります。
しかし、どうせその URL をメモしておいたり手打ちしたりする必要はないのです。
単に作業用フォルダをカレントディレクトリとして「heroku open」を実行すれば、既定のブラウザで heroku 上の URL を自動で開いてくれるからでです。
もっとも、さすがに LINE ボットに Webhook のエンドポイントを指定するためには、この heroku 上の URL が必要になります。
その場合も心配いりません。
- 作業用フォルダをカレントディレクトリとして「heroku open」実行
- ブラウザが起動して当該 Web サイトが開く
- alt + D を押してアドレスバーにフォーカス移動
- Ctrl + C でクリップボードにコピー
のステップで URL を入手可能できるので問題ないでしょう。
ワークショップの用途ならこれで十分というか、むしろどうでもいいような細かいことに手を煩わせたり頭を悩ませたりせずに済みます。
.gitignore について
ワークショップの説明 ( http://qiita.com/nkjm/items/27d0131003a4b7ef02b9 ) ですと、「Git バージョン管理外とするファイル」を指定する .gitignore ファイルについて、.gitignore ファイルそれ自身を登録してある点が気にかかりました。
たしかに、「手元のローカル開発中の内容を heroku にアップロードするだけ」を目的とすれば、このワークショップを実践する間は、このことは問題になりません。
しかしながら、今回のワークショップを超えて一般的な Git によるバージョン管理の都合を考えると、.gitignore ファイル自身をバージョン管理に追加しないのは不都合のほうが多い気がします。
例えば、今回のワークショップによって構築されたローカル Git リポジトリを、ワークショップの成果として GitHub などで公開したとしましょう。
これを誰か別の人がまっさらな作業用フォルダに git clone して、npm install したあと、その人が独自のプログラム変更を加えて git commit しようとするとどうなりますか?
node_modules フォルダ以下の、(本来は Git バージョン管理下に置く必要のない) 大量のファイル群が Git バージョン管理下に誤って追加されてしまうかもしれません!
そのような背景を考え、自分は .gitignore ファイル中には .gitignore ファイルそれ自身は指定せずにワークショップを進めました。
master ブランチの追跡ブランチに heroku リモートリポジトリを登録する
見出しは難解ですが、やろうとしていることは「git push」だけで、heroku へのプッシュができるようにしよう、という話です。
ワークショップを進めていくにあたり、手順が進むごとに git commit してそれまでの変更内容をローカル Git リポジトリに履歴登録 ⇒ git push heroku master を実行して heroku のリモートリポジトリへ履歴をプッシュ同期、を繰り返します。
しかしなにかあるごとに「git push heroku master」をタイプするのは面倒です。
もちろん、コマンドプロンプトにはコマンド履歴があります。
ですので、コマンドプロンプトでカーソルキーの上を押して過去入力したコマンドをさかのぼって再実行できます。
はたまた、"git" とかまで入力して F8 を押すことで "git" で始まるコマンドに絞ってコマンド履歴を列挙しての再実行もできます。
実際、コマンド履歴を使って同じコマンドを再実行することはよくやります。
しかし他のコマンドを多数入力するような作業のあとだと、さすがのコマンド履歴でも「git push heroku master」を探すのがだるくなってきたりします。
そこで、「git push heroku master」するときにいちどだけ、「-u」スイッチをつけて実行するのです。
すると、ローカルの master ブランチの追跡ブランチとして、heroku のリポジトリ (の master ブランチ) が登録されます。
この効能にはいくつかあるのですが、とくにこのワークショップを遂行するにあたっての利点は、
「以後、git push するだけで、git push heroku master と同等の動作となる」
点です。
これで、コマンド履歴を呼び出すのがだるいときや、ワークショップを一時中断して後日再開する場合などでも、コマンド履歴に頼ることなく「git push」とだけタイプすることでリモートリポジトリの内容を heroku へプッシュできるようになります。
なお、この技は、git を既定のオプションでインストールした状態を仮定すると、たぶん git の ver.2 以降でないとうまくいかないかもしれません。
また、チームメンバ同士とは GitHub や Bitbucket 等でソースコードを保守していて、そのいっぽうで都度手作業で heroku にプッシュしているような場合は (普通はそんなことせずにせめてリポジトリサービスの Webhook 使うとは思いますが、それはさておき)、ソース管理側とデプロイ先とでプッシュ先を仕分けたいでしょうから、この裏技は使えないかと思います。
とはいえ今回のワークショップのように、heroku へのデプロイにしか git push を使わないなど、状況が限定されていれば、このような小技でタイピングの手間やミスを減らすことができます。
アクセストークンなどの秘密の情報はソースコード上に直書きしない
ワークショップを進めていくなかで、LINE ボットの API を呼び出すために、「チャンネルアクセストークン」という認証用のキー (Bese64 エンコーディングされた結果の文字列) が必要となります。
ワークショップ中では、この認証用のキーを、ソースコード中に直書きしておりました ( http://qiita.com/nkjm/items/27d0131003a4b7ef02b9 ) 。
しかしながら、もちろん、このような秘密の情報は、ソースコードに直書きすべきではありません。
というのも、今回はワークショップだから、まぁよしとしても、通常ソースコードは、共通のリポジトリを介して開発メンバー間で共有された形でバージョン管理下におかれるはずで、そのような形で機密情報を共有することは好ましくないからです。
さらには下記事例のようなリスクもあります。
初心者がAWSでミスって不正利用されて$6,000請求、泣きそうになったお話。
http://qiita.com/mochizukikotaro/items/a0e98ff0063a77e7b694
ではそのような秘密の情報はどこに設定してどのように参照すればよいでしょうか?
私は Node.js および heroku について熟練しているとは言えません。
従いまして、ここに記載する内容に誤りやセキュリティ上の危険が含まれている可能性が少なからずあります。
しかしそれを承知で記しますと、自分の知る限りにおいては、heroku における "秘密の情報の格納先" は「heroku config」コマンドによる環境変数への設定です。
具体的には、下記のように、作業用フォルダをカレントディレクトリとして heroku コマンドを実行することで、heroku Web サイトのプログラム実行環境における環境変数 LINE_CHANNEL_ACCESS_TOKEN に認証用のキーを設定できます。
heroku config:set LINE_CHANNEL_ACCESS_TOKEN=P10I3uquic...
そしてプログラム中からは、環境変数を参照することで、この認証用のキーを入手します。
let accessToken = process.env.LINE_CHANNEL_ACCESS_TOKEN;
これでプログラムのソースコード中に直接、認証用のキーを記述することはなくなり、代わりにプログラム実行環境jの設定として認証用のキーが設定・保存されるようになりました。
Promise の使用で TypeScript 構文エラーが発生
さて、ワークショップを進めていくと、途中で以下のようなコードを記述することになります ( http://qiita.com/nkjm/items/d46bd91e1784adf1434b )。
return Promise.all(gotAllNutrition);
しかしどうしたことでしょう、このコードは「can not find name 'Promise'」という構文エラーとなって JavaScript へのコンパイルが失敗します。
原因は、TypeScript コンパイラへの指示ファイル「tsconfig.json」中のオプション指定にありました。
tsconfig.json 内の各種指定の中に、「TypeScript ソースコードを、どのバージョンの JavaScript コードとして変換するか?」という、コンパイル先の JavaScript 環境のバージョンを指定する項目があります。
「tsc --init」コマンドの実行によって作成した既定値のままだと、この JavaScript のバージョン指定が「es5」となっています。
Promise オブジェクトは「es5」ではサポートされておらず「es6」から使用できるオブジェクトです。
それで「can not find name 'Promise'」構文エラーになっていたのでした。
ワークショップ資料で示されている元の JavaScriot コードで Promise オブジェクトを使用しているように、当然のことながら、今回実行環境となっている heroku 上の Node.js 環境は、「es6」かそれ以降の JavaSrcipt バージョンとなっています。
ですので、この問題の解決策は、TypeScript コンパイラオプションにて、生成対象の JavaScript バージョンを「es5」から「es6」に引き上げてやれば OK です。
具体的には、tsconfig.json を VSCode で開き、
"target": "es5",
の箇所を
"target": "es6",
に書き換えて保存すれば OK です。
これで「can not find name 'Promise'」構文エラーは解消され、引き続きコーディングを進められます。
非同期処理を async/await 構文で書く
ワークショップを進めていくと、外部の Web API (MeCab の SaaS とか食品データベースとか) に HTTP 要求/問い合わせする処理が登場してきます ( http://qiita.com/nkjm/items/d46bd91e1784adf1434b )。
実装に使用している言語・処理系が JavaScript・Node.js ですので、このような外部 I/O 処理は非同期処理となります。
Promise パターンが使えますので、.then(~)
メソッドのチェーンで記述することで多少は改善されるものの、それでもなお、コールバック関数で結果を受け取るコーディングスタイルは、なぜか疲れるものです。
さてところで、先述のとおり、自分が使用している TypeScript コンパイラのバージョンは v.2.1 以降です。
そう、このバージョンの TypeScript コンパイラであれば、async/await 構文で非同期処理をコーディングできます!
本稿では TypeScript での async/await 構文についての解説は割愛しますが、以下に実際にワークショップで書いたコード例を張っておきます。
元のワークショップ資料での参照実装を TypeScript に置き換えて記述したコードは下記のとおりですが、
app.post('/webhook', (req, res) => {
...
mecab.parse(event.message.text)
.then((res:string[][]) =>{
var foodList = [] as string[][];
for (var elem of response){
if (elem.length > 2 && elem[1] == '名詞'){
foodList.push(elem);
}
}
var gotAllNutrition = [] as any[];
if (foodList.length > 0){
for (var food of foodList){
gotAllNutrition.push(shokuhin.getNutrition(food[0]));
}
return Promise.all(gotAllNutrition);
}
})
.then(res => console.log(res));
これをまずは、C# で LINQ 大好きな自分の好みから、some や filter を使った下記実装に変えてみました。
app.post('/webhook', (req, res) => {
...
mecab.parse(event.message.text)
.then((res: string[][]) => {
let gotAllNutrition = res
.filter(r => r[1] == '名詞')
.map(r => shokuhin.getNutrition(r[0]));
return Promise.all(gotAllNutrition);
})
.then(res => console.log(res));
そして、Promise パターンを使った、.then(~)
メソッドでのコールバック方式の実装を、async/await 構文に書き換えてみます。
app.post('/webhook', async (req, res) => {
...
let res = await mecab.parse(event.message.text) as string[][];
let gotAllNutrition = res
.filter(r => r[1] == '名詞')
.map(r => shokuhin.getNutrition(r[0]));
let allNutrition = await Promise.all(gotAllNutrition);
console.log(allNutrition);
行数的には大差ありませんけれども、非同期処理なんだけどコードの記述上はあたかも逐次処理であるかのようにコーディングでき、実装しやすく、理解もしやすいコードになったのではないかと思っております。
VSCode 上でデバッグ実行
ワークショップの手順どおり、コード書く⇒ git commit する ⇒ git push する ⇒ heroku logs --tail でログ見る、のサイクルで開発を進めてもよいのですが、
- heroku への git push はデプロイ作業も込みなので少しばかりではありますが時間がかかりますし、
- ちょっと何か試したいだけでも git commit で履歴作らないといけない
など、だんだんだるくなってきます。
そこで、ローカル開発上の環境で、VSCode でデバッグ実行してみることにします。
TypeScript コンパイル時のソースマップ生成を On にする
デバッガで実行となると、やはり、TypeScript ソースコード上でのブレークポイント設置や変数ウォッチがしたいと思います。
その準備として、TypeScript から JavaScript へのコンパイル時に、ソースマップファイルも生成するよう、TypeScript コンパイラへのオプション指示ファイル「tsconfig.json」を編集します。
tsconfig.json を VSCode で開き、
"sourceMap": false
の箇所を
"sourceMap": true
に書き換えて保存すれば OK です。
これにて以降、.ts ファイルの .js ファイルへのコンパイル時に、ソースマップファイル .js.map も生成されるようになります。
VSCode でのデバッグ実行を構成
さて次に、(いろいろやり方はあろうかと思いますが) いきなり、VSCode 上で F5 を押してデバッグ実行の開始を VSCode に指示します。
しかし VSCode も「デバッグ実行しろっていわれても、何をどう起動してデバッガにアタッチしたらいいの?」と何の構成も与えられていないため困ってしまします。
そこで VSCode はコマンドパレットを自動で開き、既定で用意されている構成を候補表示してくれます。
この候補の中に、ちゃんと都合良く「Node.js」がありますので、これを選択して Enter します。
すると、今回のワークショップで使用している Node.js 環境用のデバッグ実行構成が書かれた「.vscode/launch.json」が既定の構成で生成され、VSCode のエディタ領域に開かれます。
今回はこの既定の構成のままでよいので、VSCode のエディタ領域に開かれた launch.json はそのまま閉じます。
これで準備ができたので、もういちど VSCode 上で F5 を押します。
すると、ちゃんとデバッガ配下で「node index.js」が開始し、VSCode 上にデバッグ実行時のツールバーが表示され、デバッグコンソールも開いて、Node.js によるコンソール出力が表示されます。
(※なお、ブラウザは自動では起動しません)
例えば index.ts 上に下記のようなコードがあったとして、
app.get('/', (res, req) => {
req.send('hello');
});
上記 req.send('hello');
の行にキャレット置いて F9 を押してブレークポイントを設置したとします。
この状態から、任意のブラウザで http://localhost:3000
を開くと (※今回のワークショップの手順では、TCPポート番号 3000 で express がリッスンするようになっています)、VSCode 上でそのブレークポイントで停止してくれます。
もちろん、ブレークしている最中は変数の内容をウォッチしたり、必要とあれば変数の内容を書き換えたりもできます。
ngrok でインターネットに公開
さてこれでローカル開発環境での実行・デバッグは可能にはなりました。
しかしこのローカル開発環境で実行されている express サーバーは、あくまでも、このローカルの PC 内でのみリッスンしているだけです。
LINE ボットからの Webhook 呼び出しのように、インターネット側からはこのローカルの express サーバーに到達することはできません。
そこで、「ngrok」というサービスを利用することにします。
ngrok は、専用のクライエントアプリ(コマンド)を事前に PC にダウンロード・インストールしておき、この ngrok コマンドを実行することで、ローカル環境で実行されている Web サーバーを、ngrok が用意してくれるインターネット側のエンドポイントにリバースプロクシでつないでくれます。
...上記説明で何言っているかわからなくても大丈夫です。
下記など参考にされるとよいでしょう。
ngrokを使用してローカル環境を外部に公開する
http://qiita.com/kitaro729/items/44214f9f81d3ebda58bd
実際にやってみましょう。
すでに ngrok コマンドが利用可能な状態だとして、コマンドプロンプトから、
> ngrok http 3000
を実行します。
つまり、ローカルの TCP 3000 番ポートで待ち受けしている HTTP サービスを、インターネットに公開してください、という指示です。
すると ngrok サービスによって https://(ランダムな文字列).ngrok.io
というインターネット上の URL が自動で払い出され、ngrok コマンドのコンソールに表示されます。
この URL へのアクセスが、ローカルのポート 3000 番の Web サーバーに転送されてくるようになるのです。
あとは、LINE ボットの設定 Web ページにて、Webhook の URL を、この ngrok が払い出してくれた URL に変更します。
その上で、LINE からボットに対しメッセージを送ると、VSCode でデバッグ実行中の express に要求が飛んできます。
app.get('/webhook', (res, req) => { ...
の直後あたりにブレークポイントを設置しておけば、LINE ボットがメッセージを受信すると、ここで停止し、変数ウォッチなど自由に見て回ることができるようになります。
まとめ
まとめといっても何もないんですが、以上、ワークショップ参加したにあたり、自分流の実装作業の内容をさらしてみました。
誰か彼かの作業効率向上、コーディングの楽しみを増やすことに貢献できれば幸いです。
あと補足ですが、本稿に記したような自分が作業効率優先のためにとったいくつかの Tips は、ワークショップの手順上は「ブラックボックス化してはいけない」との配慮から、あえて「何をやっているか」がわかるように意図した手順である可能性があります。
他にも同様に、何らかの意図があってのワークショップ手順であるところを、本稿で記載したような私のやり方は、その折角の意図を潰してしまっている可能性もあろうかと思います。
その点は何卒ご留意ください。