AWS コンソールと CDK による翻訳 Web API 構築の比較

## はじめに
このガイドでは、AWS Lambda と API Gateway を使用した翻訳 Web API の構築方法を、AWS マネジメントコンソールでの手動設定と AWS CDK を使用したコードによる設定の両方から解説します。両方のアプローチを比較することで、それぞれの対応関係を理解できます。

AWS CDK (Cloud Development Kit) を使用する最大の利点は、インフラストラクチャをコードとして管理できることです。これにより、設定変更の履歴が Git などのバージョン管理システムで追跡でき、開発環境からステージング環境、本番環境まで一貫した設定を維持できます。また、コードレビューやテストを通じて、インフラの変更を安全に管理することも可能になります。

## AWS CDK の準備と初期設定
### 前提条件
CDK を使用するには、以下のツールが必要です：

- Node.js (バージョン 14.x 以上)
- AWS CLI (設定済み)
- AWS アカウントの認証情報

### AWS CDK のインストール
AWS CDK は npm を使用してグローバルにインストールします：

```bash 
npm install -g aws-cdk
```
インストールが完了したら、バージョンを確認します：
```bash 
cdk --version
```

### CDK プロジェクトの初期化
新しいディレクトリを作成し、CDK プロジェクトを初期化します：

```bash 
# 新しいディレクトリを作成
mkdir translate-api-cdk
cd translate-api-cdk

# JavaScript を使用した CDK プロジェクトを初期化
cdk init app --language javascript
```

これにより、基本的な CDK プロジェクト構造が作成されます：
```
translate-api-cdk/
  ├── bin/                  # CDK アプリのエントリーポイント
  │   └── translate-api-cdk.js
  ├── lib/                  # CDK スタック定義
  │   └── translate-api-cdk-stack.js
  ├── test/
  ├── cdk.json              # CDK の設定ファイル
  ├── package.json          # プロジェクトの依存関係定義
  └── package-lock.json
```

### AWS 環境の準備（ブートストラップ）
CDK を使用するには、AWS 環境を「ブートストラップ」する必要があります。これは CDK がデプロイに使用するリソース（S3 バケットなど）を作成するプロセスです：

```bash 
cdk bootstrap
```
この操作は AWS アカウントとリージョンごとに一度だけ実行する必要があります。

### AWS コンソールでの対応操作
AWS コンソールには CDK プロジェクトの初期化に直接対応する操作はありません。CDK はローカル開発環境でインフラストラクチャをコードとして定義し、それを AWS CloudFormation を通じてデプロイするツールです。

コンソールでは、CloudFormation スタックとして CDK のデプロイ結果を確認できます：

1. AWS マネジメントコンソールにログイン
2. CloudFormation サービスに移動
3. スタック一覧で「CDKToolkit」というスタックが表示されていれば、ブートストラップは成功しています

## Lambda 関数の作成と設定
### AWS コンソールでの設定
AWS マネジメントコンソールでは、Lambda 関数を作成するために以下の手順を実行します：

1. Lambda コンソールにアクセスし、「関数の作成」ボタンをクリック
2. 「一から作成」を選択
3. 関数名に「TranslateFunction」を入力
4. ランタイムとして「Node.js 18.x」を選択
5. 実行ロールとして「基本的な Lambda アクセス権限で新しいロールを作成」を選択
6. 「関数の作成」ボタンをクリック

作成後、Lambda 関数の設定画面で以下の項目を設定します：

- 「設定」タブから「一般設定」を選択し、メモリを 256MB に、タイムアウトを 10 秒に変更
- 「アクセス権限」タブで実行ロールを選択し、「ポリシーをアタッチ」から「TranslateFullAccess」ポリシーを追加

### CDK での設定
CDK プロジェクトでは、必要なモジュールをインストールします：

```bash 
npm install aws-cdk-lib constructs
```

次に、`lib/translate-api-cdk-stack.js` ファイルを編集して Lambda 関数を定義します：

```javascript 
const { Stack, Duration } = require('aws-cdk-lib');
const lambda = require('aws-cdk-lib/aws-lambda');
const iam = require('aws-cdk-lib/aws-iam');
const path = require('path');

class TranslateApiCdkStack extends Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    // Lambda 関数の IAM ロールを作成
    const lambdaRole = new iam.Role(this, 'TranslateLambdaRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
        iam.ManagedPolicy.fromAwsManagedPolicyName('TranslateFullAccess')
      ]
    });

    // Lambda 関数を作成
    const translateFunction = new lambda.Function(this, 'TranslateFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'translate.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
      memorySize: 256,
      timeout: Duration.seconds(10),
      role: lambdaRole
    });
  }
}

module.exports = { TranslateApiCdkStack }
```

### 対応関係の解説
コンソールと CDK の対応関係を見ていきましょう：

- 関数名:
    - コンソール: 直接「TranslateFunction」と入力
    - CDK: `'TranslateFunction'` という論理 ID を指定
    - 実際の AWS リソース名: `TranslateApiCdkStack-TranslateFunction-A1B2C3D4E5F6`` のように、CDK スタック名とランダムな識別子が付加される

例えば、CDK で `'TranslateFunction'` という ID を指定した場合、AWS コンソールでは `TranslateApiCdkStack-TranslateFunction-A1B2C3D4E5F6` のような名前で表示されます。これは CDK が一意のリソース名を生成するためです。

- ランタイム: コンソールでは「Node.js 18.x」を選択、CDK では `lambda.Runtime.NODEJS_18_X` を指定
- 実行ロール: コンソールでは自動作成後に権限を追加、CDK では iam.Role を作成して `managedPolicies` に権限を指定
- メモリ: コンソールでは設定画面で 256MB を選択、CDK では `memorySize: 256` を指定
- タイムアウト: コンソールでは設定画面で 10 秒を選択、CDK では `timeout: Duration.seconds(10)` を指定
- コード: コンソールではエディタで直接入力またはアップロード、CDK では code: `lambda.Code.fromAsset()` でローカルディレクトリを指定


# Lambda 関数のコードと依存関係管理
Lambda 関数のコードは、実際に実行される処理を定義する部分です。今回は日本語テキストを英語に翻訳する機能を実装しています。

## AWS コンソールでの設定
AWS コンソールでは、Lambda 関数のコードエディタに直接コードを入力するか、ZIP ファイルとしてアップロードします。コードエディタで以下のようなコードを入力します：

```javascript 
const AWS = require('aws-sdk');
const translate = new AWS.Translate();

exports.handler = async (event) => {
  console.log('イベント:', JSON.stringify(event));
  
  try {
    // クエリパラメータから入力テキストを取得
    const inputText = event.queryStringParameters?.inputText || 'こんにちは';
    
    const params = {
      Text: inputText,
      SourceLanguageCode: 'ja',
      TargetLanguageCode: 'en'
    };
    
    // Amazon Translate を呼び出し
    const response = await translate.translateText(params).promise();
    const outputText = response.TranslatedText;
    
    return {
      statusCode: 200,
      body: outputText,
      headers: {
        'Content-Type': 'text/plain'
      },
      isBase64Encoded: false
    };
  } catch (error) {
    console.error('エラー:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: error.message }),
      headers: {
        'Content-Type': 'application/json'
      },
      isBase64Encoded: false
    };
  }
};
```

Node.js 18.x ランタイムでは AWS SDK v2 が組み込まれていないため、依存関係を含めるには「レイヤー」を追加するか、依存関係を含めたデプロイパッケージをアップロードする必要があります。

### CDK での設定とディレクトリ構成
CDK プロジェクトでは、以下のようなディレクトリ構成を作成します：
```
translate-api-cdk/
  ├── bin/                  # CDK アプリのエントリーポイント
  │   └── translate-api-cdk.js
  ├── lib/                  # CDK スタック定義
  │   └── translate-api-cdk-stack.js
  ├── lambda/               # Lambda 関数のコードと依存関係
  │   ├── translate.js      # 関数のメインコード
  │   ├── package.json      # 関数の依存関係定義
  │   └── node_modules/     # 関数の依存関係（自動生成）
  ├── node_modules/         # CDK プロジェクトの依存関係（自動生成）
  ├── cdk.json              # CDK の設定ファイル
  ├── package.json          # CDK プロジェクトの依存関係定義
  └── package-lock.json     # 依存関係のバージョンロック（自動生成）
```

この構造では、CDK プロジェクト自体の依存関係と Lambda 関数の依存関係が明確に分離されています。これは AWS Lambda のデプロイモデルに合わせた標準的なアプローチです。

Lambda 関数のコードと依存関係を設定するには、以下の手順を実行します：

```bash 
# lambda ディレクトリを作成
mkdir lambda
cd lambda

# package.json を初期化
npm init -y

# AWS SDK v3 の Translate クライアントをインストール
npm install @aws-sdk/client-translate
```

そして、`lambda/translate.js` ファイルを作成し、以下のコードを記述します：

```javascript 
const { Translate } = require('@aws-sdk/client-translate');

const translate = new Translate({ region: process.env.AWS_REGION });

exports.handler = async (event) => {
  console.log('イベント:', JSON.stringify(event));
  
  try {
    // クエリパラメータから入力テキストを取得
    const inputText = event.queryStringParameters?.inputText || 'こんにちは';
    
    const params = {
      Text: inputText,
      SourceLanguageCode: 'ja',
      TargetLanguageCode: 'en'
    };
    
    // Amazon Translate を呼び出し
    const response = await translate.translateText(params);
    const outputText = response.TranslatedText;
    
    return {
      statusCode: 200,
      body: outputText,
      headers: {
        'Content-Type': 'text/plain'
      },
      isBase64Encoded: false
    };
  } catch (error) {
    console.error('エラー:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: error.message }),
      headers: {
        'Content-Type': 'application/json'
      },
      isBase64Encoded: false
    };
  }
};
```
CDK スタックでは、このコードを含むディレクトリを Lambda 関数のコードとして指定します：

```javascript 
code: lambda.Code.fromAsset(path.join(__dirname, '../lambda'))
```

### Lambda 関数のテスト
CDK でデプロイする前に、Lambda 関数が正しく動作するかを確認することができます。これには、以下の方法があります：

1. **CDK を使用して Lambda 関数のみをデプロイ**
    ```javascript 
    
    // lib/translate-api-cdk-stack.js の一部だけを実装
    const lambdaRole = new iam.Role(this, 'TranslateLambdaRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
        iam.ManagedPolicy.fromAwsManagedPolicyName('TranslateFullAccess')
      ]
    });
    
    const translateFunction = new lambda.Function(this, 'TranslateFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'translate.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
      memorySize: 256,
      timeout: Duration.seconds(10),
      role: lambdaRole
    });
    ```
    そして、npx cdk deploy を実行して Lambda 関数をデプロイします。デプロイ後、AWS コンソールから Lambda 関数をテストできます。
    <br>
2. **AWS SAM CLI を使用してローカルでテスト**
    AWS SAM CLI をインストールしている場合、ローカル環境で Lambda 関数をテストできます。まず、簡単な SAM テンプレートを作成します：
    
    ```yaml 
    # template.yaml
    AWSTemplateFormatVersion: '2010-09-09'
    Transform: AWS::Serverless-2016-10-31
    Resources:
      TranslateFunction:
        Type: AWS::Serverless::Function
        Properties:
          CodeUri: ./lambda
          Handler: translate.handler
          Runtime: nodejs18.x
          Timeout: 10
          MemorySize: 256
          Environment:
            Variables:
              AWS_REGION: ap-northeast-1
    ```
    そして、以下のコマンドでローカルテストを実行します：
    
    ```bash 
    # テストイベントを作成
    echo '{"queryStringParameters": {"inputText": "こんにちは"}}' > event.json
    
    # ローカルで Lambda 関数を実行
    sam local invoke TranslateFunction -e event.json
    ```
    ただし、この方法では AWS の認証情報が必要で、実際に AWS Translate サービスを呼び出します。

3. モックを使用したユニットテスト:
    AWS SDK をモック化して、ユニットテストを作成することもできます。これには Jest などのテストフレームワークを使用します：
    
    ```bash 
    # lambda ディレクトリで実行
    npm install --save-dev jest
    ```
    そして、translate.test.js ファイルを作成します：
    
    ```javascript 
    // translate.test.js
    const { handler } = require('./translate');
    
    // AWS SDK をモック化
    jest.mock('@aws-sdk/client-translate', () => {
      return {
        Translate: jest.fn().mockImplementation(() => {
          return {
            translateText: jest.fn().mockResolvedValue({
              TranslatedText: 'Hello'
            })
          };
        })
      };
    });
    
    test('translates text correctly', async () => {
      const event = {
        queryStringParameters: {
          inputText: 'こんにちは'
        }
      };
      
      const response = await handler(event);
      
      expect(response.statusCode).toBe(200);
      expect(response.body).toBe('Hello');
    });
    ```
    package.json にテストスクリプトを追加します：
    
    ```json 
    "scripts": {
      "test": "jest"
    }
    ```
    そして、テストを実行します：
    
    ```bash 
    npm test
    ```
    これらのテスト方法を使用することで、API Gateway との統合前に Lambda 関数が正しく動作することを確認できます。特にモックを使用したユニットテストは、実際の AWS サービスを呼び出さずに関数のロジックをテストできるため、CI/CD パイプラインでの自動テストに適しています。

### 対応関係の解説
Lambda 関数のコード管理における AWS コンソールと CDK の主な違いは以下の通りです：

- **コードの管理場所**
    - **コンソール**: AWS コンソール上のエディタで直接編集するか、ZIP ファイルをアップロード
    - **CDK**: ローカルファイルシステム上でコードを管理し、CDK デプロイ時に自動的にアップロード
    <br>
- **依存関係の管理**
    - **コンソール**: 依存関係を含む ZIP ファイルをアップロードするか、Lambda レイヤーを使用
    - **CDK**: node_modules ディレクトリを含むローカルディレクトリ全体をデプロイパッケージとして使用
    <br>
- **AWS SDK のバージョン**
    - **コンソール**: 古いランタイム（Node.js 14.x 以前）では AWS SDK v2 が組み込まれている
    - **CDK**: 明示的に依存関係をインストールし、AWS SDK v3 の最新機能を活用可能

CDK を使用することで、コードと依存関係をローカル環境で管理し、バージョン管理システムで追跡できるため、チーム開発や継続的インテグレーション/継続的デプロイ（CI/CD）パイプラインとの統合が容易になります。



## API Gateway の作成と設定
API Gateway は、クライアントからのリクエストを受け付け、Lambda 関数に転送するためのエンドポイントを提供します。ここでは、API Gateway の作成と設定方法を AWS コンソールと CDK の両方で説明します。

### AWS コンソールでの設定
AWS マネジメントコンソールで API Gateway を作成する手順は以下の通りです：

1. API Gateway コンソールにアクセスし、「API を作成」ボタンをクリック
2. 「REST API」を選択し、「構築」ボタンをクリック
3. 「新しい API を作成」を選択
4. API 名に「TranslateAPI」を入力
5. エンドポイントタイプは「リージョン」を選択
6. 「API の作成」ボタンをクリック

API が作成されたら、リソースとメソッドを追加します：

1. 「リソース」タブで、ルートリソース「/」を選択
2. 「アクション」ボタンをクリックし、「リソースの作成」を選択
3. リソース名に「translate」を入力
4. 「リソースの作成」ボタンをクリック
5. 作成した「/translate」リソースを選択
6. 「アクション」ボタンをクリックし、「メソッドの作成」を選択
7. ドロップダウンから「GET」を選択し、チェックマークをクリック
8. 統合タイプとして「Lambda 関数」を選択
9. 「Lambda プロキシ統合の使用」にチェックを入れる
10. Lambda 関数に「TranslateFunction」を入力
11. 「保存」ボタンをクリック
12. Lambda 関数に権限を追加することを確認するポップアップが表示されたら「OK」をクリック

次に、クエリパラメータの設定を行います：

1. 「メソッドリクエスト」をクリック
2. 「URL クエリ文字列パラメータ」を展開
3. 「クエリ文字列の追加」をクリック
4. 名前に「inputText」と入力
5. 「必須」にチェックを入れる
6. チェックマークをクリックして保存

最後に、API をデプロイします：

1. 「アクション」ボタンをクリックし、「API のデプロイ」を選択
2. 「デプロイのステージ」で「[新しいステージ]」を選択
3. ステージ名に「dev」と入力
4. 「デプロイ」ボタンをクリック

デプロイが完了すると、ステージエディタが表示され、API のエンドポイント URL が表示されます。この URL に「?inputText=こんにちは」を追加してブラウザでアクセスすると、翻訳結果が表示されます。

### CDK での設定
CDK では、API Gateway の設定を以下のようにコードで表現します。まず、必要なモジュールをインストールします：

```bash 
npm install aws-cdk-lib/aws-apigateway
```
次に、`lib/translate-api-cdk-stack.js` ファイルに API Gateway の定義を追加します：

```javascript 
const { Stack, Duration, CfnOutput } = require('aws-cdk-lib');
const lambda = require('aws-cdk-lib/aws-lambda');
const apigateway = require('aws-cdk-lib/aws-apigateway');
const iam = require('aws-cdk-lib/aws-iam');
const path = require('path');

class TranslateApiCdkStack extends Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    // Lambda 関数の IAM ロールを作成
    const lambdaRole = new iam.Role(this, 'TranslateLambdaRole', {
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
        iam.ManagedPolicy.fromAwsManagedPolicyName('TranslateFullAccess')
      ]
    });

    // Lambda 関数を作成
    const translateFunction = new lambda.Function(this, 'TranslateFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'translate.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda')),
      memorySize: 256,
      timeout: Duration.seconds(10),
      role: lambdaRole
    });

    // API Gateway を作成
    const api = new apigateway.RestApi(this, 'TranslateApi', {
      restApiName: 'Translate API',
      description: 'API for translating text from Japanese to English'
    });

    // /translate リソースを作成
    const translateResource = api.root.addResource('translate');

    // GET メソッドを追加し、Lambda 関数と統合
    translateResource.addMethod('GET', new apigateway.LambdaIntegration(translateFunction, {
      proxy: true
    }), {
      requestParameters: {
        'method.request.querystring.inputText': true
      }
    });

    // API Gateway のエンドポイント URL を出力
    const apiUrl = `${api.url}translate?inputText=${encodeURIComponent('こんにちは')}`;
    
    // CDK 出力に API URL を追加
    new CfnOutput(this, 'TranslateApiUrl', {
      value: apiUrl,
      description: 'URL for the Translate API (example with "こんにちは")'
    });
  }
}

module.exports = { TranslateApiCdkStack }
```
このコードを保存した後、CDK アプリをデプロイします：

```bash 
npx cdk deploy
```
デプロイが完了すると、コンソールに API Gateway のエンドポイント URL が表示されます。この URL にアクセスして API をテストできます。

### 対応関係の解説
API Gateway の設定における AWS コンソールと CDK の対応関係は以下の通りです：

- **API の作成**
    - コンソール: 「API を作成」ボタンをクリックし、名前とエンドポイントタイプを設定
    - CDK: `new apigateway.RestApi()` コンストラクタで API を作成し、`restApiName` と `description` を設定
    <br>
- **リソースの作成**
    - コンソール: 「リソースの作成」を選択し、リソース名を入力
    - CDK: `api.root.addResource('translate')` でリソースを作成
    <br>
- **メソッドの作成**
    - コンソール: 「メソッドの作成」を選択し、HTTP メソッドと統合タイプを設定
    - CDK: `translateResource.addMethod('GET', new apigateway.LambdaIntegration())` でメソッドを作成
    <br>
- **Lambda プロキシ統合**
    - コンソール: 「Lambda プロキシ統合の使用」にチェックを入れる
    - CDK: `new apigateway.LambdaIntegration(translateFunction, { proxy: true })` で設定
    <br>
- **クエリパラメータの設定**
    - コンソール: 「メソッドリクエスト」から「URL クエリ文字列パラメータ」を設定
    - CDK: `requestParameters: { 'method.request.querystring.inputText': true }` で設定
    <br>
- **API のデプロイ**
    - コンソール: 「API のデプロイ」を選択し、ステージを設定
    - CDK: CDK アプリのデプロイ時に自動的に API がデプロイされる
    <br>
- **エンドポイント URL の取得**
    - コンソール: ステージエディタで URL を確認
    - CDK: `api.url` プロパティで URL を取得し、`CfnOutput` で出力
    <br>
CDK を使用すると、API Gateway の設定をコードとして管理できるため、設定の再現性が高まり、環境間の一貫性を保つことができます。また、リソースやメソッドの追加・変更・削除も簡単に行えます。

### API のテストと動作確認
API がデプロイされたら、実際に動作するかテストしましょう。

#### ブラウザでのテスト
最も簡単なテスト方法は、ブラウザで API のエンドポイント URL にアクセスすることです：
```
https://xxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/translate?inputText=こんにちは
```
正しく設定されていれば、ブラウザに「Hello」と表示されるはずです。

#### curl を使用したテスト
コマンドラインから API をテストする場合は、curl コマンドを使用できます：

```bash 
curl "https://xxxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/translate?inputText=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF"
```
注意: URL 内の日本語は URL エンコードする必要があります。

#### Postman を使用したテスト
より高度なテストには、Postman などの API テストツールを使用できます：

1. Postman を起動
2. 新しいリクエストを作成
3. HTTP メソッドを「GET」に設定
4. URL に API のエンドポイント URL を入力
5. 「Params」タブで「inputText」キーに「こんにちは」値を設定
6. 「Send」ボタンをクリック

レスポンスとして「Hello」が返ってくれば、API は正しく機能しています。

### CloudWatch Logs の確認
API の動作に問題がある場合は、CloudWatch Logs で Lambda 関数のログを確認できます：

1. AWS マネジメントコンソールで CloudWatch サービスに移動
2. 左側のナビゲーションペインで「ログ」→「ロググループ」を選択
3. /aws/lambda/TranslateApiCdkStack-TranslateFunction-XXXX という名前のロググループを探す
4. 最新のログストリームをクリックしてログを確認

ログには、Lambda 関数の実行状況やエラーメッセージが記録されています。

## まとめ
このガイドでは、AWS Lambda と API Gateway を使用した翻訳 Web API の構築方法を、AWS マネジメントコンソールでの手動設定と AWS CDK を使用したコードによる設定の両方から解説しました。