4
2

More than 5 years have passed since last update.

GAE と Angular で普通の SPA の作り方 〜 導入編

Last updated at Posted at 2018-02-23

はじめに

  • Google App Engine をバックエンドサーバーとして動く普通の Angular アプリケーションの作り方を紹介していく。
  • 今回は導入編としてアプリケーションのひな形を作って通信ができるだけのところまでやってみる。

準備

  • Go, Node.js のインストール

  • Google Cloud SDK のインストール

    • GAE のローカル開発サーバーなどが入っているので公式ドキュメントの通りにインストールする
  • Angular CLI のインストール

    • Angular CLI を使って Angular プロジェクトの初期化や管理ができるのでREADME.mdに従ってインストールする

プロジェクトルートの作成

  • $GOPATH の通っているディレクトリにプロジェクトルートを作成する
  • 以下は github.com で公開し、ユーザー名が you で、プロジェクト名が gae-angular-sample の場合のプロジェクトルートディレクトリの作成
mkdir -p $GOPATH/src/github.com/you/gae-angular-sample/{server,client}

GAE アプリケーションのひな形作成

  • GAE アプリのひな形としては、HTTP リクエストを処理するメインの go ファイルと、設定用の yaml が必要になる

  • メインの Go ファイルとして "/" へのリクエストで "hello world" と出力するだけの HTTP ハンドラの定義された server/gae/app.go を作成する

server/gae/app.go
package main

import (
    "net/http"
    "fmt"
)

func init() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "hello world")
    })
}
  • 設定用の Yaml として全リクエストを前記の Go ファイルで処理するように定義した server/gae/app.yaml を作成する
server/gae/app.yaml
runtime: go
api_version: go1.8

handlers:
- url: /.*
  script: _go_app

開発サーバーを起動する

  • Cloud SDK に入っている dev_appserver.py コマンドで開発サーバーを立ち上げる
cd server/gae
dev_appserver.py app.yaml
  • 起動ログに表示される通り http://localhost:8080 で立ち上げたアプリケーションへアクセスできる
  • これも起動ログにあるが http://localhost:8000 で管理用のサーバーにアクセスすることができ、開発用にエミュレートされた Google Datastore などを操作することができる

Angular アプリケーションのひな形の作成

  • Angular CLI を使ってひな形を作成する
# プロジェクトルートヘ
cd $PROJECT_ROOT
# client という名前でひな形の作成
ng new client
# 依存パッケージのインストール
cd client && npm install

Angular アプリの開発サーバーを起動する

npm start
  • http://localhost:4200 でクライアントの開発サーバーへアクセス

GAE アプリと Angular アプリを同一ホストで提供する

  • GAE と Angular でそれぞれ開発サーバーを立ち上げると、それぞれ別ドメインとして動作してしまい Cookie などを共有することができずに面倒な場合がある
  • ここでは Angular アプリのソースをビルドして GAE アプリで提供することで同一ドメインで動作させる

Angular アプリのビルド

  • 下記の通り client/package.json に GAE アプリ用のビルドスクリプトを追加する
client/package.json
{
  ...
  "scripts": {
    ...
    "build:gae": "ng build --prod --output-path=../server/gae/client/ --base-href=/client/"
  • ビルドスクリプトを実行して server/gae/client にビルド後のソースがコピーされていることを確認する
npm run build:gae

GAE アプリで Angular アプリのファイルを提供する

  • GAE アプリの設定ファイルを変更してビルドした Angular アプリのファイルを静的ファイルとして提供する
  • 以下のように "handlers" 要素に "/client/" 以下の静的ファイルを提供する設定と、それ以外の "/client/.*" で "/client/index.html" を提供する設定を追加する
server/gae/app.yaml
runtime: go
api_version: go1.8

handlers:
- url: /client/(.*\.(gif|png|jpeg|jpg|css|js|ico))$
  static_files: client/\1
  upload: client/(.*)
- url: /client/(.*)
  static_files: client/index.html
  upload: client/index.html
- url: /.*
  script: _go_app
  • GAE 開発サーバーを起動し http://localhost:8080/client/ で Angular アプリが立ち上がるのを確認する

自動でリビルドする

  • ビルドコマンドにオプションを足すとファイル変更を検知してリビルドしてくれる
npm run build:gae -- --watch

Angular アプリから GAE アプリへ通信する

GAE アプリに通信の受け口を追加する

  • パラメータを受けて動的な結果を返す HTTP ハンドラを定義する。
  • パラメータの bind と JSON のレスポンスなどウェブサーバーではおなじみの処理を手軽に実装したいのでウェブアプリケーションフレームワーク echo を利用する
server/gae/app.go
package main

import (
    "github.com/labstack/echo"
    "net/http"
)

func init() {
    // 軽量なウェブアプリケーションフレームワーク echo を使う
    // 素の net/http に比べてパラメータの bind や Json の出力を便利になる
    e := echo.New()

    // ルート定義
    e.GET("/hello", helloHandler)

    // 全リクエストを echo で処理する
    http.Handle("/", e)
}

// helloHandler のリクエスト
type helloRequest struct {
    // クエリストリングの name 要素を受け取る
    Name string `query:"name"`
}

// helloHandler のレスポンス
type helloResponse struct {
    Message string
}

// echo のハンドラ型を定義
func helloHandler(c echo.Context) error {
    // 入力を受け取って構造体に入れる
    req := new(helloRequest)
    c.Bind(req)

    // ステータスコード 200 で JSON を返す
    return c.JSON(http.StatusOK, helloResponse{
        Message: "hello " + req.Name,
    })
}
  • 追加したライブラリをインストールして開発サーバーを起動する
go get github.com/labstack/echo
cd server/gae
dev_appserver.py app.yaml

Angular アプリから GAE アプリに通信する

  • Angular で HTTP 通信をする場合は @angular/common/http の HttpClient を利用する

  • HttpClient を DI できるように AppModule に HttpClientModule を追加する

client/src/app/app.module.ts
 import { AppComponent } from './app.component';
+import {HttpClientModule} from "@angular/common/http";

 @NgModule({
   declarations: [
     AppComponent
   ],
   imports: [
+    HttpClientModule,
     BrowserModule
   ],
  • AppComponent 初期化時に通信を行う
    • コンストラクタで HttpClient を DI で初期化し GET リクエストを送信、console.log で結果を表示する
    • レスポンスの型 helloResponse は事前に定義する
client/src/app/app.module.ts
interface helloResonse {
  Message: string
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  constructor(private http: HttpClient) {
    this.http.get<helloResonse>("/hello?name=bob").subscribe(
      res => console.log(res)
    );
  }
}

おわりに

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2