トレタ開発者ブログ

飲食店向け予約/顧客台帳サービス「トレタ」など、飲食業界のVertical SaaS企業です。

トレタO/X toCアプリで行ったパフォーマンス観点の対応

こんにちは!トレタでフロントエンドエンジニアをしている shira です。

この記事ではトレタO/Xというモバイルオーダー事業のうち、自分が関わっているtoCアプリで行ったパフォーマンス観点の対応について紹介します。

トレタO/Xに関しては、次の記事をご覧ください。

note.com

前提

トレタO/XではtoB向けとtoC向けのアプリケーションがあり、toBは共通、toCは各法人別という構成になっています。

この記事ではトレタO/XのtoCアプリのうち、塚田農場(エー・ピー・ホールディングス)様向けtoCアプリ (以下「toCアプリ」という。)の紹介をさせていただきます。

この記事で紹介するtoCアプリは2020年末から開発しているWebアプリケーションで、小さくリリースを繰り返し、改善してきました。

この記事では、パフォーマンス観点で行った対応の一部をご紹介します。

パフォーマンス観点の課題と対策

1. 画像が多く、読み込みに時間がかかる

課題

toCアプリでは全てのメニューに対して画像を表示しています。そのため、パフォーマンス観点で一番気にしていた点は画像の多さでした。パフォーマンスに影響することが明らかなため、初期リリース前に最低限の対応を入れたいと考えました。

f:id:punipunityan:20220214015009p:plain
塚田農場メニュー一覧画面

対応方針

まず、画像サイズを小さくする方法を検討し、WebP を使うことにしました。

Googleによると、WebP に変換することで PNG は 26% 小さくなり、JPEG は 25〜34% 小さくなるとされています。期待大です。(参考:An image format for the Web

WebPは基本的にモダンブラウザで利用可能ですが、対応していないブラウザのために WebP の他に PNG もしくは JPEG 画像を用意する必要がありました。

前提として、運用上変更が必要になる画像は全てContentfulに登録しています。メニューの画像も同様です。

最初は登録時にそれぞれ2種類の画像を登録する必要があると大変だな、と思っていたのですが、Contentfulだと簡単にできる方法がありましたので、その方法を紹介します。

対応方法

Contentfulでは画像のファイル形式を簡単に変更することが可能です。

参考までに、具体的なコードを記載します。

Content Delivery API でデータを取得した際、画像の情報は以下の形式で取得することができます。

(以下、ドキュメントより引用)

{
  "fields": {
    "title": "Nyan Cat",
    "file": {
      "contentType": "image/png",
      "fileName": "Nyan_cat_250px_frame.png",
      "url": "//images.ctfassets.net/yadj1kx9rmg0/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png",
      "details": {
        "image": {
          "width": 250,
          "height": 250
        },
        "size": 12273
      }
    }
  },
  "metadata": {
    "tags": [
      {
        "sys": {
          "type": "Link",
          "linkType": "Tag",
          "id": "nyCampaign"
        }
      }
    ]
  },
  "sys": {
    "id": "nyancat",
    "type": "Asset",
    "space": {
      "sys": {
        "type": "Link",
        "linkType": "Space",
        "id": "yadj1kx9rmg0"
      }
    },
    "createdAt": "2016-12-20T10:43:35.772Z",
    "updatedAt": "2016-12-20T10:43:35.772Z",
    "revision": 1
  }
}

fields.file.url が画像のURLになります。 ファイル形式を変更したい場合、fm パラメータを使います。WebPにするには、画像のURLに?fm=webpをつけ [fields.file.url]?fm=webp のようにするだけでOKです。

アプリケーションで画像を指定する際は以下のように指定しました。

<picture>
  <source  srcset="//images.ctfassets.net/yadj1kx9rmg0/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png?fm=webp'" type="image/webp" />
  <img src="//images.ctfassets.net/yadj1kx9rmg0/4gp6taAwW4CmSgumq2ekUm/9da0cd1936871b8d72343e895a00d611/Nyan_cat_250px_frame.png" alt="" />
</picture>

<picture> 要素内の<source> 要素に type="image/webp" を指定し、srcset には [fields.file.url]?fm=web 形式のURLを指定しています。img タグには WebP非対応ブラウザ向けに元々のURL fields.file.url(PNG / JPEG 画像)を指定します。 これにより、WebPに対応しているブラウザではWebP形式の画像を表示し、そうでない場合は元々の画像を表示するようになりました。

さくっと対応できる上、効果が大きいので、とても便利でした!✨ ✨ ✨

今後の課題

WebPに対応することである程度改善できましたが、Contentful側に登録してある元々の画像が大きすぎるケースが発生しており、その点が課題です。画像を登録する方に気を付けていただく方法もありますが、同じ画像を異なるレイアウトで表示している部分もあるため、toCアプリ側での対策も検討したいと考えています。Contentful の Images API を使って、画像をリサイズできるため、デバイスやレイアウトに応じたサイズの画像を返すような改修を将来的に入れたいと考えています。 また、一部WebP未対応箇所があるため、その点も今後改修予定です。

2. 動画がすぐに再生されない

課題

toCアプリでは、注文完了時ランダムに画像や動画を表示する仕様となっています。

f:id:punipunityan:20220309194207p:plain

この機能の開発中、動画の再生が遅れるという問題が発生しました。 インターネットの速度が速い場合は問題なかったのですが、あまり良くない場合に発生するようでした。

対応方針

まず、動画素材の調整を行いました。 具体的には、動画の長さ調整や画質的に問題ない範囲での圧縮などを行いました。(この対応はデザイナーが対応してくださいました。)

アプリ側では事前読み込みが効果的ではないかという仮説のもと、試してみることにしました。 具体的には、注文完了時に表示する画像・動画をアプリ起動時にpreloadで全て取得するよう対応しました。(最初prefetchを使おうとしましたが、safariで使えないようなのでpreloadを採用しました。)

アプリ起動時にpreloadを行うことにしたのは、ユーザーが必ず開く画面だからです。

結果、注文完了画面遷移時、動画がスムーズに再生するようになりました 🎉 🎉 🎉

今後の課題

この対応によって、必要ではないファイル(画像・動画)も取得してしまうという課題は残ります。

また、前項に記載したWebP対応が preload 対応部分の画像に関しては対応できていません。

いくつか改善の余地は残っている状況ですが、その点は今後検討していきたいと考えています。

3. BFFを導入するとレスポンスが遅くなった

課題

アプリの要件が複雑になってきたこともあり、BFFを導入する方針となりました。 BFFへの切り替え対応を行い、Vercelのプレビュー環境で動作確認をしたところ、レスポンスが明らかに遅くなっていることがわかりました。

前提として、toCアプリは以下と通信を行なっていました。

  • APIサーバー
  • Contentful
  • Firebase

BFF対応の際、APIサーバーへのリクエストとContentfulへのリクエストをBFF経由で行うようにしたのですが、そのうちAPIサーバーへのリクエストのレスポンスが明らかに遅くなってしまいました。 APIによっては、6秒弱かかっているケースもありました。

トレタO/Xは

toCアプリ <=> APIサーバー <=> 各バックエンドサービスという構成になっています。

f:id:punipunityan:20220214021125p:plain
トレタO/Xのシステム構成

BFFは、上記の図のtoCアプリとAPIサーバーの間に入るものとなっています。 具体的には Vercel の Serverless Functions を利用しています。 また、BFFでは一部情報をFirestoreにキャッシュして利用しているため、Firebaseとの通信も発生します。

対策

対策はいくつか考え、簡単に試せるものから試しました。 検討した案は以下になります。

  • VercelのServerless FunctionsのRegionを日本にする
  • まとめられるリクエストがないか見直す
  • 処理中にスピナーを出す
  • Firebaseをやめてupstash使う

結局上から3つを行いました。順番に、検討・対応した内容を紹介します。

VercelのServerless FunctionsのRegionを日本にする
Vercel の Serverless Functions の Region が sfo1 になっていたのを hnd1 に変更しました。sfo1 になっていたのはデフォルトのままにしていたためです。( 2021年1月14日より前に作成されたプロジェクトは、デフォルトで米国のサンフランシスコ(sfo1)になり、それ以降は新しいデフォルトの米国ワシントンDC(iad1)になるようです。 参考:Default Region

APIサーバーは日本にあるため、この変更によりBFFとAPIサーバーの物理的距離が近くなりました。

結論、この対応は一番効果的でした。この変更により、元々の速度と大差なく動くようになりました 🎉 🎉 🎉

Regionの影響の大きさを感じました🌏

参考:Regions for Serverless Functions

vercel.com

まとめられるリクエストがないか見直す
リクエストを直列で飛ばすとユーザーを待たせる時間が長くなってしまうため、並列でリクエストを飛ばせる箇所がないか見直しました。すでにある程度対応済みでしたが、見直したところ2箇所程度まとめられるところがあったのでまとめました。具体的には Promise.all() を使って並列でリクエストを飛ばすようにしています。

処理中にスピナーを出す
これは根本的な改善ではありませんが以下理由で対応しました。

  • BFFを挟んだことにより多少レスポンスが遅くなることはある程度仕方ないと考えられる
  • そもそも、あらゆる要因によってレスポンスが遅くなる場合があるが、その際、ユーザーに対してフィードバックがないという課題があった

ちなみに、これまでの通信中の表示制御は以下のようになっていました。(フィードバックを得て改善する前提で対応していました。)

  • APIとの通信時
    • 体感でサクサクだったので、GETは読み込み中の表示はなし、POSTはボタンの連打防止対応を入れていた
  • Contentfulとの通信時
    • 体感でやや待たされると感じられるため、「読み込み中...」文言を画面中央に表示

この改善により、体験のばらつきやレスポンス遅延時の懸念がある程度解消されました。

Firebase をやめて upstash を使う
Firebase にキャッシュするのをやめて、upstash を使うと早くなるのでは?という案がありました。ただ、他プロジェクトで試した方によると、100ms くらい早くなるけど体感はあまり変わらないとのことでした。それに加え、VercelのServerless FunctionのRegionの変更によって大幅に改善していたため、この対応は見送ることにしました。

まとめ

写真を見てお腹が空いた方、O/Xが気になった方、ぜひ塚田農場様に足を運んでみてください😊

この記事では、開発中に見つかったパフォーマンス観点での懸念と対策についていくつか紹介させていただきました。

toCアプリを開発し始めて1年以上経過しました。まだまだ改善したいことはたくさんあります。最近では日々の機能追加に加え、週1日改善Dayを設ける取り組みをはじめました。長くメンテナンスしていくためにコードの見直しを進めているところです。

課題を見つけて改善するのが好きな方、一緒に改善しましせんか!

トレタに入社してから3ヶ月間で感じたこと

はじめに

はじめまして。2021年の10月から株式会社トレタでサーバーサイドエンジニアとして働いている神山です。

実務としては、トレタO/Xの認証/権限管理サービス周りを担当しています。

なおトレタO/Xについては代表のnoteをご覧ください。 note.com

今回はトレタに入社してから感じたことや、どのような業務をしているかなど記載していこうと思います。

トレタのエンジニアはこんな感じなんだなぁ、というのが伝われば幸いです。

簡単な経歴と転職の経緯

大学を卒業後、新卒で入社したのは産業電気機器のメーカーでハードウェアエンジニアとして4年半ほど勤務していました。

作る仕事がしたいという思いから入社したのですが、新規プロジェクトが中々立ち上がらないことから悩み、ソフトウェアエンジニアへの方針転換を決めました。

2020年1月からは受託開発企業に入社し、金融機関関連サービスの開発や、交通関係の予約システムの開発に従事していました。

サービス視点を持って開発したい、もっとスキルを伸ばしていきたいという思いから転職活動を開始、2021年10月にトレタに入社しました。

入社の決め手

1. トレタO/Xのエンハンスのために設計機会が豊富

トレタO/Xはローンチされてからまだ日の浅いサービスであることを知りました。

エンハンスのための設計余地がまだまだあるのではないかと考えていました。

加えて、マイクロサービスはモノリスを分散していく用途で採用している会社が多いかとおもいます。

新規サービスとしてマイクロサービスに携わることができるのは非常に貴重な経験なのではと考えました。

2. より難易度の高い設計能力を求められる

トレタO/XはSaaSであり、それを実現するためにマイクロサービスの構成となっています。

SaaSであるということは当然ながら、ユーザーの様々なユースケースをクリアするように機能設計をしていかなければなりません。

加えてマイクロサービスによる機能実現を図るため、各サービスにおけるデータ整合性などを考慮し設計をする必要があります。

以上を踏まえると、難易度の高い設計が必要となるのではと考えていました。

マイクロサービスに関する詳しい構成はこちらで解説していますのでご覧ください。

https://blog.hatena.ne.jp/toreta-dev/toreta-dev.hatenablog.com/edit?entry=13574176438044016049

3. 労働環境が良い

コロナの影響もあり、かなり早い段階からフルリモートを取り入れている会社であることを知りました。

フルリモートであることを必須の条件とは考えていなかったのですが、移動時間があまり好きではない僕としては出勤時間を0にできるというのは大きな魅力でした・・。

4. 心理的安全性が高い

面接の段階から、穏やかな方々が多いのではないかという印象がありました。

加えて様々な媒体でトレタのことを調べていたのですが、自社の利益だけを追求する、という方針を取らない印象が強くありました。

サービスを利用してくださる飲食店の方々のことを考え、どうすれば業界全体がよくなるのかという視点を常に持ち続けていることが凄く印象に残ったのを覚えています。

実際に入社して感じたこと

1. トレタO/Xのエンハンスのために設計機会が豊富

こちらは入社前に予想していた通りでした。

僕が担当しているのは認証/権限管理サービスというトレタO/Xの中のごく一部ではあるのですが、入社してすぐに機能設計を行うこととなりました。

トレタの設計はかなり綿密で、機能のユースケース洗い出しから一つ一つのデータの概念、またその概念の定義から考えていくこととなります。

この時CTOとともにミーティングを重ねつつ設計を進めたのですが、

自身よりも圧倒的に技術力のある方からレビューを受けられるというのは、かなり嬉しいカルチャーショックでした。

現段階では設計フェーズは終わり、2022年の早い段階で機能リリースできるように実装を進めています。

2. より難易度の高い設計能力を求められる

こちらに関してもサービスの難易度の高さを痛感することとなりました。

マイクロサービスを採択していることにより、当然ながら各サービスで保持している機能、保持しているデータが存在します。

これらのデータ整合を保ちつつ今後のエンハンスを見据えた機能設計をするためには、様々な前提条件を把握していかなければなりません。

ただ前提条件を抑えるだけでもかなり四苦八苦しており、そこからどうすればより良いユーザー体験を作り出すことができるのかというのは非常に難しく、その一方で技術者としては良い環境だと感じています。

3. 労働環境が良い

こちらも入社前伺っていた通り、エンジニアは全員フルリモートに対応して業務に当たっています。

また通勤時間がない分、副業をしている社員もいるようです。

僕自身も副業や勉強の時間をしたり、自身の趣味に時間を割くことができるようになりました。

4. 心理的安全性が高い

こちらも入社前に予想していた通りでした。

業務の中でわからない部分や、設計のために意見や意図を収集したりするのですが、質問した全員が快く相談に乗ってくださります。

やはり穏やかな社員が多く、プライベートな話題でもちょっとした笑いを混ぜつつ会話をする方が多いです。

まとめ

入社して何より感じるのは、今トレタは転換期を迎えているということです。

コロナによる飲食業界の意識変革、今までのモノリシックな技術からの脱却、O/Xのリリースなど新しい挑戦の場が多く用意されています。

だからこそ、事業の成長とともにエンジニアとしてもスキルアップを図ることができるのではないかと考えています。

さいごに

トレタでは一緒に開発する仲間を募集しています。
興味がある方は是非カジュアル面談へお越しください!

www.wantedly.com

トレタO/Xの開発にジョインしてやってきたこと

こんにちは、パパエンジニアのkentaroです.

本記事はトレタ Advent Calendar 2020の 24 日目の記事です。
去年の終わりにO/Xの開発に携わるようになってから様々な経験を積むことができたので、今回はそれを振り返ってみたいと思います。

なお、「トレタO/X」については代表のnoteをご覧ください。

note.com

O/Xジョイン前

トレタの主力商品である予約/顧客台帳サービスのiOSアプリ開発を担当していました。

toreta.in

トレタ入社以前も2014年8月から Swift / Objective-C によるiOS開発をずっとやってきていた、というキャリアです。
(なお、その前は営業等をやっておりエンジニアではありませんでした。これはこれで語れる話なのですが、別の機会に譲ります。)

O/Xジョイン

2020年11月中旬、当時開催されていたネイティブ定例にkitagawaさんが参加し、「O/Xでエンジニアが足りていないんだけど、やってみたい人いませんか?」というのに手を挙げたのがきっかけです。 まずはワンダーテーブルさまのよなよなビアワークス向けのモバイルオーダーアプリ開発を担当することになりました。

yonayona

技術スタックとしてはフロントエンドがFlutter Web、バックエンドがFirebaseという構成でした。

firebase

ジョインしたはいいがしんどい

11月中に諸々のインプットをしてもらい、12月入ったところで環境構築を行い実際に開発を開始しました。
今振り返ってみてもこの頃はめちゃくちゃしんどかったです。

しんどさの原因は「なんもわからん」状態だったこと。

技術面ではそれまで慣れ親しんできたSwiftからDartに変わり、Flutterもしくは採用しているpackageが提供する機能などとにかくキャッチアップすることだらけでした。
公式ドキュメントが充実していること、SwiftUIの経験があったので宣言的UIへの抵抗感がなかったことは救いでしたが、開発効率はiOSのときとは比べ物にならないほど落ちていた実感がありました。

また、モバイルオーダーという文脈だと予約事業とは異なったドメイン知識が要求されるのですが、それが圧倒的に不足していたことも辛かったです。

しんどさをなくす

これはもう特効薬はなく、愚直に「わかるようになる」しかありませんでした。

技術面では同僚エンジニアとビデオ会議をしまくり、実装で詰まっているところの相談をしたり、PRの意図の確認をさせてもらったりしていました。
その後は試行錯誤しながらコードを書いては動作確認をしたり、PR作ったり、うまくいかなかったらまた相談したり…と、ひたすらプロダクトと向き合うしかありませんでした。

ドメイン知識に関しては社内のドキュメントを読み漁り、わからんことがあれば有識者に質問をしたり…のような当たり前のことを継続し続けていました。

2021年2月に自分が担当した比較的大きめな機能追加がリリースされた頃にはしんどさはほとんど無くなっていたように記憶しています。

共通基盤対応

この頃のバックエンドはPoC期間のプロトタイピング用のものだったため、製品版として共通基盤を利用する対応が必要となってきていました。
全体像としてはマイクロサービスへの挑戦とその結果を参照ください。

micro-service

自分はフロントエンド側の以下対応を担当しました。

  • ユーザー向けアプリの共通基盤対応
    • 共通基盤に向けてのAPIリクエスト周りの実装
  • スタッフ向けアプリのリプレイス
    • 共通基盤に向けてのAPIリクエスト周りの実装
    • Flutter2系を採用
    • Riverpodによる状態管理

主な挑戦

APIクライアントの実装に挑戦してみました。
詳細は割愛しますが、リクエスト用抽象classをextendsしたリクエストclassを渡してあげると、リクエストclass側で期待している型のレスポンスが受け取れるような仕組みになっています。
SwiftではProtocolとGenericsで実装するイメージですが、同じ要領で抽象classとGenericsを使い実装しました。
Dartでも抽象的な処理が書けるんだなーと感動したのを覚えています。

6月リリース

こちらの共通基盤対応は無事6月にリリースされました!

議事録を書くようになった

この頃から日々のMTGやスタンドアップの議事録を書くようになりました。
もともと自分とは別の法人のプロジェクトを担当しているQAが取り組みをはじめていて「ええやん!」と思って真似したのがきっかけです。
話したのはいいんだけど、結局何が決まって何がNext Actionなんだっけ?というのがテキストとして残るとその場にいなかった人にも共有しやすいし、後から検索もできて便利だなと感じています。

tech.toreta.in

ちなみにNotionに各種議事録が作成されているのですが、会議体ごとにまとめて表示できるようにしてみたりもしました。
これを作ってたらNotion力が向上したような気がします。

notion

React化対応

PoCではFlutterで作っていたユーザー向けモバイルオーダーアプリをReactにリプレイスするプロジェクトです。
自分は2021年9月から本格的に参加しています。
こちらはまだリリースされておらず、絶賛開発中です。

Webフロントエンドへの挑戦

Flutter版もWebアプリではあったのですが、基本的にプラットフォームのことをあまり気にせず開発できるので、このプロジェクトでWebのフロントエンド開発に業務で初めて携わる感覚がありました。
技術スタックとしては、TypeScript・React・Next.jsを採用しています。

個人ブログを上記技術スタックで開発した経験はあったものの、業務での開発はやはり一味違いました。
TypeScriptの重鎮にPRをレビューしてもらうので色々と指摘されることはあるのですが、意図・根拠が明確でめちゃくちゃ勉強になりました。
まだまだ勉強することがたくさんありますが、楽しんでやれているのはいいことかなと思っています。

振り返ってみて

本当に色々なことやったんだなーと感じました。1年じゃなくて、2, 3年くらい経ってるんじゃねーの?という感覚です。
新しい環境に飛び込むとこんなにも密度が高いんだなーという気持ち…
まだ書きたいことはあったけど、キリがないので特に印象深いことに絞って書きました。

あと、SwiftもDartもTypeScriptも好きだなーということに気がつけました。みんないい言語!!

最後に

新しい技術に挑戦したい方も、得意な技術でバリューを発揮したいという方も、もし興味がありましたらお気軽にカジュアル面談等お申し込みください!

www.wantedly.com

© Toreta, Inc.

Powered by Hatena Blog