アプリ音痴の両親のためにLINEで共有した写真をデジタルフォトフレーム風にするアプリを作った話


この記事はぷりぷりあぷりけーしょんず Advent Calendar 2019の22日目の記事です。

はじめに

大遅刻スミマセン。、
まさかの年越してからアドベントカレンダーに登録するという最早アドベントカレンダーとはなんぞやな状況です。
師走の風に流され、忙しさのあまり年内に完成させ記事にする予定のアプリが新年初コミットとともに完成に至る形になりました。

さて、年を越したと同時にエンジニアに転職して丸3年が経ちました。
1年半ほど前は丸3年経つまでにサーバー・ネットワークの設定からアプリの開発、何かしらのサービスの一般公開をしたいというのを目標に業務とは別に個人学習、開発を行なっていました。
結果的にサーバー・ネットワークの設定,何かしらのサービスの一般公開というのは叶うことはなかったわけですが個人的に家族のための使うことができるレベルのアプリを作成することが叶いました。

作ったもの

私の両親はまあアプリ音痴でインスタグラムはもちろんのこと、LINEがギリギリ使えるレベルです。その割に父親はガジェット好きでiPadはいっちょ前に4台持っています。
いっちょ前にリビングに立て掛けてあるiPadを有効活用してもらいたいと思いLINEだけで完結するデジタルフォトフレームアプリfamiphoto(famip○rtからパクったとかは言わない)を作成しました。

アーキテクチャ

ざっくり上記のようになっており、アプリのフローとしては以下の通りです。

  1. LINEグループにMessagingAPIで作成したbotを招待
    UUIDを作成し、グループIDをキーにDynamoDBに保存、S3にUUIDのディレクトリを作成
  2. 今まで通り画像を共有(ここ重要)
    グループIDからUUIDを取得し、画像をUUIDのディレクトリに保存
  3. famiphoto urlとメッセージを送るとデジタルフォトフレームのurlを返す
    urlはhttps://***firebase.app/family/{uuid}
  4. urlにアクセスするとデジタルフォトフレームとして起動しリビングに飾る
    裏では{uuid}をリクエストパラメータにAPIGatewayからLambdaを起動し、S3の画像URL一覧を取得してる
  5. PWAなのでホームに落としてあげれば、普通にiPad使いたいときは普通に使ってもらい、飾っておくときはアプリを起動して置くだけで良い

使用している技術は
- AWSLambda Runtime-Go
- DynamoDB
- S3
- Firebase hosting
- Vue.js
- PWA

AWS側のインフラ周りはCDKで管理しておりTypescriptで記述しています。

実装

実装の面で特別記述するような話はないのですが、初めて挑戦したような箇所もありそれぞれでハマった所、苦労した所を自分へのメモとして残しておきます。
もし参考になれば幸いです。

CDK

APIGateway, Lambdaを使ったCDKは何度か構築したことがありますが今回初めてクエリパラメータ使うことになりました

stack.ts
    // API Gateway
    const api = new apigateway.RestApi(this, `api`, {
      restApiName: `api`,
    })

    // Lambda
    const lambdaHandler = new lambda.Function(this, `api`, {
      code: lambda.Code.fromAsset("../bin"),
      handler: "main",
      runtime: lambda.Runtime.GO_1_X,
    })

    const lambdaHandlerIntegration = new apigateway.LambdaIntegration(lambdaHandler, {proxy: true})
    api.root.addResource(`api`)
    api.root.addMethod("GET", lambdaHandlerIntegration, {
      requestParameters: {
        "method.request.querystring.{parameter_name}": true
      }
    })

一部を適当に抜粋していますが大体上記の通り設定していきます。

apigateway.root.addMethodのoptionにrequestParametersを定義し、"method.request.querystring.{parameter_name}"でクエリパラメータを設定できます。
今回自分の場合はuuidで処理を行っていくので"method.request.querystring.uuid"となります。

lambda側で取得する際は以下の通りです

lambda.go
func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    uuid = req.QueryStringParameters["uuid"])
    ....
}

Lambda × Go

LambdaでのGoというより、Lambdaでのline-sdk-goの話ですがこれは以前の記事でも書かせていただきました。

APIGatewayProxyRequestをline-bot-sdk-go/linebot.Eventにパース

それ以外にもBodyを返す際にちょいと苦労(ドキュメント読めば良い話だったわけですが)したので何をしてるかそのまま転載します。

controller.go

type ContentsResponse struct {
    Body []string `json:"contents"`
}

func handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    contents, err := c.photo.GetContentURLs(req.QueryStringParameters["uuid"])
    if err != nil {
        err = fmt.Errorf("Failed get contents: %v ", err)
        return errorResponse(err), err
    }

    body, err := json.Marshal(&ContentsResponse{
        Body: contents,
    })
    if err != nil {
        return events.APIGatewayProxyResponse{}, err
    }

    return events.APIGatewayProxyResponse{
        StatusCode:      200,
        Body:            string(body),
        IsBase64Encoded: false,
    }, nil
}

PWA

フロントの知識がほぼ皆無な私が今回PWAを選定した理由はアプリのフローであげている今まで通り画像を共有(ここ重要)してもらい普通にiPad使いたいときは普通に使ってもらい、飾っておくときはアプリを起動して置くだけで良いようにしたかったからです。

PWAのアプリをFirebaseでhostingする記事はググればたくさん出てくるのでハマることは特にありませんでした。(ありがとうございます)

firebaseにhostingする際はbuild時に生成されるdistを参照するように設定されているわけですがURLは先述の通りhttps://***firebase.app/family/{uuid}となります。
これはuuidで画像のURLのリストを取るためなのですがアプリごとにuuidを切り替えられるようにしたいためPWAとして起動する際のstart_urlを動的に変更する必要があります。

PWAのmanifest.jsonを動的に生成する
↑こちらの記事を参考にさせていただき、ほぼそのまま使わせていただきました。
しかしこれだとbuildするたびにdist/index.htmlが毎回書き変わってしまい、<link rel=manifest>にidを付与する必要ができるため下記のようなスクリプトを用意してbuild時はこれを使うようにしています。

build.sh
#bin/bash
npm run build
sed -i  's/href=\/manifest.json/id="my-manifest"/g' ./dist/index.html

終わりに

個人開発というとどうしても途中で投げ出してしまったり、アイデアで出しで設計終わったらそれっきりだったり、設計拘ってたら飽きたりと色々続かないことが多いです。
今回の開発で思ったのは

  1. 初挑戦は1つ〜2つくらい(規模によると思う)
  2. 今までの知識のアップデートを中心に
  3. 時間のかかりそうな箇所は得意分野で
  4. わからなそうな領域はググってコピペでいいから終わらせる

というのがアプリを作る際のモチベーション維持の要になるように思います。
今回の私のアプリでいうと

  1. PWA
  2. Go, Vue.js
  3. CDK, Firebase
  4. 画像のフルスクリーン表示やスライドショーぽくするところ

といったところでしょうか。

エンジニア丸3年経過時の目標へのコミットは残念ながら届きませんでしたが全くの未経験から相談なしにエンジニアへの転職をしてやって見せてやることはできたかと思います。(父親は令和の世を生きる人間とは思えない程固い人間)

ここまで作ることができ、あとはやる気次第だと思うので2020年のアドベントカレンダーでは一般公開したサービスの記事を書けるように変わらずマイペースに個人開発をしていきたいと思います。

参考

Vue CLI 3 で PWA チュートリアル(Service Workers / Add to Home Screen / Push Notifications)
とっても簡単!Vueでフルスクリーン機能を実装する