【AWS Lambda×LINE Messaging API】翻訳アプリを作ろう

## はじめに
皆さん、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)

ということで、この3つ全てを使った翻訳アプリを作りたいと思います。

## アーキテクチャ
以下の2つの条件を満たしたら成功です。

①LINEで「こんにちは」と入力したら、「Hello」と返ってくる
②タイムスタンプと「こんにちは」、「Hello」がDBに保存される

![arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/ac43c94e-ceee-5f9c-699d-6528417fcad6.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に書いていることをやっていきます。

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 aws-sdk --save
```

#### 翻訳部分を作る
今回は、翻訳する部分、DBにデータを登録する部分と様々な機能があるため動作ごとにファイルを切り分けてあげましょう。
以下のように作っていきます。

```
.
├── api/
│   ├── Common/
│   │        └── getTranslate.js
│   │        └── putDynamoDB.js
│   └── index.js
```

まずは、`index.js`が作っていきます。

```javascript: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);

// モジュールのインストール
const translate = require('./Common/getTranslate');

exports.handler = async (event, context) => {
  // JSONとして解析して値やオブジェクトを構築する
  const body = JSON.parse(event.body);

  // LINE Eventを取得
  const response = body.events[0];

  // 翻訳を行うために必要な情報
  const input_text = response.message.text;
  const sourceLang = 'ja';
  const targetLang = 'en';

  // 翻訳（関数呼び出し）
  const res = await translate.getTranslate(input_text, sourceLang, targetLang);
  const output_text = res.TranslatedText;

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

  try {
    // メッセージの送信
    await client.replyMessage(replyToken, post);
  } catch (err) {
    console.log(err);
  }
};
```

では次に、`getTranslate.js`を作っていきましょう。
コードだけ書いても訳がわからないと思うので、リファレンスを見ましょう。

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Translate.html#translateText-property

入力されたテキストをソース言語からターゲット言語に変換する、`translateText`を使います。
必須項目は以下の3つで、`SourceLanguageCode`に元の言語コード、`TargetLanguageCode`に変換先の言語コード、`Text`に変換するテキストを入れればいいことがわかります。

```
SourceLanguageCode: 'STRING_VALUE', /* required */
TargetLanguageCode: 'STRING_VALUE', /* required */
Text: 'STRING_VALUE', /* required */
```

そのあとはこのデータを実行するだけです。

```
translate.translateText(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});
```

APIが理解できたところで進めていきましょう。

```javascript:Common/getTranslate.js
// パッケージのインストール
const AWS = require('aws-sdk');

// 必要なAWSサービス
const translate = new AWS.Translate();

exports.getTranslate = (input, inLang, outLang) => {
  return new Promise((resolve, reject) => {
    // 必要なデータ
    const params = {
      Text: input,
      SourceLanguageCode: inLang,
      TargetLanguageCode: outLang,
    };

    // 翻訳を行う
    translate.translateText(params, (err, data) => {
      if (err) {
        console.log(err);
        reject();
      } else {
        resolve(data);
      }
    });
  });
};
```

### 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 21.21.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/a5bb7930-75d2-fb4e-99da-f989ec33df2a.png)

作成完了しました！

![スクリーンショット 2021-06-26 21.23.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/2a1c4502-9e4a-fdf1-8850-a07fbfc6455b.png)

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

zipの方が簡単なので今回はzipにします。
今回ファイルサイズだいぶ限界になるので10MBオーバーしたときはS3にアップロードしてください。
ちなみに`aws-sdk`をインストールしましたが、Lambdaに標準装備しているパッケージなのでこちらをアンストして容量下げてもらっても構いません。

`node_module`と`Commonディレクトリ`と`index.js`の3つをzipにしてください。
命名は適当でいいです。

![スクリーンショット 2021-06-26 21.27.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/96b17fba-58e0-718a-6878-eabf65c0d1df.png)

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

![スクリーンショット 2021-06-26 21.27.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/b985eb6c-d006-fa36-b67a-02253f72ec3f.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)

では最後に、実行ロールに`AWS Translate`の権限を付与しましょう。

![スクリーンショット 2021-06-26 21.32.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/2901acef-d452-bd46-f9ee-37afd1395d55.png)

ポリシーをアタッチさせましょう。

![スクリーンショット 2021-06-26 21.33.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/99c3d079-fe6d-717f-37db-872d82e04076.png)

![スクリーンショット 2021-06-26 21.34.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/b917f6f7-e3e5-e85e-918a-6bd6a0a9d779.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 21.35.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/41780525-7d67-feba-7aca-803dd2abeef8.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 21.37.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/3f1dd5ea-9995-1bca-4806-dbe87a1d5ecd.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 の画像 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/eb1dc183-5724-ca4f-30b8-8554386abf4b.png)

うまくいってますね！

### Lambdaで使うコードを作る
#### DBにデータを登録する機能を作る

```
.
├── api/
│   ├── Common/
│   │        └── getTranslate.js
│   │        └── putDynamoDB.js
│   └── index.js
```

最初に、`index.js`から作っていきます

```javascript: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);

// モジュールのインストール
const translate = require('./Common/getTranslate');
const dynamodb = require('./Common/putDynamoDB');

exports.handler = async (event, context) => {
  // JSONとして解析して値やオブジェクトを構築する
  const body = JSON.parse(event.body);

  // LINE Eventを取得
  const response = body.events[0];

  // 翻訳を行う
  const input_text = response.message.text;
  const sourceLang = 'ja';
  const targetLang = 'en';

  // 翻訳（関数呼び出し）
  const res = await translate.getTranslate(input_text, sourceLang, targetLang);
  const output_text = res.TranslatedText;

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

  // DB-タイムスタンプ
  const date = new Date();
  const Y = date.getFullYear();
  const M = ('00' + (date.getMonth() + 1)).slice(-2);
  const D = ('00' + date.getDate()).slice(-2);
  const h = ('00' + (date.getHours() + 9)).slice(-2);
  const m = ('00' + date.getMinutes()).slice(-2);
  const s = ('00' + date.getSeconds()).slice(-2);
  const dayTime = Y + M + D + h + m + s;

  try {
    // メッセージの送信
    await client.replyMessage(replyToken, post);
    // DB保存（関数呼び出し）
    await dynamodb.putDynamoDB(dayTime, input_text, output_text);
  } catch (err) {
    console.log(err);
  }
};
```

次に、`putDynamoDB.js`を作ります。
コードだけ書いても訳がわからないと思うので、リファレンスを見ましょう。

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#putItem-property

アイテム(レコード)を作成したいので、`putItem`を使います。
必須項目は以下の3つで、`Item`にデータ、`ReturnConsumedCapacity`に集計、`TableName`にテーブルの名前を入れればいいことがわかります。

```
var params = {
  Item: {
   "AlbumTitle": {
     S: "Somewhat Famous"
    }, 
   "Artist": {
     S: "No One You Know"
    }, 
   "SongTitle": {
     S: "Call Me Today"
    }
  }, 
  ReturnConsumedCapacity: "TOTAL", 
  TableName: "Music"
 };
```

そのあとはこのデータを実行するだけです。

```
 dynamodb.putItem(params, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else     console.log(data);           // successful response
 });
```

APIが理解できたところで進めていきましょう。


```javascript:Common/putDynamoDB.js
// パッケージのインストール
const AWS = require('aws-sdk');

// 必要なAWSサービス
const dynamodb = new AWS.DynamoDB();

exports.putDynamoDB = (dayTime, input, output) => {
  return new Promise((resolve, reject) => {
    const params = {
      Item: {
        timestamp: {
          S: dayTime,
        },
        input_text: {
          S: input,
        },
        output_text: {
          S: output,
        },
      },
      ReturnConsumedCapacity: 'TOTAL',
      TableName: 'translations_history',
    };

    dynamodb.putItem(params, (err, data) => {
      if (err) {
        console.log(err);
        reject();
      } else {
        resolve();
      }
    });
  });
};
```

では最後に、実行ロールに`AWS DynamoDB`の権限を付与しましょう。

![スクリーンショット 2021-06-26 21.32.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/2901acef-d452-bd46-f9ee-37afd1395d55.png)

ポリシーをアタッチさせましょう。

![スクリーンショット 2021-06-26 21.33.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/99c3d079-fe6d-717f-37db-872d82e04076.png)

![スクリーンショット 2021-06-26 21.54.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/29a42aa4-8a36-d2b1-7ece-07f89306e2a7.png)

これでアタッチできました。

### DynamoDBを作成する

![スクリーンショット 2021-06-26 21.57.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/1616adc7-8657-706f-1c4b-c24ef83c989b.png)

設定はこのようにします。

![スクリーンショット 2021-06-26 21.58.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/d18747c3-276c-ba91-fe86-992e6b19d51c.png)

これで完了です。

最後に正しく動作するか確認していきましょう。

翻訳は正しくできていますね！

![iOS の画像 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/acf43f56-7bb3-c0e9-3541-71d63442c4ea.png)

では次に、DBにデータが保存されているか確認しましょう。

![スクリーンショット 2021-06-26 22.08.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/7b07e32c-d9cd-d8fd-877d-96c21ace2f67.png)

保存されてますね！
時刻も問題なく反映されています。

これで全て終了です。

## 最後に
少し難しかった等あれば、DynamoDBを使わないただのおうむ返しアプリなども作っているのでこちらも参考にされてください。

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

次は以前作成した天気予報アプリなどをLambdaでデプロイしていきます。

最後まで読んでいただきありがとうございます。