Slide 1

Slide 1 text

MAUが1年で292%に成長した 「aumoのおでかけ比較サイト」 における取り組み アウモ株式会社 エンジニア 関谷恒甫 栗本拓弥

Slide 2

Slide 2 text

目次 ● 発表者紹介 ● aumoについて ● フロントエンドをRailsのテンプレートエンジンからNuxt.jsに 移行した話 ● フロントエンドにNGINXのリバースプロキシを設置してキャッ シュを導入した話 ● まとめ 2

Slide 3

Slide 3 text

自己紹介 3 ● 名前:関谷恒甫 ● 所属:アウモ株式会社 ● 担当:メディアチームにおける開発全般 ● 経歴: ○ 2020年4月:グリーに新卒入社 ○ 2020年4月:アウモに配属 ○ 2020年6月:WFSに配属 ○ 2021年12月:アウモに配属

Slide 4

Slide 4 text

自己紹介 ● 名前:栗本拓弥 ● 所属:アウモ株式会社 ● 担当:メディアチームにおける開発全般 ● 経歴: ○ 2022年4月:グリーに新卒入社 ○ 2022年4月:アウモに配属 4

Slide 5

Slide 5 text

aumoについて

Slide 6

Slide 6 text

サービスの全体像

Slide 7

Slide 7 text

メディア事業

Slide 8

Slide 8 text

メディア事業の提供サービス

Slide 9

Slide 9 text

新規事業の取り組み

Slide 10

Slide 10 text

SaaS事業

Slide 11

Slide 11 text

SaaS事業の提供サービス

Slide 12

Slide 12 text

SaaS 事業の機能紹介 

Slide 13

Slide 13 text

Fintech

Slide 14

Slide 14 text

Fintech事業の概要

Slide 15

Slide 15 text

マーケティングソリューション

Slide 16

Slide 16 text

マーケティングソリューションの概要

Slide 17

Slide 17 text

おでかけ比較サイトのフロントエンドを Railsのテンプレートエンジンから Nuxt.jsに移行した話 17

Slide 18

Slide 18 text

何をやったのか ● aumo.jpの一覧ページ(例 aumo.jp/areas/241)のフロントエンドを、 Railsのテンプレートエンジン(slim)からNuxt.jsに移行した。 18

Slide 19

Slide 19 text

19 gourmet.aumo.jp aumo.jp なぜやるのか ● aumoではaumo.jpドメインと3つのサブドメイン(gourmet.aumo.jp, leisure.aumo.jp, travel.aumo.jp)で一覧 ページを運営しています。

Slide 20

Slide 20 text

なぜやるのか (歴史的経緯のため)ドメインによって フロントエンドの実装が異なる ● サブドメイン ○ Nuxt.js ● aumo.jp ○ Railsテンプレートエンジン 20

Slide 21

Slide 21 text

なぜやるのか ● 一覧ページを開発する際は基本的にドメイン4つ全てが開発の対象 ○ サブドメインではNuxt.jsのコンポーネントを使い回すことができる が、aumo.jpドメインではそれらを使えないので、テンプレートエン ジンのコードも書く必要がある。。。 21

Slide 22

Slide 22 text

なぜやるのか ● 一覧ページを開発する際は基本的にドメイン4つ全てが開発の対象 ○ サブドメインではNuxt.jsのコンポーネントを使い回すことができる が、aumo.jpドメインではそれらを使えないので、テンプレートエン ジンのコードも書く必要がある。。。 22 2度手間!!!

Slide 23

Slide 23 text

移行の流れ ※アウモでは業務時間の1~2割程度を、各自がやりたい開発改善等の  時間に当てて良いということになってるので、その仕組みを今回は  利用しました。 23 1. アプリケーションコード変更 2. インフラ変更 3. リリース

Slide 24

Slide 24 text

移行の流れ 1. アプリケーションコード変更 2. インフラ変更 3. リリース 24

Slide 25

Slide 25 text

アプリケーションコードの変更 ● テンプレートエンジンのコードを、Nuxtに書き換える。 ● 基本的には既存のNuxtのコンポーネントを使い回す。 ● aumo.jpドメインのみに存在するUI等がある場合に、コンポーネントを新規 作成or修正。 ● デザインが同じでもaumo.jpドメインとサブドメインで仕様が違う可能性があ るので、コードを一つ一つ確認し差分があった場合は、PMと適時仕様をすり 合わせる。 25

Slide 26

Slide 26 text

移行の流れ 1. アプリケーションコード変更 2. インフラ変更 3. リリース 26

Slide 27

Slide 27 text

インフラの変更 ● aumo.jpドメインには一覧ページ(https://aumo.jp/areas/241)だけで なく、記事ページ(https://aumo.jp/articles/145272)も存在する。 ● 一覧ページへのアクセスのみをNuxtサーバに転送するように変更する必要 がある。 ○ ALBのリスナールールを編集して、特定のパスパターン (例 /areas/*)の時のみリクエストを転送するように設定。 27

Slide 28

Slide 28 text

インフラ構成の変更 28 BEFORE

Slide 29

Slide 29 text

インフラ構成の変更 29 BEFORE AFTER

Slide 30

Slide 30 text

移行の流れ 1. アプリケーションコード変更 2. インフラ変更 3. リリース 30

Slide 31

Slide 31 text

リリース ここまでは順調だったが。。。 31

Slide 32

Slide 32 text

リリース1回目 レスポンス速度が悪化する 32

Slide 33

Slide 33 text

リリース1回目 レスポンス速度が悪化する 原因 ● コード変更に伴い、リリースのタイミングでAPI側 で既存のキャッシュが使えなくなり、 Elasticsearchへのアクセスが増えて負荷が上 がった。 対策 ● 元々Elasticsearchの負荷が高めだったので、 この機会にスケールアップを実施。 33

Slide 34

Slide 34 text

リリース2回目 相変わらずレスポンス速度が遅い状態が続く 34

Slide 35

Slide 35 text

リリース2回目 相変わらずレスポンス速度が遅い状態が続く 原因 ● aumo.jpからリクエストが転送される分、Nuxtサーバへのリクエストが 増えたから? 対策 ● サーバー台数を増やしてみる。 35

Slide 36

Slide 36 text

リリース3回目 相変わらずレスポンス速度が遅い状態が続く 36

Slide 37

Slide 37 text

リリース3回目 相変わらずレスポンス速度が遅い状態が続く 原因 ● パフォーマンスチューニングをほとんど行っておらず、API側でN+1のク エリが複数発生していたから? 対策 ● N+1のクエリをひたすら潰す。 37

Slide 38

Slide 38 text

リリース4回目 相変わらずレスポンス速度が遅い状態が続く 38

Slide 39

Slide 39 text

リリース4回目 相変わらずレスポンス速度が遅い状態が続く 原因 ● 不明。何も思いつかず。 対策 ● レスポンスが遅い原因が不明のままなので、問題を切り分けてみる。 ● aumo.jpドメインのレスポンスのみが遅いのかサブドメインのレスポンス も遅いのかを見分けるために、aumo.jpとサブドメインで使うコンテナを 分ける。 39

Slide 40

Slide 40 text

リリース5回目 aumo.jpドメインへのリクエストのみが遅く、 サブドメインへのリクエストは正常なことが判明 40

Slide 41

Slide 41 text

アクセスログの調査 アクセスログをよく見てみる ● 最初の数分は問題なさそうだが、チラホラ遅い レスポンスが混ざっている。 ● その後は全部のレスポンスが遅くなる。 ● 遅いレスポンスをよく見ると、エリア番号が大き い傾向があることに気づく。       41

Slide 42

Slide 42 text

アクセスログの調査 アクセスログをよく見てみる ● 最初の数分は問題なさそうだが、チラホラ遅い レスポンスが混ざっている。 ● その後は全部のレスポンスが遅くなる。 ● 遅いレスポンスをよく見ると、エリア番号が大き い傾向があることに気づく。       42

Slide 43

Slide 43 text

アクセスログの調査 43 アクセスログをよく見てみる ● 最初の数分は問題なさそうだが、チラホラ遅い レスポンスが混ざっている。 ● その後は全部のレスポンスが遅くなる。 ● 遅いレスポンスをよく見ると、エリア番号が大き い傾向があることに気づく。        エリア番号が関係してそうなコード  を 探す。

Slide 44

Slide 44 text

問題のコード 44 computed: { targetAreaRegion () { return this.allRegions.find((region) => region.prefectures.some((pref) => pref.areas.some((area) => Number(area.id) === Number(this.targetArea) ) ) ) } } allRegions = [{ id: 1, name: '北海道・東北', prefectures: [{ id: 1, name: '北海道', areas: [{ id: 1, name: '札幌市' }, ...], }, ...] }, ...] ・allRegionsはregion->prefecture->areaという階層構造になっていて、階層構造を上か らiterateしていき、targetArea(例 札幌市)が属するregion(例 北海道・東北地方)を探す。 ・areaは全部で1600個程あるので、targetAreaが配列の後ろの方にある場合は探すのに 時間がかかる。 ・computedプロパティでキャッシュしてるので大丈夫だと思っていたが。。。

Slide 45

Slide 45 text

Nuxt.jsをSSRで利用する時の落とし穴 45 原因 ● Nuxt.jsをSSRで利用する場合は、computedプロパティが キャッシュされない。 https://github.com/nuxt/nuxt/issues/2447 対策 ● 計算が重い処理はcomputedプロパティではなく、storeに保存すること で、レンダリング中に処理が走るのを1回だけにする。

Slide 46

Slide 46 text

リリース成功!!! 46 リリース6回目

Slide 47

Slide 47 text

学んだ点 ● ログは超大事。生のアクセスログ、ALBのメトリクス、ECSのメトリクスなど色 んなログを見るべき。 ● テストが充実してないと、リファクタリングのハードルが上がる。 ● リファクタリングは辛いので、設計の段階でなるべく先まで見通して、リファクタ リングをそもそもしなくて済むようにできるのがベスト。 より詳細が知りたい方は、テックブログを見てください! https://techblog.aumo.co.jp/articles/2233 47

Slide 48

Slide 48 text

おでかけ比較サイトのフロントエンドにNGINXのリ バースプロキシを設置して キャッシュを導入した話 48

Slide 49

Slide 49 text

何をやったのか ● 一覧ページを表示しているNuxtサーバの前段に NGINXリバースプロキシを置いて、ページキャッシュを導入した。 49 既存の構成 リクエストが来るたび 大量のAPIを叩いていた リバプロ導入した構成 1回目のリクエストは 従来通り 2回目以降のリクエストは NGINXがキャッシュを返す

Slide 50

Slide 50 text

なぜやるのか ● SEO対策として一覧ページのコンテンツをリッチにして、 MAUが大きく成長した! が、、 50 追加したコンテンツ例

Slide 51

Slide 51 text

なぜやるのか ● SEO対策として一覧ページのコンテンツをリッチにして、 MAUが大きく成長した! が、、 51 追加したコンテンツ例 MAUの推移

Slide 52

Slide 52 text

なぜやるのか ● しかし、ページがリッチになるにつれてレスポンス速度が遅くなってしまって いた 52

Slide 53

Slide 53 text

なぜやるのか レスポンス速度の悪化によりCoreWebVitalの指標が低下してしまった ● CoreWebVitalとは ○ Googleが定めるWebサイトのUXを測る重要な指標のこと。 ○ PageSpeed Insightsというサイトで誰でも計測できる ● CoreWebVitalの主な指標 ○ Time to First Byte (TTFB): ■ レスポンスの最初のByteが返るまでの時間 ○ First Contentful Paint (FCP): ■ 視覚コンテンツの初期表示時間 ○ Largest Contentful Paint (LCP): ■ 最大コンテンツの表示時間 53

Slide 54

Slide 54 text

なぜやるのか 54 ● CoreWebVitalが不合格だらけなので、これを改善することで、 MAUがさらに成長するかもしれない

Slide 55

Slide 55 text

なぜやるのか 55 ● CoreWebVitalが不合格だらけなので、これを改善することで、 MAUがさらに成長するかもしれない ちくしょう。 速度改善だ!

Slide 56

Slide 56 text

なぜやるのか ● 今後コンテンツがさらにリッチになっても、レスポンス速度の悪化を招かな いようにしたい。 ● Nuxtのレスポンスを丸々キャッシュする「ページキャッシュ」を 導入すると、キャッシュ済みページへのアクセスは一瞬で返せる。 56

Slide 57

Slide 57 text

なぜやるのか ● 今後コンテンツがさらにリッチになっても、レスポンス速度の悪化を招かな いようにしたい。 ● Nuxtのレスポンスを丸々キャッシュする「ページキャッシュ」を 導入すると、キャッシュ済みページへのアクセスは一瞬で返せる。 57 ちくしょう。 ページキャッシュだ!

Slide 58

Slide 58 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 3. リリース 58

Slide 59

Slide 59 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 3. リリース 59

Slide 60

Slide 60 text

NGINXリバースプロキシ設置 - インフラ構成 - BEFORE

Slide 61

Slide 61 text

NGINXリバースプロキシ設置 - インフラ構成 - 61 BEFORE AFTER

Slide 62

Slide 62 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 3. リリース 62

Slide 63

Slide 63 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 1. 数千件の特定URLをキャッシュする 2. SPとPCでキャッシュを分ける 3. リリース 63

Slide 64

Slide 64 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 1. 数千件の特定URLをキャッシュする 2. SPとPCでキャッシュを分ける 3. リリース 64

Slide 65

Slide 65 text

NGINXでページキャッシュ導入 65 要件1 ● 数千件の特定URLをキャッシュする 対応 ● mapディレクティブで対象パスを制限 ○ 条件分岐というとif文が頭に浮かびますが、NGINXではif文は邪悪と言わ れておりなるべく使わないべきとされています。 そこで、mapディレクティブで条件分岐を行うようにしました。

Slide 66

Slide 66 text

NGINXでページキャッシュ導入 66 ● mapディレクティブで対象パスを制限 map $host$request_uri $is_target_path { default 0; include /etc/nginx/cache_target_path.map; } aumo.jp/prefectures/13/scenes/8 1; aumo.jp/prefectures/27/categories/a12 1; gourmet.aumo.jp/areas/466/categories/a15 1; /etc/nginx/cache_target_path.map

Slide 67

Slide 67 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 1. 数千件の特定URLをキャッシュする 2. SPとPCでキャッシュを分ける 3. リリース 67

Slide 68

Slide 68 text

NGINXでページキャッシュ導入 68 要件2 ● SPとPCでキャッシュを分ける 対応 ● キャッシュキーにPCかSPかの情報を含める

Slide 69

Slide 69 text

NGINXでページキャッシュ導入 69 ● キャッシュキーにPCかSPかの情報を含める ○ PCかSPかの情報をもつ$deviceをmapディレクティブで用意する ○ キャッシュキー(proxy_cache_key)に$deviceを加える ■ キャッシュキーの例:aumo.jp/areas/13:sp location / { ...... proxy_cache_key $host$uri:$device; ...... }

Slide 70

Slide 70 text

ページキャッシュ導入の流れ 1. NGINXリバースプロキシ設置 2. NGINXでページキャッシュ導入 3. リリース 70

Slide 71

Slide 71 text

リリース後の効果計測 71 ● レスポンス速度 ○ 0.00秒でレスポンスを返している!

Slide 72

Slide 72 text

72 ● CoreWebVital指標 ○ 合格!! リリース後の効果計測 指標
 定義
 キャッシュ前
 目標値
 キャッシュ後
 全体
 不合格
 合格
 合格
 LCP
 メインコンテンツの読み込み速度
 4.3s
 2.5s
 1.7s
 FID
 操作を行ってから応答が発生するま での遅延時間
 489ms
 100ms
 なし
 CLS
 読み込み時のレイアウトのずれ
 0
 0
 0
 TTFB
 サーバー初期応答時間
 2.8s
 0.8s
 0.5s
 FCP
 視覚コンテンツの初期表示時間
 3.7s
 1.8s
 1.3s
 INP
 コンテンツの反応速度
 3,943ms
 200ms
 2,263ms


Slide 73

Slide 73 text

73 ● MAU ○ MAUが成長! リリース後の効果計測

Slide 74

Slide 74 text

キャッシュについてのまとめ 74 ● 速度改善にも種類がある ○ バックエンド: DBアクセスのキャッシュ、N+1 ○ フロントエンド: ページキャッシュ、CDN ● ページキャッシュを導入すれば、ページがリッチになってもレスポンス速度が速 いまま変化しない より詳細が知りたい方は、テックブログを見てください! https://techblog.aumo.co.jp/articles/2278

Slide 75

Slide 75 text

このセッションのまとめ 75 ● フロントエンドをRailsのテンプレートエンジンからNuxt.jsに移行することで 開発工数削減ができた。 ● フロントエンドにNGINXのリバースプロキシを設置してキャッシュを導入する ことでレスポンス速度の改善ができた。 ● これからもサービスの質を高めて、より多くの人に使ってもらえるサービスに成 長させていきます!

Slide 76

Slide 76 text

ご成長(静聴)ありがとうございました! 76

Slide 77

Slide 77 text

77