Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

AWSサーバーレスを活用したAngularのWebアプリを作ってみた

こんにちは!エンジニア2年目、フロントエンドを勉強中のmiuraです。

この記事はAmazon Web Services Advent Calendar 2017の13日目の記事となります。

qiita.com

私は最近、Angularを使ったフロントエンド開発やAWSでのサーバーレス開発をしているのですが、
サーバーレスのWebアプリを構築するとなった際につまった部分がありました。

それは、
WebアプリをS3に、サーバーレスをAPI GatewayAWS Lambdaで構成すると双方のURLが違うため、
ブラウザではSame-Origin Policy (SOP)が適用されてエラーになってしまうことです。
これを回避するためにはCross-Origin Resource Sharing (CORS)の設定を行う必要があります。

今回は、そのCORSの設定を含めてAWS上で一通りの動きができるWebアプリを作っていきます。

全体構成

全体構成は以下の図のようなシンプルな構成を作ります。

f:id:acro-engineer:20171213020726p:plain:w500

サーバーレス

S3にWebアプリをアップロードし、静的ウェブホスティングを設定してWebアプリにアクセス出来るようにします。
そして、API Gatewayを通してAWS Lambdaから"Hello, World!"というメッセージを取得出来るようにします。

Angularを使ったWebアプリ

今回はサンプルとして、Angularの公式ページにあるQuick Startを引用します。

https://angular.io/guide/quickstart

このQuick Startはタイトルにある"Welcome to app!"の"app"部分がAngularによってinjectしているサンプルになっています。
そこで今回、この"app"の部分をAWS Lambdaから取得した"Hello, World!"に変えていきたいと思います。

ツール/ライブラリ

ツールとライブラリは以下のものを使います。

名前 バージョン
Node.js v6.5以上
Angular 5.0.0
AWS CLI 1.11.100
Serverless Framework 1.20.2
Python 3.6


それでは早速作っていきましょう。


サーバーレス

Serverless Frameworkのプロジェクト生成

Serverless FrameworkとはをYAMLファイルに簡単な記述を行えば、コマンド1つでAWS環境を構築できるツールです。
AWS CLIのセットアップが必要ですが、いろんなサイトで紹介されているので今回は割愛します。

以下のコマンドを実行すれば、簡単にPython3系用のサンプルプロジェクトが生成されます。

npm install -g serverless
serverless create --template aws-python3 --path my-serverless

AWS Lambda用ソースコードの作成

では、Serverless Frameworkで生成されたhandler.pyを編集して、
通信先としてのレスポンスを返すAWS Lambdaのソースコードを書いていきます。

このときに、注意すべきは冒頭でも書いたCORSの設定です。

CORSとは、データのアクセスを許可できるWebサイトに対してOriginを越えたアクセスを可能にするための仕組みで、
HTTPレスポンスヘッダに"Access-Control-Allow-Origin"として信頼するWebサイトのOriginを付与することでブラウザ側でアクセスが許可されます。

qiita.com

今回許可するWebサイトのOriginは後述するWebアプリを設置するS3のエンドポイントになります。

my-serverless/handler.py

import json

def hello(event, context):
    body = {
        "message": "Hello World!"
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body),
        "headers": {
            "Access-Control-Allow-Origin": "Webアプリを置いたS3のエンドポイント"  # CORS設定
        }
    }

    return response

API Gatewayの設定

次にHTTP通信の受け口であるAPI Gatewayの設定をします。

my-serverless/serverless.yml

service: my-serverless

provider:
  name: aws
  runtime: python3.6
  region: us-west-2
  apiKeys:             # APIキーを生成
    - api-key

functions:
  hello:
    handler: handler.hello
    events:
     - http:           # API Gatewayの設定
        path: hello
        method: get
        private: true  # APIキーを使用する
        cors: true     # API GatewayにCORSの設定をする

デプロイ

AWS Lambda, API Gatewayの設定ができたので、AWSにデプロイを行います。

cd my-serverless
sls deploy

上記のコマンドを実行するだけでAWSにデプロイが完了します。
AWSコンソールでAPI Gatewayを見ると、作成したAWS Lambda関数と紐付けて設定されていることが分かります。
またGETメソッドの他にOPTIONSが設定され、ここにCORSの設定が反映されます。

f:id:acro-engineer:20171213125802p:plain:w800


Angularを使ったWebアプリ

Angularプロジェクトの生成

以下のコマンドを実行して、Angularのサンプルプロジェクトを生成します。

npm i -g @angular/cli
ng new my-app

HTTP通信用サービスの作成

HTTP通信を行うためのAngularのサービスを作成します。
HTTP通信用のクラスとしてHttpClient, HttpHeadersをインポートして使用します。

my-app/src/app/app.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable()
export class AppService {
  constructor(private http: HttpClient) { }

  getMessage(callback: (msg: string) => void): void {
    // API GatewayとHTTP通信して、取得成功時にコールバックを行なう
    this.http.get('API GatewayのURL', {
      headers: new HttpHeaders().set('x-api-key', 'APIキー')
    }).subscribe(data => {
      callback(data['message']);
    });
  }
}

次にWebアプリのモジュールにサービスで使ったHttpClient, HttpHeadersのモジュールであるHttpClientModuleをインポートします。
また、作成したサービスAppService も登録します。

my-app/src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { AppService } from './app.service';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule        // HTTP通信モジュールをインポート
  ],
  providers: [AppService],  // 作成したサービスを登録 
  bootstrap: [AppComponent]
})
export class AppModule { }

そして、ページ表示の内容や動作を定義しているコンポーネントに作成したサービスを呼び出す処理と、
HTTP通信成功時の処理を記述します。

my-app/src/app/app.component.ts

import { Component } from '@angular/core';

import { AppService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [AppService]
})
export class AppComponent {
  constructor(private appService: AppService) { }

  title = 'app';

  ngOnInit(): void {
    this.appService.getMessage(msg => {
      this.title = msg;  // HTTP通信成功時にタイトルに取得したメッセージを表示する
    })
  }
}

ビルド

以下のコマンドを実行すると本番環境用のビルドが行われます。
ビルド結果はmy-app/distフォルダに出力されます。

cd my-app
ng build --prod

S3の静的ウェブホスティング

AWSコンソールでS3を開き、バケットを作成します。
※今回はmy-web-applicationという名前で作成しています。

f:id:acro-engineer:20171213044453j:plain:w500

そして、その作成したS3バケットの中にビルド結果のmy-app/distフォルダ内のファイルをアップロードします。

f:id:acro-engineer:20171213051253j:plain:w500

次にバケットポリシーをブラウザから直接S3バケットの中のオブジェクトを取得出来るように設定します。
バケットのアクセス権限タブ内にあるバケットポリシーに以下を設定します。
Resourceには作成したバケットのARNを指定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-web-application/*"
        }
    ]
}

最後にバケットのプロパティタブにあるStatic website hostingを選択します。
「このバケットを使用してウェブサイトをホストする」をチェックし、
インデックスドキュメントにindex.htmlを入力して保存します。

f:id:acro-engineer:20171213044912j:plain:w500

これにより、静的ウェブホスティングの設定画面の上部に表示されているエンドポイントにアクセスすると、
S3に保存されたWebアプリが表示されました。
また、表示される際にHTTP通信をして"Hello World!"が取得され、タイトルに表示されました。

f:id:acro-engineer:20171213053847j:plain


まとめ

今回、ServerlessFrameworkやAngularを使ったサーバーレスのWebアプリの作成をしました。
その際、S3にあるWebアプリからAPI Gatewayを通してデータの受け渡しを行なうために、
サーバーレス側にCORS設定方法を紹介しました。

試しにサーバーレスの構成でWebアプリを作ってみたい人はぜひ参考にしてみてください。
それでは。

Acroquest Technologyでは、キャリア採用を行っています。


  • ビッグデータHadoop/Spark、NoSQL)、データ分析(Elasticsearch、Python関連)、Web開発(SpringCloud/SpringBoot、AngularJS)といった最新のOSSを利用する開発プロジェクトに関わりたい。
  • マイクロサービスDevOpsなどの技術を使ったり、データ分析機械学習などのスキルを活かしたい。
  • 社会貢献性の高いプロジェクトや、顧客の価値を創造するようなプロジェクトで、提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や、対外的な勉強会の開催・参加を通した技術の発信、社内勉強会での技術情報共有により、エンジニアとして成長したい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

【データ分析】
データ分析案件で時系列データの異常検知に挑戦したいエンジニアWanted! - Acroquest Technology株式会社のエンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com