【AWS Lambda×LINE Messaging API】ちょー簡単！オウム返しBotを作ろう

## はじめに
皆さん、Lambdaをご存知でしょうか？
Lambdaはサーバーレスアーキテクチャを実現する上で根幹となるサービスです。

### サーバーレスアーキテクチャとは
AWSにおけるサーバーレスとは、**「インスタンスベースの仮想サーバー（EC2など）を使わずにアプリケーションを開発するアーキテクチャ」**を指します。

一般にシステムの運用には、プログラムを動かすためのサーバーが必要です。
そしてそのサーバーは、常に稼働していなければなりません。

しかし開発者がやりたいことは、「サーバーの管理」なのでしょうか？
エンドユーザーに価値を届けることこそが使命なわけです。

ということで、こういうめんどくさい作業から解放してくれるのがサーバーレスアーキテクチャなわけです。

サーバーレスアーキテクチャでよく使われるサービスは以下の通りです。
特に、丸で囲っている3つがよく使われます。

![スクリーンショット 2021-06-26 15.43.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/4d71d546-5839-0784-8cd0-d8d4466e915c.png)

### AWS Lambdaの特徴
サーバーのプロビジョニング/管理なしでプログラムを実行できるサービスです。
**「何言ってんの？」**と思われた方のために要約すると、
「コードの実行などはLambda側でやるから、あなたはコードを書いてなさい」ということです。

そして、Lambdaの圧倒的なメリットは料金の安さです。

https://aws.amazon.com/jp/lambda/pricing/

リクエストベースの料金体系となっており、
実行回数×実行時間となっています。

リクエスト　100万件あたり 0.20USD
実行時間　GB-秒あたり 0.0000166667USD

これだけ安いのにも関わらず無料枠もあります。
1か月ごとに100万件の無料リクエスト、および40万GB-秒のコンピューティング時間が無料利用枠です。

**なので、もはや0円です。**
HerokuやNetlifyなど無料でデプロイできる選択肢はたくさんありますが、
これらはレスポンスに時間がかかるなど問題があります。

そういった問題も緩和できるLambdaは初学者の味方です。

しかし対応言語が限られているのが若干のネックです。
Java、Go、PowerShell、Node.js、C#、Python、Rubyなどが対応しています。
有名どころとしては、PHPが対応していないのが残念ですが、カスタムランタイムを実装することで利用可能になります。（少々難易度が上がります）

### アプリケーションエンジニアこそぜひ！！
サーバーレスアーキテクチャであれば、EC2と比べ自由度は低くなりますが、管理の手間がほぼありません。
なので、**アプリケーションエンジニアのインフラの入口としては最適な題材です。**

DynamoDBはRDSに比べるとNoSQLならではの癖があるので、1つ壁があります。（Joinが使えないなど）
テーブル構造など意識して取り組む必要があるのでここだけ難易度があります。

ということで今回は、LambdaとAPI Gatewayの2つのみを使ってオウム返しのBotを作りたいと思います。（デバッグのためにCloutWatchも少しだけ使います。）

## アーキテクチャ
LINEで「こんにちは」と入力したら、「こんにちは」と返ってくれば成功です！

![arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/0653accd-134b-8aa3-8bd4-d978acb50e23.png)

## ハンズオン
### 前提
初めてAWSを使う方に対しての注意です。
ルートユーザーで行うのはよろしくないので、全ての権限を与えた`Administrator`ユーザーを作っておいてください。

公式サイトはこちらです。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/getting-started_create-admin-group.html

文章は辛いよって方は、初学者のハンズオン動画があるのでこちらからどうぞ。

https://pages.awscloud.com/event_JAPAN_Ondemand_Hands-on-for-Beginners-1st-Step_LP.html?trk=aws_introduction_page

また、LINE Developersのアカウントも必要となります。
LINE関連の記事を書いているのでこちらから確認してください。
**チャネルシークレット**と**チャネルアクセストークン**が発行できればOKです。

https://qiita.com/Ryo9597/items/4cd1908e5bd41467cd52#line-developers%E3%81%AB%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

### Lambdaで使うコードを作る
LINE Messaging API SDK for nodejsに書いていることをやっていきます。
こちらがまんまオウム返しなので、これをLambdaで使えるように書き直していきます。

https://line.github.io/line-bot-sdk-nodejs/getting-started/basic-usage.html#synopsis

#### `package.json`の作成
```terminal:ターミナル
$ npm init -y
```

#### パッケージのインストール
```terminal:ターミナル
$ npm install @line/bot-sdk --save
```

#### コードを書いていきます
先に説明すると、`exports.handler = async (event, context) => {}`内にコードを書いていきます。

ちなみにここで、大切なので**`コールドスタート`**と**`ウォームスタート`**についても解説します。
Lambdaは呼び出されると、コンテナ上でプログラムが実行されます。
コンテナは再利用されますが、利用可能なコンテナがないときはコールドスタートとなります。
また、`exports.handler`外の場合、ウォームスタートとなります。
なので使い回す処理などはウォームスタートにすると早く動作します。

![lambda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/73d830c7-945b-0b42-2250-88dc36914f6e.png)

```js:index.js
'use strict';

// パッケージのインストール
const line = require('@line/bot-sdk');

// LINEアクセストークンの設定
const config = {
  channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
  channelSecret: process.env.CHANNEL_SECRET,
};

// インスタンス化
const client = new line.Client(config);

exports.handler = async (event, context) => {
  // eventを出力
  console.log(event);

  // JSONとして解析して値やオブジェクトを構築する
  const body = JSON.parse(event.body);
  console.log(`【body】: ${JSON.stringify(body)}`);

  // LINE Eventを取得
  const response = body.events[0];
  console.log(`【response】: ${JSON.stringify(response)}`);

  // メッセージ送信のために必要な情報
  const replyToken = response.replyToken;
  const post = {
    type: 'text',
    text: response.message.text,
  };

  try {
    await client.replyMessage(replyToken, post);
  } catch (err) {
    console.log(err);
  }
};
```

`console.log`は、CloudWatch内で確認できるので、デバッグも容易です。
なので、ここでコードの全体像が理解できなくても大丈夫です！

### Lambdaを作成する
Lambdaとコンソールで検索してください。

![スクリーンショット 2021-06-26 18.10.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/834e6e15-f221-6a5c-72e8-24bc973aac9b.png)

「関数の作成」をクリック

![スクリーンショット 2021-06-26 18.11.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/15167ee4-6f99-d433-bde5-1f4798096fa2.png)

設定を見ていきましょう！

![スクリーンショット 2021-06-26 18.13.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/601083cd-0ec5-6803-c230-fb971e7fd883.png)

作成完了しました！

![スクリーンショット 2021-06-26 18.15.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/5ebda4aa-d2d8-ae76-3c11-080ac3cbf54a.png)

では、先ほど作成したコードをLambdaで使えるようにしましょう。
方法は2つあります。
・S3経由
・zip経由

zipの方が簡単なので今回はzipにします。

`node_module`と`index.js`の2つをzipにしてください。
命名は適当でいいです。

![スクリーンショット 2021-06-26 18.18.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/a4820cb4-bf81-4459-26da-e554c7f9084c.png)

ではアップロードしましょう。

![スクリーンショット 2021-06-26 18.19.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/d1194a75-1496-85e1-1910-0ce1406a6421.png)

コードがLambdaで確認できました！

![スクリーンショット 2021-06-26 18.20.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/af68ca86-d768-117c-a4c1-94de881e32ff.png)

では次に環境変数の設定を行います。
先ほどLINEで取得した値をこちらで入力します。

![スクリーンショット 2021-06-26 18.22.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/8426cf30-a8e2-0d23-bae5-300b88378803.png)

これでLambdaは完了です。

### API Gatewayの設定

API Gatewayとコンソールで検索してください。

APIタイプはREST APIで作成します。

![スクリーンショット 2021-06-26 18.24.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/97b65523-21ba-b4ad-58f7-c6a659f45bdc.png)

API名は適当でいいです。

![スクリーンショット 2021-06-26 18.26.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/0d55185b-dd09-2deb-d387-c1f00ce5483a.png)

メソッドの作成をしましょう。

![スクリーンショット 2021-06-26 18.27.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/774ef2d6-3687-8dc2-b8bc-bbb096568c81.png)

POSTを選択して決定。

![スクリーンショット 2021-06-26 18.28.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/690e6e9b-589a-4256-16a7-e61a85ea9ad8.png)

セットアップを行う。

![スクリーンショット 2021-06-26 18.29.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/a979f2fa-611c-a7ab-fe55-6df3de219cb3.png)

CORSの設定を行います。

![スクリーンショット 2021-06-26 18.31.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/7028ed30-441a-2047-6445-84962caabaf4.png)

デフォルトのままでOK

![スクリーンショット 2021-06-26 18.31.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/85d407c5-867d-9b4d-c6f0-5be8399d7f26.png)

最後にデプロイしましょう！

![スクリーンショット 2021-06-26 18.32.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/8e6b087b-72eb-53e2-d84a-b0fb2a8b6e72.png)

ステージ名も適当でいいです。

![スクリーンショット 2021-06-26 18.33.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/97af9dee-4332-4632-6f8d-4dd0a577976f.png)

このようにURLが発行されます。

![スクリーンショット 2021-06-26 18.34.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/847b86bc-395a-e602-599c-fa1b1831cdb2.png)

ではこのURLをLINEの方に入力しましょう。

完了したらテストです。
結果を見てみましょう。

![iOS の画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/299f8827-96cd-ebe3-d759-c6dec80ff9c4.png)

うまくいってますね！
私はユーロ2020全試合見ているので睡眠時間が最近やばいですw
ちなみにポルトガル推しです。

本題に戻ります。
最後にCloudWatchでデバッグがどうなっているのか見ていきましょう。

![スクリーンショット 2021-06-26 18.41.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/32211bca-1c93-0877-744e-adf4328b8a1e.png)

このログストリーム内にデバッグの内容が格納されています。

![スクリーンショット 2021-06-26 18.42.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/71140da6-2236-30e9-f3ae-8875ca275837.png)

デバッグできてますね！

![スクリーンショット 2021-06-26 18.43.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/69335229-ac6c-f89b-45ed-2ebdee5fa35c.png)

これで完了です。
ちなみに、LambdaはEC2などと違って、起動時間ではなくリクエストが課金条件なのでわざわざ削除する必要もありません。

## 最後に
次はDynamoDBを使う構成のアプリを作成していきます。
ちょっとレベルアップしてます。

https://qiita.com/Ryo9597/items/9f4c76791ec238923387